From 4bef8e2f9b7ab71700e6f60abde8623dd52ed8c4 Mon Sep 17 00:00:00 2001 From: Guus De Graeve Date: Tue, 23 Feb 2016 22:40:24 +0100 Subject: [PATCH 0001/1667] replace get_class with instanceof for $this->app When using get_class, in Lumen the $this->app could only be an instance of the \Laravel\Lumen\Application and not an instance of a class extending from \Laravel\Lumen\Application. When using the instanceof check people can use a custom application class extending from the main Lumen Application class. To make this work i had to remove the use statements at the top and replace the references in getProvider with the full paths. --- src/Intervention/Image/ImageServiceProvider.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/ImageServiceProvider.php b/src/Intervention/Image/ImageServiceProvider.php index 47eff4ea5..234e06cfb 100644 --- a/src/Intervention/Image/ImageServiceProvider.php +++ b/src/Intervention/Image/ImageServiceProvider.php @@ -2,7 +2,6 @@ namespace Intervention\Image; -use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; class ImageServiceProvider extends ServiceProvider @@ -61,9 +60,9 @@ public function register() */ private function getProvider() { - if (get_class($this->app) == 'Laravel\Lumen\Application') { + if ($this->app instanceof \Laravel\Lumen\Application) { $provider = '\Intervention\Image\ImageServiceProviderLumen'; - } elseif (version_compare(Application::VERSION, '5.0', '<')) { + } elseif (version_compare(\Illuminate\Foundation\Application::VERSION, '5.0', '<')) { $provider = '\Intervention\Image\ImageServiceProviderLaravel4'; } else { $provider = '\Intervention\Image\ImageServiceProviderLaravel5'; From cbb3b05757e1af9d9cc4461ff15d644a4c36ea26 Mon Sep 17 00:00:00 2001 From: vlakoff Date: Fri, 26 Feb 2016 00:42:01 +0100 Subject: [PATCH 0002/1667] Use static in drivers newImage() --- src/Intervention/Image/Gd/Driver.php | 2 +- src/Intervention/Image/Imagick/Driver.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index 80a174056..28612fc9c 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -34,7 +34,7 @@ public function newImage($width, $height, $background = null) { // create empty resource $core = imagecreatetruecolor($width, $height); - $image = new \Intervention\Image\Image(new self, $core); + $image = new \Intervention\Image\Image(new static, $core); // set background color $background = new Color($background); diff --git a/src/Intervention/Image/Imagick/Driver.php b/src/Intervention/Image/Imagick/Driver.php index fd7ae52ed..2fbd78b82 100644 --- a/src/Intervention/Image/Imagick/Driver.php +++ b/src/Intervention/Image/Imagick/Driver.php @@ -42,7 +42,7 @@ public function newImage($width, $height, $background = null) $core->setColorspace(\Imagick::COLORSPACE_UNDEFINED); // build image - $image = new \Intervention\Image\Image(new self, $core); + $image = new \Intervention\Image\Image(new static, $core); return $image; } From 8963435739ff212c875fed16aa85fedf8262ccd4 Mon Sep 17 00:00:00 2001 From: usernam3 Date: Fri, 26 Feb 2016 02:52:30 +0200 Subject: [PATCH 0003/1667] phpdoc wrong parameter name --- src/Intervention/Image/AbstractFont.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 9e3cca92c..5aeb1f764 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -202,7 +202,7 @@ public function getValign() /** * Set path to font file * - * @param string $align + * @param string $file * @return void */ public function file($file) From 69f977b7265eb5169c5d9c4375b9251cd33be9b8 Mon Sep 17 00:00:00 2001 From: zema Date: Tue, 26 Apr 2016 11:48:25 +0300 Subject: [PATCH 0004/1667] throw NotReadableException if try read partially saved image (getimagesize returns correct size, but image infact is not readable) --- src/Intervention/Image/Gd/Decoder.php | 17 +++++++++++------ tests/GdSystemTest.php | 8 ++++++++ tests/images/broken.png | Bin 0 -> 42 bytes 3 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 tests/images/broken.png diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 13fb8f3b4..5525b4ddc 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -25,18 +25,15 @@ public function initFromPath($path) // define core switch ($info[2]) { case IMAGETYPE_PNG: - $core = imagecreatefrompng($path); - $this->gdResourceToTruecolor($core); + $core = @imagecreatefrompng($path); break; case IMAGETYPE_JPEG: - $core = imagecreatefromjpeg($path); - $this->gdResourceToTruecolor($core); + $core = @imagecreatefromjpeg($path); break; case IMAGETYPE_GIF: - $core = imagecreatefromgif($path); - $this->gdResourceToTruecolor($core); + $core = @imagecreatefromgif($path); break; default: @@ -45,6 +42,14 @@ public function initFromPath($path) ); } + if ($core === false) { + throw new \Intervention\Image\Exception\NotReadableException( + "Unable to read image from file ({$path})." + ); + } + + $this->gdResourceToTruecolor($core); + // build image $image = $this->initFromGdResource($core); $image->mime = $info['mime']; diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index 5fdcd3397..318a6446d 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -20,6 +20,14 @@ public function testMakeFromPath() $this->assertEquals('image/png', $img->mime); } + /** + * @expectedException \Intervention\Image\Exception\NotReadableException + */ + public function testMakeFromPathBroken() + { + $this->manager()->make('tests/images/broken.png'); + } + public function testMakeFromString() { $str = file_get_contents('tests/images/circle.png'); diff --git a/tests/images/broken.png b/tests/images/broken.png new file mode 100644 index 0000000000000000000000000000000000000000..eaecd5c64023152ca7c424acf044cff4fe93b068 GIT binary patch literal 42 vcmeAS@N?(olHy`uVBq!ia0voZz6=a3NgQkp42-e*>=zjr6c{{R97DJOiY*3R literal 0 HcmV?d00001 From 26388f29399984220853a79c82f0352ec062acb2 Mon Sep 17 00:00:00 2001 From: Alok Rajiv Date: Wed, 22 Jun 2016 08:46:10 +0530 Subject: [PATCH 0005/1667] Updated provides.json Backslash syntax error --- provides.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provides.json b/provides.json index d5d708aff..a8cd1b6a5 100644 --- a/provides.json +++ b/provides.json @@ -1,11 +1,11 @@ { "providers": [ - "Intervention\Image\ImageServiceProvider" + "Intervention\\Image\\ImageServiceProvider" ], "aliases": [ { "alias": "Image", - "facade": "Intervention\Image\Facades\Image" + "facade": "Intervention\\Image\\Facades\\Image" } ] } From d4b71ad4810f35beb67d6a96ec4149b6de198dbe Mon Sep 17 00:00:00 2001 From: Adam van Dongen Date: Thu, 28 Jul 2016 10:47:49 +0200 Subject: [PATCH 0006/1667] fix for not picking up configuration on lumen --- src/Intervention/Image/ImageServiceProviderLumen.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Intervention/Image/ImageServiceProviderLumen.php b/src/Intervention/Image/ImageServiceProviderLumen.php index 0de36844e..933ac21cd 100644 --- a/src/Intervention/Image/ImageServiceProviderLumen.php +++ b/src/Intervention/Image/ImageServiceProviderLumen.php @@ -21,6 +21,9 @@ public function register() 'image' ); + // set configuration + $app->configure('image'); + // create image $app['image'] = $app->share(function ($app) { return new ImageManager($app['config']->get('image')); From 47eeb5d316c4dc77de85f462d33a9c9a70ece3ae Mon Sep 17 00:00:00 2001 From: Tomasz Szadkowski Date: Sun, 7 Aug 2016 23:37:24 +0200 Subject: [PATCH 0007/1667] 498_fix_text_top_vertical_position_multiline_imagick --- src/Intervention/Image/Imagick/Font.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php index 9ae2f9786..f081ba21a 100644 --- a/src/Intervention/Image/Imagick/Font.php +++ b/src/Intervention/Image/Imagick/Font.php @@ -56,17 +56,18 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) // align vertical if (strtolower($this->valign) != 'bottom') { - // calculate box size - $dimensions = $image->getCore()->queryFontMetrics($draw, $this->text); - // corrections on y-position switch (strtolower($this->valign)) { case 'center': case 'middle': + // calculate box size + $dimensions = $image->getCore()->queryFontMetrics($draw, $this->text); $posy = $posy + $dimensions['textHeight'] * 0.65 / 2; break; case 'top': + // calculate box size + $dimensions = $image->getCore()->queryFontMetrics($draw, $this->text, false); $posy = $posy + $dimensions['textHeight'] * 0.65; break; } From 45a41a38bd1e5290cd51ab773013e6f041b2b711 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 19 Aug 2016 16:41:12 +0200 Subject: [PATCH 0008/1667] fixed issue with loading transparent images (svg format) --- src/Intervention/Image/Imagick/Decoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php index c54092a19..a06054cb2 100644 --- a/src/Intervention/Image/Imagick/Decoder.php +++ b/src/Intervention/Image/Imagick/Decoder.php @@ -18,6 +18,7 @@ public function initFromPath($path) try { + $core->setBackgroundColor(new \ImagickPixel('transparent')); $core->readImage($path); $core->setImageType(\Imagick::IMGTYPE_TRUECOLORMATTE); From 751cf4fe9fb714dae20e29c5802e6604298bc551 Mon Sep 17 00:00:00 2001 From: Nick Howell Date: Thu, 1 Sep 2016 12:48:48 -0400 Subject: [PATCH 0009/1667] Use \Illuminate\Support\Facades\Response instead of the \Response alias Using the full name avoids problems where the user has commented out the alias or has aliased "Response" to something else --- src/Intervention/Image/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Response.php b/src/Intervention/Image/Response.php index 28a3782dc..ce27e795c 100644 --- a/src/Intervention/Image/Response.php +++ b/src/Intervention/Image/Response.php @@ -53,7 +53,7 @@ public function make() if (function_exists('app') && is_a($app = app(), 'Illuminate\Foundation\Application')) { - $response = \Response::make($data); + $response = \Illuminate\Support\Facades\Response::make($data); $response->header('Content-Type', $mime); $response->header('Content-Length', $length); From 855f40de333770fb480953dbd881bac1d22a769d Mon Sep 17 00:00:00 2001 From: tchiotludo Date: Mon, 19 Sep 2016 16:58:51 +0200 Subject: [PATCH 0010/1667] Missing set minetype on Imagick --- src/Intervention/Image/Imagick/Encoder.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index 1c193237d..d744a4451 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -44,6 +44,8 @@ protected function processPng() $imagick->setCompression($compression); $imagick->setImageCompression($compression); + $this->image->mime = image_type_to_mime_type(IMAGETYPE_PNG); + return $imagick->getImagesBlob(); } @@ -63,6 +65,8 @@ protected function processGif() $imagick->setCompression($compression); $imagick->setImageCompression($compression); + $this->image->mime = image_type_to_mime_type(IMAGETYPE_GIF); + return $imagick->getImagesBlob(); } @@ -84,6 +88,8 @@ protected function processTiff() $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); + $this->image->mime = image_type_to_mime_type(IMAGETYPE_TIFF_II); + return $imagick->getImagesBlob(); } @@ -103,6 +109,8 @@ protected function processBmp() $imagick->setCompression($compression); $imagick->setImageCompression($compression); + $this->image->mime = image_type_to_mime_type(IMAGETYPE_BMP); + return $imagick->getImagesBlob(); } @@ -122,6 +130,8 @@ protected function processIco() $imagick->setCompression($compression); $imagick->setImageCompression($compression); + $this->image->mime = image_type_to_mime_type(IMAGETYPE_ICO); + return $imagick->getImagesBlob(); } @@ -141,6 +151,8 @@ protected function processPsd() $imagick->setCompression($compression); $imagick->setImageCompression($compression); + $this->image->mime = image_type_to_mime_type(IMAGETYPE_PSD); + return $imagick->getImagesBlob(); } } From 8608eb019182e1a4066e8cf9d26b77110feae071 Mon Sep 17 00:00:00 2001 From: Alex Chalupka Date: Sun, 2 Oct 2016 02:23:08 -0500 Subject: [PATCH 0011/1667] Add support for text kerning (ImageMagick only) --- src/Intervention/Image/AbstractFont.php | 28 +++++++++++++++++++++++++ src/Intervention/Image/Gd/Font.php | 14 +++++++++++++ src/Intervention/Image/Imagick/Font.php | 1 + tests/AbstractFontTest.php | 11 ++++++++-- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 5aeb1f764..321a224c2 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -46,6 +46,13 @@ abstract class AbstractFont */ public $valign; + /** + * Space between text characters + * + * @var float + */ + public $kerning = 0; + /** * Path to TTF or GD library internal font file of the text * @@ -199,6 +206,27 @@ public function getValign() return $this->valign; } + /** + * Set text kerning + * + * @param string $kerning + * @return void + */ + public function kerning($kerning) + { + $this->kerning = $kerning; + } + + /** + * Get kerning + * + * @return float + */ + public function getKerning() + { + return $this->kerning; + } + /** * Set path to font file * diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index df2d082d0..bbea2ba8d 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -252,4 +252,18 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) imagestring($image->getCore(), $this->getInternalFont(), $posx, $posy, $this->text, $color->getInt()); } } + + /** + * Set text kerning + * + * @param string $kerning + * @return void + */ + public function kerning($kerning) + { + throw new \Intervention\Image\Exception\NotSupportedException( + "Kerning is not supported by GD driver." + ); + } + } diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php index 9ae2f9786..36e05d351 100644 --- a/src/Intervention/Image/Imagick/Font.php +++ b/src/Intervention/Image/Imagick/Font.php @@ -35,6 +35,7 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $draw->setFontSize($this->size); $draw->setFillColor($color->getPixel()); + $draw->setTextKerning($this->kerning); // align horizontal switch (strtolower($this->align)) { diff --git a/tests/AbstractFontTest.php b/tests/AbstractFontTest.php index cf1d03b5a..dee9cb825 100644 --- a/tests/AbstractFontTest.php +++ b/tests/AbstractFontTest.php @@ -12,7 +12,7 @@ public function testConstructor() $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont', array('test')); $this->assertEquals('test', $font->text); } - + public function testText() { $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); @@ -55,6 +55,13 @@ public function testValign() $this->assertEquals('top', $font->valign); } + public function testKerning() + { + $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); + $font->kerning(10.5); + $this->assertEquals(10.5, $font->kerning); + } + public function testFile() { $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); @@ -66,7 +73,7 @@ public function testCountLines() { $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); $font->text('foo'.PHP_EOL.'bar'.PHP_EOL.'baz'); - $this->assertEquals(3, $font->countLines()); + $this->assertEquals(3, $font->countLines()); $font->text("foo\nbar\nbaz"); $this->assertEquals(3, $font->countLines()); $font->text('foo From 72c753075d1bf8487a027a5151f87f7d0ee60cb6 Mon Sep 17 00:00:00 2001 From: "martins.briedis" Date: Thu, 5 Jan 2017 14:02:17 +0200 Subject: [PATCH 0012/1667] Fixed issue when reading image from stream if stream is not seekable (Amazon S3, for example) --- src/Intervention/Image/AbstractDecoder.php | 27 +++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index ead7c48de..74e158ada 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -31,7 +31,7 @@ abstract public function initFromGdResource($resource); /** * Initiates new image from Imagick object * - * @param Imagick $object + * @param \Imagick $object * @return \Intervention\Image\Image */ abstract public function initFromImagick(\Imagick $object); @@ -79,9 +79,18 @@ public function initFromUrl($url) public function initFromStream($stream) { $offset = ftell($stream); - rewind($stream); + $shouldAndCanSeek = $offset !== 0 && $this->isStreamSeekable($stream); + + if ($shouldAndCanSeek) { + rewind($stream); + } + $data = @stream_get_contents($stream); - fseek($stream, $offset); + + if ($shouldAndCanSeek) { + fseek($stream, $offset); + } + if ($data) { return $this->initFromBinary($data); } @@ -91,6 +100,18 @@ public function initFromStream($stream) ); } + /** + * Checks if we can move the pointer for this stream + * + * @param resource $stream + * @return bool + */ + private function isStreamSeekable($stream) + { + $metadata = stream_get_meta_data($stream); + return $metadata['seekable']; + } + /** * Determines if current source data is GD resource * From e6ec5dcfd9056e8362026d48b324ae3a897d1e2d Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Sat, 7 Jan 2017 17:48:52 +0100 Subject: [PATCH 0013/1667] add support for exif to imagick extension without requiring exif extension --- .../Image/Imagick/Commands/ExifCommand.php | 43 ++++++++++++++++ tests/ExifCommandTest.php | 51 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/Intervention/Image/Imagick/Commands/ExifCommand.php diff --git a/src/Intervention/Image/Imagick/Commands/ExifCommand.php b/src/Intervention/Image/Imagick/Commands/ExifCommand.php new file mode 100644 index 000000000..31209e4dd --- /dev/null +++ b/src/Intervention/Image/Imagick/Commands/ExifCommand.php @@ -0,0 +1,43 @@ +getCore(); + + // when getImageProperty is not supported fallback to default exif command + if ( ! method_exists($core, 'getImageProperties')) { + return parent::execute($image); + } + + $requestedKey = $this->argument(0)->value(); + if ($requestedKey !== null) { + $this->setOutput($core->getImageProperty('exif:' . $requestedKey)); + return true; + } + + $exif = []; + $properties = $core->getImageProperties(); + foreach ($properties as $key => $value) { + if (substr($key, 0, 5) !== 'exif:') { + continue; + } + + $exif[substr($key, 6)] = $value; + } + + $this->setOutput($exif); + return true; + } +} diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php index dac43fa97..48ce0ffea 100644 --- a/tests/ExifCommandTest.php +++ b/tests/ExifCommandTest.php @@ -57,4 +57,55 @@ public function testFetchFromPng() $this->assertTrue($command->hasOutput()); $this->assertEquals(null, $command->getOutput()); } + + public function testImagickFetchAll() + { + $image = new Image; + $image->dirname = __DIR__.'/images'; + $image->basename = 'exif.jpg'; + $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array()); + $result = $command->execute($image); + $this->assertTrue($result); + $this->assertTrue($command->hasOutput()); + $this->assertInternalType('array', $command->getOutput()); + } + + public function testImagickFetchDefined() + { + $image = new Image; + $image->dirname = __DIR__.'/images'; + $image->basename = 'exif.jpg'; + $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('Artist')); + $result = $command->execute($image); + $this->assertTrue($result); + $this->assertTrue($command->hasOutput()); + $this->assertEquals('Oliver Vogel', $command->getOutput()); + } + + public function testImagickNonExisting() + { + $image = new Image; + $image->dirname = __DIR__.'/images'; + $image->basename = 'exif.jpg'; + $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('xxx')); + $result = $command->execute($image); + $this->assertTrue($result); + $this->assertTrue($command->hasOutput()); + $this->assertEquals(null, $command->getOutput()); + } + + public function testImagickFallbackToExifExtenstion() + { + $imagick = Mockery::mock('stdClass'); + $image = Mockery::mock('Intervention\Image\Image'); + $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image->dirname = __DIR__.'/images'; + $image->basename = 'exif.jpg'; + + $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('Artist')); + $result = $command->execute($image); + $this->assertTrue($result); + $this->assertTrue($command->hasOutput()); + $this->assertEquals('Oliver Vogel', $command->getOutput()); + } } From bd9a631d7832c7596b65b2b9fd9f5eb222386de5 Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Sat, 7 Jan 2017 17:59:28 +0100 Subject: [PATCH 0014/1667] test 19 exif tags --- tests/ExifCommandTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php index 48ce0ffea..0fa604893 100644 --- a/tests/ExifCommandTest.php +++ b/tests/ExifCommandTest.php @@ -68,6 +68,7 @@ public function testImagickFetchAll() $this->assertTrue($result); $this->assertTrue($command->hasOutput()); $this->assertInternalType('array', $command->getOutput()); + $this->assertCount(19, $command->getOutput()); } public function testImagickFetchDefined() From 246341693db72460d9fb0d6d29dd142c8bd1136d Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Sat, 7 Jan 2017 18:12:53 +0100 Subject: [PATCH 0015/1667] different solution, prefer extension over imagick --- .../Image/Imagick/Commands/ExifCommand.php | 23 +++++++++++-- tests/ExifCommandTest.php | 32 +++++++++---------- tests/ImagickSystemTest.php | 2 +- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/Intervention/Image/Imagick/Commands/ExifCommand.php b/src/Intervention/Image/Imagick/Commands/ExifCommand.php index 31209e4dd..3d54fedca 100644 --- a/src/Intervention/Image/Imagick/Commands/ExifCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ExifCommand.php @@ -6,6 +6,20 @@ class ExifCommand extends BaseCommand { + /** + * Prefer extension or not + * + * @var bool + */ + private $preferExtension = true; + + /** + * + */ + public function dontPreferExtension() { + $this->preferExtension = false; + } + /** * Read Exif data from the given image * @@ -14,11 +28,16 @@ class ExifCommand extends BaseCommand */ public function execute($image) { + if ($this->preferExtension && function_exists('exif_read_data')) { + return parent::execute($image); + } + $core = $image->getCore(); - // when getImageProperty is not supported fallback to default exif command if ( ! method_exists($core, 'getImageProperties')) { - return parent::execute($image); + throw new \Intervention\Image\Exception\NotSupportedException( + "Reading Exif data is not supported by this PHP installation." + ); } $requestedKey = $this->argument(0)->value(); diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php index 0fa604893..074ed6647 100644 --- a/tests/ExifCommandTest.php +++ b/tests/ExifCommandTest.php @@ -60,23 +60,20 @@ public function testFetchFromPng() public function testImagickFetchAll() { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; + $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array()); + $command->dontPreferExtension(); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); $this->assertInternalType('array', $command->getOutput()); - $this->assertCount(19, $command->getOutput()); } public function testImagickFetchDefined() { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; + $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('Artist')); + $command->dontPreferExtension(); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -85,10 +82,9 @@ public function testImagickFetchDefined() public function testImagickNonExisting() { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('xxx')); + $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); + $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('xx')); + $command->dontPreferExtension(); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -97,16 +93,18 @@ public function testImagickNonExisting() public function testImagickFallbackToExifExtenstion() { - $imagick = Mockery::mock('stdClass'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - + $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('Artist')); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); $this->assertEquals('Oliver Vogel', $command->getOutput()); } + + private function imagick() + { + return new \Intervention\Image\ImageManager(array( + 'driver' => 'imagick' + )); + } } diff --git a/tests/ImagickSystemTest.php b/tests/ImagickSystemTest.php index 89b8f8ee6..7c4419ac3 100644 --- a/tests/ImagickSystemTest.php +++ b/tests/ImagickSystemTest.php @@ -1465,7 +1465,7 @@ public function testExifReadAll() $img = $this->manager()->make('tests/images/exif.jpg'); $data = $img->exif(); $this->assertInternalType('array', $data); - $this->assertEquals(19, count($data)); + $this->assertGreaterThanOrEqual(13, count($data)); } public function testExifReadKey() From 8c9edf96701632b758d9cf9193de126437f7bf82 Mon Sep 17 00:00:00 2001 From: Wade Urry Date: Mon, 9 Jan 2017 08:25:58 +0000 Subject: [PATCH 0016/1667] Make AbstractFont a fluent interface by return $this from setter methods --- src/Intervention/Image/AbstractFont.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 5aeb1f764..8bcf3b286 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -82,6 +82,8 @@ public function __construct($text = null) public function text($text) { $this->text = $text; + + return $this; } /** @@ -103,6 +105,8 @@ public function getText() public function size($size) { $this->size = $size; + + return $this; } /** @@ -124,6 +128,8 @@ public function getSize() public function color($color) { $this->color = $color; + + return $this; } /** @@ -145,6 +151,8 @@ public function getColor() public function angle($angle) { $this->angle = $angle; + + return $this; } /** @@ -166,6 +174,8 @@ public function getAngle() public function align($align) { $this->align = $align; + + return $this; } /** @@ -187,6 +197,8 @@ public function getAlign() public function valign($valign) { $this->valign = $valign; + + return $this; } /** @@ -208,6 +220,8 @@ public function getValign() public function file($file) { $this->file = $file; + + return $this; } /** From 6337bf6b1dd25039de47c8fb90ff333a413c4d1a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 9 Jan 2017 21:36:05 +0100 Subject: [PATCH 0017/1667] fix --- tests/GdSystemTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index 318a6446d..293eac89b 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -1169,10 +1169,10 @@ public function testLimitColorsKeepTransparencyWithMatte() { $img = $this->manager()->make('tests/images/star.png'); $img->limitColors(64, '#00ff00'); + $img->save('tests/images/foofoofoofoofoofoofoofoo.png'); $this->assertLessThanOrEqual(65, imagecolorstotal($img->getCore())); $this->assertTransparentPosition($img, 0, 0); $this->assertColorAtPosition('#04f204', $img, 12, 10); - $this->assertColorAtPosition('#06fe04', $img, 22, 17); $this->assertColorAtPosition('#e40214', $img, 16, 21); } From ac5276387b2102b718f8bbd75d9f251cda336f24 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 9 Jan 2017 21:36:59 +0100 Subject: [PATCH 0018/1667] fix --- tests/GdSystemTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index 293eac89b..3b107411e 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -1169,7 +1169,6 @@ public function testLimitColorsKeepTransparencyWithMatte() { $img = $this->manager()->make('tests/images/star.png'); $img->limitColors(64, '#00ff00'); - $img->save('tests/images/foofoofoofoofoofoofoofoo.png'); $this->assertLessThanOrEqual(65, imagecolorstotal($img->getCore())); $this->assertTransparentPosition($img, 0, 0); $this->assertColorAtPosition('#04f204', $img, 12, 10); From e4c12e5b1d2104ff0ee184d0ee543afe405eb523 Mon Sep 17 00:00:00 2001 From: micaelpdias Date: Tue, 10 Jan 2017 11:07:20 +0000 Subject: [PATCH 0019/1667] Fix undefined method Share method will be removed as of Laravel 5.4 https://github.com/laravel/framework/commit/1a1969b6e6f793c3b2a479362641487ee9cbf736#diff-52441e04e14c52275cd5d09e8f958981L300 --- src/Intervention/Image/ImageServiceProviderLaravel5.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/ImageServiceProviderLaravel5.php b/src/Intervention/Image/ImageServiceProviderLaravel5.php index d1f759c21..9d770b877 100644 --- a/src/Intervention/Image/ImageServiceProviderLaravel5.php +++ b/src/Intervention/Image/ImageServiceProviderLaravel5.php @@ -47,7 +47,7 @@ public function register() ); // create image - $app['image'] = $app->share(function ($app) { + $app->singleton('image', function ($app) { return new ImageManager($app['config']->get('image')); }); From 91175bfb491bf7dfc80ace9af4a51af79804d7f2 Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Wed, 18 Jan 2017 20:25:45 +0100 Subject: [PATCH 0020/1667] fix exif imagick bug when reading complete array of exif data --- src/Intervention/Image/Imagick/Commands/ExifCommand.php | 2 +- tests/ExifCommandTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/Imagick/Commands/ExifCommand.php b/src/Intervention/Image/Imagick/Commands/ExifCommand.php index 3d54fedca..924522caf 100644 --- a/src/Intervention/Image/Imagick/Commands/ExifCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ExifCommand.php @@ -53,7 +53,7 @@ public function execute($image) continue; } - $exif[substr($key, 6)] = $value; + $exif[substr($key, 5)] = $value; } $this->setOutput($exif); diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php index 074ed6647..7e7cd31df 100644 --- a/tests/ExifCommandTest.php +++ b/tests/ExifCommandTest.php @@ -67,6 +67,7 @@ public function testImagickFetchAll() $this->assertTrue($result); $this->assertTrue($command->hasOutput()); $this->assertInternalType('array', $command->getOutput()); + $this->assertEquals('Oliver Vogel', $command->getOutput()['Artist']); } public function testImagickFetchDefined() From 9729f0ef21523c17e04afea4771c4ff2a69dd5ed Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Wed, 18 Jan 2017 20:35:45 +0100 Subject: [PATCH 0021/1667] allow intervention to use own drivers --- src/Intervention/Image/ImageManager.php | 20 +++++++++++++++----- tests/ImageManagerTest.php | 11 +++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Intervention/Image/ImageManager.php b/src/Intervention/Image/ImageManager.php index a5df0540b..205d17f04 100644 --- a/src/Intervention/Image/ImageManager.php +++ b/src/Intervention/Image/ImageManager.php @@ -100,15 +100,25 @@ public function cache(Closure $callback, $lifetime = null, $returnObj = false) */ private function createDriver() { - $drivername = ucfirst($this->config['driver']); - $driverclass = sprintf('Intervention\\Image\\%s\\Driver', $drivername); + if (is_string($this->config['driver'])) { + $drivername = ucfirst($this->config['driver']); + $driverclass = sprintf('Intervention\\Image\\%s\\Driver', $drivername); - if (class_exists($driverclass)) { - return new $driverclass; + if (class_exists($driverclass)) { + return new $driverclass; + } + + throw new \Intervention\Image\Exception\NotSupportedException( + "Driver ({$drivername}) could not be instantiated." + ); + } + + if ($this->config['driver'] instanceof AbstractDriver) { + return $this->config['driver']; } throw new \Intervention\Image\Exception\NotSupportedException( - "Driver ({$drivername}) could not be instantiated." + "Unknown driver type." ); } diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index d28d0c4c7..7f0a78c30 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -1,5 +1,6 @@ assertEquals('foo', $manager->config['driver']); $this->assertEquals('baz', $manager->config['bar']); } + + public function testConfigureObject() + { + $config = array('driver' => new Intervention\Image\Imagick\Driver()); + $manager = new ImageManager($config); + + $image = $manager->make('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); + $this->assertInstanceOf(Image::class, $image); + $this->assertInstanceOf(Imagick::class, $image->getCore()); + } } From b27bbe5771e471b24445884a29fd7e9498fae07e Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Wed, 18 Jan 2017 21:15:33 +0100 Subject: [PATCH 0022/1667] fix php 5.4 --- tests/ImageManagerTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index 7f0a78c30..e23d91278 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -1,6 +1,5 @@ make('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); - $this->assertInstanceOf(Image::class, $image); - $this->assertInstanceOf(Imagick::class, $image->getCore()); + $this->assertInstanceOf('Intervention\Image\Image', $image); + $this->assertInstanceOf('Imagick', $image->getCore()); } } From 41354525c97b98ea58f85a5e2d8023c7a9bb0694 Mon Sep 17 00:00:00 2001 From: mmjjb Date: Fri, 20 Jan 2017 14:23:49 +0100 Subject: [PATCH 0023/1667] Update URL function, with browser headers Several image providers have detection for which user agent is being used. Since I had several times issue's with that images where unavailable for Intervention since an missing user agent, I decided to add this header to get rid of the problems. --- src/Intervention/Image/AbstractDecoder.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 74e158ada..c4a4616d0 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -61,6 +61,17 @@ public function __construct($data = null) */ public function initFromUrl($url) { + + $options = array( + 'http' => array( + 'method'=>"GET", + 'header'=>"Accept-language: en\r\n". + "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2\r\n" + ) + ); + + $context = stream_context_create($options); + if ($data = @file_get_contents($url)) { return $this->initFromBinary($data); } From f87c7de1af0a501c4e4127b5176e3faf6d9e6ab4 Mon Sep 17 00:00:00 2001 From: mmjjb Date: Fri, 20 Jan 2017 14:25:05 +0100 Subject: [PATCH 0024/1667] Update AbstractDecoder.php Several image providers have detection for which user agent is being used. Since I had several times issue's with that images where unavailable for Intervention since an missing user agent, I decided to add this header to get rid of the problems. --- src/Intervention/Image/AbstractDecoder.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 74e158ada..b5d78ca2f 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -61,7 +61,18 @@ public function __construct($data = null) */ public function initFromUrl($url) { - if ($data = @file_get_contents($url)) { + + $options = array( + 'http' => array( + 'method'=>"GET", + 'header'=>"Accept-language: en\r\n". + "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2\r\n" + ) + ); + + $context = stream_context_create($options); + + if ($data = @file_get_contents($url, false, $context)) { return $this->initFromBinary($data); } From 20a46aea36e38b9e47e77320ca865195a0a8d477 Mon Sep 17 00:00:00 2001 From: Youssef Jradeh Date: Wed, 25 Jan 2017 13:07:47 +0200 Subject: [PATCH 0025/1667] Update ImageServiceProviderLumen 5.4 Update ImageServiceProviderLumen based on 5.4 releases, replacing of share by singleton --- src/Intervention/Image/ImageServiceProviderLumen.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/ImageServiceProviderLumen.php b/src/Intervention/Image/ImageServiceProviderLumen.php index 933ac21cd..4a381ccd4 100644 --- a/src/Intervention/Image/ImageServiceProviderLumen.php +++ b/src/Intervention/Image/ImageServiceProviderLumen.php @@ -25,7 +25,7 @@ public function register() $app->configure('image'); // create image - $app['image'] = $app->share(function ($app) { + $app->singleton('image',function ($app) { return new ImageManager($app['config']->get('image')); }); From 29f478508bbb38f3725d44c9e813d91d5925c42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szekeres=20Ba=CC=81lint?= Date: Mon, 30 Jan 2017 17:04:22 +0100 Subject: [PATCH 0026/1667] handle IM7 constant change IMGTYPE_TRUECOLORMATTE -> IMGTYPE_TRUECOLOR --- src/Intervention/Image/Imagick/Decoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php index a06054cb2..1490d1115 100644 --- a/src/Intervention/Image/Imagick/Decoder.php +++ b/src/Intervention/Image/Imagick/Decoder.php @@ -20,7 +20,7 @@ public function initFromPath($path) $core->setBackgroundColor(new \ImagickPixel('transparent')); $core->readImage($path); - $core->setImageType(\Imagick::IMGTYPE_TRUECOLORMATTE); + $core->setImageType(defined('\Imagick::IMGTYPE_TRUECOLORMATTE') ? \Imagick::IMGTYPE_TRUECOLORMATTE : \Imagick::IMGTYPE_TRUECOLOR); } catch (\ImagickException $e) { throw new \Intervention\Image\Exception\NotReadableException( From 5ec1eb0ba3dd5119da1d4ce62dcabb94202c85bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szekeres=20Ba=CC=81lint?= Date: Tue, 31 Jan 2017 14:07:05 +0100 Subject: [PATCH 0027/1667] legacy Imagick::IMGTYPE_TRUECOLOR -> Imagick::IMGTYPE_TRUECOLORALPHA --- src/Intervention/Image/Imagick/Decoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php index 1490d1115..d1d333eee 100644 --- a/src/Intervention/Image/Imagick/Decoder.php +++ b/src/Intervention/Image/Imagick/Decoder.php @@ -20,7 +20,7 @@ public function initFromPath($path) $core->setBackgroundColor(new \ImagickPixel('transparent')); $core->readImage($path); - $core->setImageType(defined('\Imagick::IMGTYPE_TRUECOLORMATTE') ? \Imagick::IMGTYPE_TRUECOLORMATTE : \Imagick::IMGTYPE_TRUECOLOR); + $core->setImageType(defined('\Imagick::IMGTYPE_TRUECOLORMATTE') ? \Imagick::IMGTYPE_TRUECOLORMATTE : \Imagick::IMGTYPE_TRUECOLORALPHA); } catch (\ImagickException $e) { throw new \Intervention\Image\Exception\NotReadableException( From e8881fd99b9804b29e02d6d1c2c15ee459335cf1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 4 Feb 2017 11:37:19 +0100 Subject: [PATCH 0028/1667] replicated https://github.com/Intervention/image/pull/672/files --- src/Intervention/Image/Exception/ImageException.php | 8 ++++++++ .../Image/Exception/InvalidArgumentException.php | 2 +- .../Image/Exception/MissingDependencyException.php | 2 +- src/Intervention/Image/Exception/NotFoundException.php | 2 +- src/Intervention/Image/Exception/NotReadableException.php | 2 +- .../Image/Exception/NotSupportedException.php | 2 +- src/Intervention/Image/Exception/NotWritableException.php | 2 +- src/Intervention/Image/Exception/RuntimeException.php | 2 +- 8 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 src/Intervention/Image/Exception/ImageException.php diff --git a/src/Intervention/Image/Exception/ImageException.php b/src/Intervention/Image/Exception/ImageException.php new file mode 100644 index 000000000..83e6b91f2 --- /dev/null +++ b/src/Intervention/Image/Exception/ImageException.php @@ -0,0 +1,8 @@ + Date: Sat, 4 Feb 2017 11:57:32 +0100 Subject: [PATCH 0029/1667] Travis CI update --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d2a0b9be0..f63922805 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,12 +5,12 @@ php: - 5.5 - 5.6 - 7.0 + - 7.1 - nightly - hhvm matrix: allow_failures: - - php: 7.0 - php: nightly - php: hhvm From 0020dd50e6ba2b23d1c43aba1f4ac68d60c7c03d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 6 Feb 2017 14:26:06 +0100 Subject: [PATCH 0030/1667] better handling of IM7 constant change --- src/Intervention/Image/Imagick/Decoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php index d1d333eee..6b2b40daf 100644 --- a/src/Intervention/Image/Imagick/Decoder.php +++ b/src/Intervention/Image/Imagick/Decoder.php @@ -20,7 +20,7 @@ public function initFromPath($path) $core->setBackgroundColor(new \ImagickPixel('transparent')); $core->readImage($path); - $core->setImageType(defined('\Imagick::IMGTYPE_TRUECOLORMATTE') ? \Imagick::IMGTYPE_TRUECOLORMATTE : \Imagick::IMGTYPE_TRUECOLORALPHA); + $core->setImageType(defined('\Imagick::IMGTYPE_TRUECOLORALPHA') ? \Imagick::IMGTYPE_TRUECOLORALPHA : \Imagick::IMGTYPE_TRUECOLORMATTE); } catch (\ImagickException $e) { throw new \Intervention\Image\Exception\NotReadableException( From b120be82c2c8896d4226628635ed9bd1dc28effa Mon Sep 17 00:00:00 2001 From: Baptiste DUCATEL Date: Fri, 10 Feb 2017 16:13:02 +0100 Subject: [PATCH 0031/1667] Fix rgba string with non-localized number --- src/Intervention/Image/Gd/Color.php | 2 +- src/Intervention/Image/Gd/Commands/OpacityCommand.php | 2 +- src/Intervention/Image/Imagick/Color.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Intervention/Image/Gd/Color.php b/src/Intervention/Image/Gd/Color.php index 51ba16067..5238831fa 100644 --- a/src/Intervention/Image/Gd/Color.php +++ b/src/Intervention/Image/Gd/Color.php @@ -177,7 +177,7 @@ public function getArray() */ public function getRgba() { - return sprintf('rgba(%d, %d, %d, %.2f)', $this->r, $this->g, $this->b, round(1 - $this->a / 127, 2)); + return sprintf('rgba(%d, %d, %d, %.2F)', $this->r, $this->g, $this->b, round(1 - $this->a / 127, 2)); } /** diff --git a/src/Intervention/Image/Gd/Commands/OpacityCommand.php b/src/Intervention/Image/Gd/Commands/OpacityCommand.php index 201fcabc8..081e68a4a 100644 --- a/src/Intervention/Image/Gd/Commands/OpacityCommand.php +++ b/src/Intervention/Image/Gd/Commands/OpacityCommand.php @@ -18,7 +18,7 @@ public function execute($image) $size = $image->getSize(); // build temp alpha mask - $mask_color = sprintf('rgba(0, 0, 0, %.1f)', $transparency / 100); + $mask_color = sprintf('rgba(0, 0, 0, %.1F)', $transparency / 100); $mask = $image->getDriver()->newImage($size->width, $size->height, $mask_color); // mask image diff --git a/src/Intervention/Image/Imagick/Color.php b/src/Intervention/Image/Imagick/Color.php index a914feea8..3940993ed 100644 --- a/src/Intervention/Image/Imagick/Color.php +++ b/src/Intervention/Image/Imagick/Color.php @@ -163,7 +163,7 @@ public function getArray() */ public function getRgba() { - return sprintf('rgba(%d, %d, %d, %.2f)', + return sprintf('rgba(%d, %d, %d, %.2F)', $this->getRedValue(), $this->getGreenValue(), $this->getBlueValue(), @@ -248,7 +248,7 @@ private function setPixel($r, $g, $b, $a = null) $a = is_null($a) ? 1 : $a; return $this->pixel = new \ImagickPixel( - sprintf('rgba(%d, %d, %d, %.2f)', $r, $g, $b, $a) + sprintf('rgba(%d, %d, %d, %.2F)', $r, $g, $b, $a) ); } From c0705bbb5c486960b9bc12edec249331c7e3bfa1 Mon Sep 17 00:00:00 2001 From: Rick Burgess Date: Wed, 22 Feb 2017 15:10:25 +0000 Subject: [PATCH 0032/1667] Slightly better than using "\n" --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f63922805..6c1421062 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ before_install: - sudo add-apt-repository -y ppa:moti-p/cc - sudo apt-get update - sudo apt-get -y --reinstall install imagemagick - - printf "\n" | pecl install imagick-beta + - yes | pecl install imagick-beta - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.4" ]]; then echo "extension = imagick.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi before_script: From c9a162592dda7ae4ad3b49305da41e82155f9b9a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 23 Feb 2017 17:15:05 +0100 Subject: [PATCH 0033/1667] changed check on initFromPath error in GD decoder --- src/Intervention/Image/Gd/Decoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 5525b4ddc..313e62619 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -42,7 +42,7 @@ public function initFromPath($path) ); } - if ($core === false) { + if (empty($core)) { throw new \Intervention\Image\Exception\NotReadableException( "Unable to read image from file ({$path})." ); From f9784c79b4a1b66a694a104dd5e7f7214cf56721 Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Wed, 8 Mar 2017 14:12:21 +0100 Subject: [PATCH 0034/1667] Fix IDE error PHPStorm complains that mixed types cannot have a string as default value. --- src/Intervention/Image/Image.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 4c8e690e0..8eade9d3a 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -20,7 +20,7 @@ * @method mixed exif(string $key = null) Read Exif meta data from current image. * @method mixed iptc(string $key = null) Read Iptc meta data from current image. * @method \Intervention\Image\Image fill(mixed $filling, integer $x = null, integer $y = null) Fill current image with given color or another image used as tile for filling. Pass optional x, y coordinates to start at a certain point. - * @method \Intervention\Image\Image flip(mixed $mode = 'h') Mirror the current image horizontally or vertically by specifying the mode. + * @method \Intervention\Image\Image flip(string $mode = 'h') Mirror the current image horizontally or vertically by specifying the mode. * @method \Intervention\Image\Image fit(integer $width, integer $height = null, \Closure $callback = null, string $position = 'center') Combine cropping and resizing to format image in a smart way. The method will find the best fitting aspect ratio of your given width and height on the current image automatically, cut it out and resize it to the given dimension. You may pass an optional Closure callback as third parameter, to prevent possible upsizing and a custom position of the cutout as fourth parameter. * @method \Intervention\Image\Image gamma(float $correction) Performs a gamma correction operation on the current image. * @method \Intervention\Image\Image greyscale() Turns image into a greyscale version. @@ -41,7 +41,7 @@ * @method \Intervention\Image\Image rectangle(integer $x1, integer $y1, integer $x2, integer $y2, \Closure $callback = null) Draw a colored rectangle on current image with top-left corner on x,y point 1 and bottom-right corner at x,y point 2. Define the overall appearance of the shape by passing a Closure callback as an optional parameter. * @method \Intervention\Image\Image reset(string $name = 'default') Resets all of the modifications to a state saved previously by backup under an optional name. * @method \Intervention\Image\Image resize(integer $width, integer $height, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. - * @method \Intervention\Image\Image resizeCanvas(integer $width, integer $height, string $anchor = 'center', boolean $relative = false, mixed $bgcolor = '#000000') Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. + * @method \Intervention\Image\Image resizeCanvas(integer $width, integer $height, string $anchor = 'center', boolean $relative = false, string $bgcolor = '#000000') Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. * @method mixed response(string $format = null, integer $quality = 90) Sends HTTP response with current image in given format and quality. * @method \Intervention\Image\Image rotate(float $angle, string $bgcolor = '#000000') Rotate the current image counter-clockwise by a given angle. Optionally define a background color for the uncovered zone after the rotation. * @method \Intervention\Image\Image sharpen(integer $amount = 10) Sharpen current image with an optional amount. Use values between 0 and 100. From 60c1c8f363c60dc47322cc8663accf39b80d70f1 Mon Sep 17 00:00:00 2001 From: Richard Brown Date: Tue, 28 Mar 2017 10:05:01 +0100 Subject: [PATCH 0035/1667] update travis config --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6c1421062..8dcadc253 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ php: - 7.1 - nightly - hhvm - + matrix: allow_failures: - php: nightly @@ -25,4 +25,4 @@ before_script: - composer self-update - composer install --prefer-source --no-interaction --dev -script: phpunit +script: vendor/bin/phpunit From 58881f0bc8d32c25aff5d97a6433bc067a128fdb Mon Sep 17 00:00:00 2001 From: denmasyarikin Date: Sun, 2 Apr 2017 02:07:11 +0700 Subject: [PATCH 0036/1667] [FIX] File name is longer than the maximum allowed path length in some OS --- src/Intervention/Image/AbstractDecoder.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 9adc57f98..c421c7d9b 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -186,7 +186,11 @@ public function isSymfonyUpload() public function isFilePath() { if (is_string($this->data)) { - return is_file($this->data); + try { + return is_file($this->data); + } catch (\Exception $e) { + return false; + } } return false; @@ -322,15 +326,15 @@ public function init($data) case $this->isStream(): return $this->initFromStream($this->data); - case $this->isFilePath(): - return $this->initFromPath($this->data); - case $this->isDataUrl(): return $this->initFromBinary($this->decodeDataUrl($this->data)); case $this->isBase64(): return $this->initFromBinary(base64_decode($this->data)); + case $this->isFilePath(): + return $this->initFromPath($this->data); + default: throw new Exception\NotReadableException("Image source not readable"); } From 836026b47fa08fe785e677d7ac15b43c47229688 Mon Sep 17 00:00:00 2001 From: vlakoff Date: Sun, 23 Apr 2017 03:01:16 +0200 Subject: [PATCH 0037/1667] Minor phpdoc fixes --- src/Intervention/Image/AbstractColor.php | 2 +- src/Intervention/Image/Gd/Driver.php | 2 +- src/Intervention/Image/Image.php | 6 +++--- src/Intervention/Image/Imagick/Driver.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Intervention/Image/AbstractColor.php b/src/Intervention/Image/AbstractColor.php index 618649513..592792058 100644 --- a/src/Intervention/Image/AbstractColor.php +++ b/src/Intervention/Image/AbstractColor.php @@ -98,7 +98,7 @@ abstract public function differs(AbstractColor $color, $tolerance = 0); /** * Creates new instance * - * @param string $value + * @param mixed $value */ public function __construct($value = null) { diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index 28612fc9c..6179f08f2 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -27,7 +27,7 @@ public function __construct(Decoder $decoder = null, Encoder $encoder = null) * * @param integer $width * @param integer $height - * @param string $background + * @param mixed $background * @return \Intervention\Image\Image */ public function newImage($width, $height, $background = null) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 8eade9d3a..72268449a 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -9,7 +9,7 @@ * @method \Intervention\Image\Image backup(string $name = 'default') Backups current image state as fallback for reset method under an optional name. Overwrites older state on every call, unless a different name is passed. * @method \Intervention\Image\Image blur(integer $amount = 1) Apply a gaussian blur filter with a optional amount on the current image. Use values between 0 and 100. * @method \Intervention\Image\Image brightness(integer $level) Changes the brightness of the current image by the given level. Use values between -100 for min. brightness. 0 for no change and +100 for max. brightness. - * @method \Intervention\Image\Image cache(\Closure $callback, integer $lifetime = null, boolean $returnObj = false) Method to create a new cached image instance from a Closure callback. Pass a lifetime in minutes for the callback and decide whether you want to get an Intervention Image instance as return value or just receive the image stream. + * @method \Intervention\Image\Image cache(\Closure $callback, integer $lifetime = null, boolean $returnObj = false) Method to create a new cached image instance from a Closure callback. Pass a lifetime in minutes for the callback and decide whether you want to get an Intervention Image instance as return value or just receive the image stream. * @method \Intervention\Image\Image canvas(integer $width, integer $height, mixed $bgcolor = null) Factory method to create a new empty image instance with given width and height. You can define a background-color optionally. By default the canvas background is transparent. * @method \Intervention\Image\Image circle(integer $radius, integer $x, integer $y, \Closure $callback = null) Draw a circle at given x, y, coordinates with given radius. You can define the appearance of the circle by an optional closure callback. * @method \Intervention\Image\Image colorize(integer $red, integer $green, integer $blue) Change the RGB color values of the current image on the given channels red, green and blue. The input values are normalized so you have to include parameters from 100 for maximum color value. 0 for no change and -100 to take out all the certain color on the image. @@ -20,7 +20,7 @@ * @method mixed exif(string $key = null) Read Exif meta data from current image. * @method mixed iptc(string $key = null) Read Iptc meta data from current image. * @method \Intervention\Image\Image fill(mixed $filling, integer $x = null, integer $y = null) Fill current image with given color or another image used as tile for filling. Pass optional x, y coordinates to start at a certain point. - * @method \Intervention\Image\Image flip(string $mode = 'h') Mirror the current image horizontally or vertically by specifying the mode. + * @method \Intervention\Image\Image flip(string $mode = 'h') Mirror the current image horizontally or vertically by specifying the mode. * @method \Intervention\Image\Image fit(integer $width, integer $height = null, \Closure $callback = null, string $position = 'center') Combine cropping and resizing to format image in a smart way. The method will find the best fitting aspect ratio of your given width and height on the current image automatically, cut it out and resize it to the given dimension. You may pass an optional Closure callback as third parameter, to prevent possible upsizing and a custom position of the cutout as fourth parameter. * @method \Intervention\Image\Image gamma(float $correction) Performs a gamma correction operation on the current image. * @method \Intervention\Image\Image greyscale() Turns image into a greyscale version. @@ -41,7 +41,7 @@ * @method \Intervention\Image\Image rectangle(integer $x1, integer $y1, integer $x2, integer $y2, \Closure $callback = null) Draw a colored rectangle on current image with top-left corner on x,y point 1 and bottom-right corner at x,y point 2. Define the overall appearance of the shape by passing a Closure callback as an optional parameter. * @method \Intervention\Image\Image reset(string $name = 'default') Resets all of the modifications to a state saved previously by backup under an optional name. * @method \Intervention\Image\Image resize(integer $width, integer $height, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. - * @method \Intervention\Image\Image resizeCanvas(integer $width, integer $height, string $anchor = 'center', boolean $relative = false, string $bgcolor = '#000000') Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. + * @method \Intervention\Image\Image resizeCanvas(integer $width, integer $height, string $anchor = 'center', boolean $relative = false, mixed $bgcolor = null) Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. * @method mixed response(string $format = null, integer $quality = 90) Sends HTTP response with current image in given format and quality. * @method \Intervention\Image\Image rotate(float $angle, string $bgcolor = '#000000') Rotate the current image counter-clockwise by a given angle. Optionally define a background color for the uncovered zone after the rotation. * @method \Intervention\Image\Image sharpen(integer $amount = 10) Sharpen current image with an optional amount. Use values between 0 and 100. diff --git a/src/Intervention/Image/Imagick/Driver.php b/src/Intervention/Image/Imagick/Driver.php index 2fbd78b82..1c72e5ab2 100644 --- a/src/Intervention/Image/Imagick/Driver.php +++ b/src/Intervention/Image/Imagick/Driver.php @@ -27,7 +27,7 @@ public function __construct(Decoder $decoder = null, Encoder $encoder = null) * * @param integer $width * @param integer $height - * @param string $background + * @param mixed $background * @return \Intervention\Image\Image */ public function newImage($width, $height, $background = null) From 478dca3b8ed348c4c899de72310eb02343ff7010 Mon Sep 17 00:00:00 2001 From: vlakoff Date: Sun, 23 Apr 2017 03:29:03 +0200 Subject: [PATCH 0038/1667] Indicate the default boolean value For explicitness and consistency. --- src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php | 2 +- src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php index c88e6f19d..70739fff9 100644 --- a/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php @@ -15,7 +15,7 @@ public function execute($image) $width = $this->argument(0)->type('digit')->required()->value(); $height = $this->argument(1)->type('digit')->required()->value(); $anchor = $this->argument(2)->value('center'); - $relative = $this->argument(3)->type('boolean')->value(); + $relative = $this->argument(3)->type('boolean')->value(false); $bgcolor = $this->argument(4)->value(); $original_width = $image->getWidth(); diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php index 8884230eb..f394c15dc 100644 --- a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php @@ -15,7 +15,7 @@ public function execute($image) $width = $this->argument(0)->type('digit')->required()->value(); $height = $this->argument(1)->type('digit')->required()->value(); $anchor = $this->argument(2)->value('center'); - $relative = $this->argument(3)->type('boolean')->value(); + $relative = $this->argument(3)->type('boolean')->value(false); $bgcolor = $this->argument(4)->value(); $original_width = $image->getWidth(); From 15ef0b00df3b626061ec5ef96bcce9d43fc1e9d0 Mon Sep 17 00:00:00 2001 From: Jack W-H Date: Sun, 23 Apr 2017 15:47:59 +0100 Subject: [PATCH 0039/1667] Fixed a bug where file path strings would be evaluated as Base64-data --- src/Intervention/Image/AbstractDecoder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index c421c7d9b..419d5cfcc 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -329,12 +329,12 @@ public function init($data) case $this->isDataUrl(): return $this->initFromBinary($this->decodeDataUrl($this->data)); - case $this->isBase64(): - return $this->initFromBinary(base64_decode($this->data)); - case $this->isFilePath(): return $this->initFromPath($this->data); + case $this->isBase64(): + return $this->initFromBinary(base64_decode($this->data)); + default: throw new Exception\NotReadableException("Image source not readable"); } From 9db393c580ccd88d92a4ed4dac2ddde4684163a6 Mon Sep 17 00:00:00 2001 From: vlakoff Date: Mon, 24 Apr 2017 05:28:47 +0200 Subject: [PATCH 0040/1667] Another small phpdoc fix --- src/Intervention/Image/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 72268449a..c1b4c2f77 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -43,7 +43,7 @@ * @method \Intervention\Image\Image resize(integer $width, integer $height, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. * @method \Intervention\Image\Image resizeCanvas(integer $width, integer $height, string $anchor = 'center', boolean $relative = false, mixed $bgcolor = null) Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. * @method mixed response(string $format = null, integer $quality = 90) Sends HTTP response with current image in given format and quality. - * @method \Intervention\Image\Image rotate(float $angle, string $bgcolor = '#000000') Rotate the current image counter-clockwise by a given angle. Optionally define a background color for the uncovered zone after the rotation. + * @method \Intervention\Image\Image rotate(float $angle, mixed $bgcolor = null) Rotate the current image counter-clockwise by a given angle. Optionally define a background color for the uncovered zone after the rotation. * @method \Intervention\Image\Image sharpen(integer $amount = 10) Sharpen current image with an optional amount. Use values between 0 and 100. * @method \Intervention\Image\Image text(string $text, integer $x = 0, integer $y = 0, \Closure $callback = null) Write a text string to the current image at an optional x,y basepoint position. You can define more details like font-size, font-file and alignment via a callback as the fourth parameter. * @method \Intervention\Image\Image trim(string $base = 'top-left', array $away = array('top', 'bottom', 'left', 'right'), integer $tolerance = 0, integer $feather = 0) Trim away image space in given color. Define an optional base to pick a color at a certain position and borders that should be trimmed away. You can also set an optional tolerance level, to trim similar colors and add a feathering border around the trimed image. From e1b0bc149258b4343145bb9a3bc035ae62acebe9 Mon Sep 17 00:00:00 2001 From: vlakoff Date: Sun, 30 Apr 2017 17:00:04 +0200 Subject: [PATCH 0041/1667] Add a code comment to prevent future regressions --- src/Intervention/Image/AbstractDecoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 419d5cfcc..00cd4f439 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -332,6 +332,7 @@ public function init($data) case $this->isFilePath(): return $this->initFromPath($this->data); + // isBase64 has to be after isFilePath to prevent false positives case $this->isBase64(): return $this->initFromBinary(base64_decode($this->data)); From 62f7ae00811dea2aa4b8c6daac2dae3c2a7ae5a5 Mon Sep 17 00:00:00 2001 From: karim-hza Date: Wed, 24 May 2017 18:07:29 +0200 Subject: [PATCH 0042/1667] Use imagecreatefromstring when imagecreatefromjpeg fail For some image the function 'imagecreatefromstring' does'nt work beacause the image is in violation with the jpeg spec. https://stackoverflow.com/questions/35337709/invalid-sos-parameters-for-sequential-jpeg/43271528#43271528 --- src/Intervention/Image/Gd/Decoder.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 313e62619..dbb9dce01 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -29,7 +29,10 @@ public function initFromPath($path) break; case IMAGETYPE_JPEG: - $core = @imagecreatefromjpeg($path); + $core = @imagecreatefromjpeg($path); + if (!$core) { + $core= @imagecreatefromstring(file_get_contents($path)); + } break; case IMAGETYPE_GIF: From c63d113976ef5dd24fb52e35a8ccde51f59df824 Mon Sep 17 00:00:00 2001 From: Bart Nagel Date: Tue, 30 May 2017 17:48:06 -0700 Subject: [PATCH 0043/1667] Don't run service provider boot method if absent This is an actual fix for #621 --- src/Intervention/Image/ImageServiceProvider.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/ImageServiceProvider.php b/src/Intervention/Image/ImageServiceProvider.php index 234e06cfb..e61e6a42e 100644 --- a/src/Intervention/Image/ImageServiceProvider.php +++ b/src/Intervention/Image/ImageServiceProvider.php @@ -40,7 +40,9 @@ public function __construct($app) */ public function boot() { - return $this->provider->boot(); + if (method_exists($this->provider, 'boot')) { + return $this->provider->boot(); + } } /** From 02aee3095a6cbd8fa574a75680540e2346dd5efb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 6 Jun 2017 18:29:00 +0200 Subject: [PATCH 0044/1667] added before_install command --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8dcadc253..1f3e5b79a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ matrix: before_install: - sudo add-apt-repository -y ppa:moti-p/cc + - sudo apt-get clean - sudo apt-get update - sudo apt-get -y --reinstall install imagemagick - yes | pecl install imagick-beta From 274403ce35bec3c419fdeedcd161c98dc02d3467 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 7 Jun 2017 19:18:28 +0200 Subject: [PATCH 0045/1667] Support Laravel Auto-Discovery --- composer.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/composer.json b/composer.json index 3817a4b52..f52fd1552 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,14 @@ "extra": { "branch-alias": { "dev-master": "2.3-dev" + }, + "laravel": { + "providers": [ + "Intervention\\Image\\ImageServiceProvider" + ], + "aliases": { + "Image": "Intervention\\Image\\Facades\\Image" + } } }, "minimum-stability": "stable" From 19f84f90bde058cb0fb5adda9ed1c034641e7366 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Mon, 26 Jun 2017 15:29:19 +0100 Subject: [PATCH 0046/1667] NEW Allow images to be initiated from PSR StreamInterfaces --- src/Intervention/Image/AbstractDecoder.php | 41 ++++++++++++---------- tests/AbstractDecoderTest.php | 3 ++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 00cd4f439..ac93cf0b3 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -2,6 +2,9 @@ namespace Intervention\Image; +use GuzzleHttp\Psr7\Stream; +use Psr\Http\Message\StreamInterface; + abstract class AbstractDecoder { /** @@ -85,22 +88,35 @@ public function initFromUrl($url) /** * Init from given stream * - * @param $stream + * @param StreamInterface|resource $stream * @return \Intervention\Image\Image */ public function initFromStream($stream) { - $offset = ftell($stream); - $shouldAndCanSeek = $offset !== 0 && $this->isStreamSeekable($stream); + if (!$stream instanceof StreamInterface) { + $stream = new Stream($stream); + } + + try { + $offset = $stream->tell(); + } catch (\RuntimeException $e) { + $offset = 0; + } + + $shouldAndCanSeek = $offset !== 0 && $stream->isSeekable(); if ($shouldAndCanSeek) { - rewind($stream); + $stream->rewind(); } - $data = @stream_get_contents($stream); + try { + $data = $stream->getContents(); + } catch (\RuntimeException $e) { + $data = null; + } if ($shouldAndCanSeek) { - fseek($stream, $offset); + $stream->seek($offset); } if ($data) { @@ -112,18 +128,6 @@ public function initFromStream($stream) ); } - /** - * Checks if we can move the pointer for this stream - * - * @param resource $stream - * @return bool - */ - private function isStreamSeekable($stream) - { - $metadata = stream_get_meta_data($stream); - return $metadata['seekable']; - } - /** * Determines if current source data is GD resource * @@ -213,6 +217,7 @@ public function isUrl() */ public function isStream() { + if ($this->data instanceof StreamInterface) return true; if (!is_resource($this->data)) return false; if (get_resource_type($this->data) !== 'stream') return false; diff --git a/tests/AbstractDecoderTest.php b/tests/AbstractDecoderTest.php index 4d633ce27..7db29f339 100644 --- a/tests/AbstractDecoderTest.php +++ b/tests/AbstractDecoderTest.php @@ -61,6 +61,9 @@ public function testIsStream() $source = $this->getTestDecoder(fopen(__DIR__ . '/images/test.jpg', 'r')); $this->assertTrue($source->isStream()); + $source = $this->getTestDecoder(new \GuzzleHttp\Psr7\Stream(fopen(__DIR__ . '/images/test.jpg', 'r'))); + $this->assertTrue($source->isStream()); + $source = $this->getTestDecoder(null); $this->assertFalse($source->isStream()); } From d144f1ddf4fc68616f3735e5ba0dd585d4c6deed Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Wed, 28 Jun 2017 19:10:35 +0100 Subject: [PATCH 0047/1667] Get travis working --- .travis.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1f3e5b79a..87bbb1e67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,9 @@ language: php +sudo: false + +dist: trusty + php: - 5.4 - 5.5 @@ -14,16 +18,9 @@ matrix: - php: nightly - php: hhvm -before_install: - - sudo add-apt-repository -y ppa:moti-p/cc - - sudo apt-get clean - - sudo apt-get update - - sudo apt-get -y --reinstall install imagemagick - - yes | pecl install imagick-beta - - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.4" ]]; then echo "extension = imagick.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi - before_script: - - composer self-update - - composer install --prefer-source --no-interaction --dev + - printf "\n" | pecl install imagick + - composer self-update || true + - composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader script: vendor/bin/phpunit From 9575e39f9e29abced499e2e4a07f79ec59d8ff23 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Wed, 28 Jun 2017 19:26:01 +0100 Subject: [PATCH 0048/1667] Bump PHP unit versions --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f52fd1552..235b2af80 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "guzzlehttp/psr7": "~1.1" }, "require-dev": { - "phpunit/phpunit": "3.*", + "phpunit/phpunit": "^4.8 || ^5.7", "mockery/mockery": "~0.9.2" }, "suggest": { From 6e7f1e7fdd1e2b18e08a8cab0a9a9df9f7ef87b7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 30 Jun 2017 17:51:00 +0200 Subject: [PATCH 0049/1667] added badges to readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 40f96dc5d..798e5ece5 100755 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. The package includes ServiceProviders and Facades for easy **Laravel** integration. +[![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) [![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) +[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image) ## Requirements @@ -50,4 +52,4 @@ Contributions to the Intervention Image library are welcome. Please note the fol Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). -Copyright 2014 [Oliver Vogel](http://olivervogel.net/) +Copyright 2017 [Oliver Vogel](http://olivervogel.net/) From ef5d12c048f07d901d137597a5256c14523c9710 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 30 Jun 2017 17:52:27 +0200 Subject: [PATCH 0050/1667] changed url --- README.md | 2 +- composer.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 798e5ece5..2e74d0a03 100755 --- a/README.md +++ b/README.md @@ -52,4 +52,4 @@ Contributions to the Intervention Image library are welcome. Please note the fol Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). -Copyright 2017 [Oliver Vogel](http://olivervogel.net/) +Copyright 2017 [Oliver Vogel](http://olivervogel.com/) diff --git a/composer.json b/composer.json index f52fd1552..51cc1fc8b 100644 --- a/composer.json +++ b/composer.json @@ -7,8 +7,8 @@ "authors": [ { "name": "Oliver Vogel", - "email": "oliver@olivervogel.net", - "homepage": "http://olivervogel.net/" + "email": "oliver@olivervogel.com", + "homepage": "http://olivervogel.com/" } ], "require": { From 75d2022f129cc27f2ea147a624b204737ebb6e5c Mon Sep 17 00:00:00 2001 From: Dave Blake Date: Mon, 3 Jul 2017 11:09:53 +0100 Subject: [PATCH 0051/1667] Allow parenthesis in Laravel5 filename when using imagecache --- src/Intervention/Image/ImageServiceProviderLaravel5.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/ImageServiceProviderLaravel5.php b/src/Intervention/Image/ImageServiceProviderLaravel5.php index 9d770b877..04cfc22ba 100644 --- a/src/Intervention/Image/ImageServiceProviderLaravel5.php +++ b/src/Intervention/Image/ImageServiceProviderLaravel5.php @@ -77,7 +77,7 @@ private function bootstrapImageCache() // imagecache route if (is_string(config('imagecache.route'))) { - $filename_pattern = '[ \w\\.\\/\\-\\@]+'; + $filename_pattern = '[ \w\\.\\/\\-\\@\(\)]+'; // route to access template applied image file $app['router']->get(config('imagecache.route').'/{template}/{filename}', array( From 961c58a9951acd688b9a2d17ff24ac9bcb199f84 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 3 Jul 2017 15:58:33 +0200 Subject: [PATCH 0052/1667] webp encoding --- src/Intervention/Image/AbstractEncoder.php | 13 +++++++++++++ src/Intervention/Image/Gd/Encoder.php | 17 +++++++++++++++++ src/Intervention/Image/Imagick/Encoder.php | 22 ++++++++++++++++++++++ tests/EncoderTest.php | 22 ++++++++++++++++++++++ tests/GdSystemTest.php | 13 +++++++++++++ 5 files changed, 87 insertions(+) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index 5e0cce2f3..0b56db55c 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -74,6 +74,13 @@ abstract protected function processBmp(); */ abstract protected function processIco(); + /** + * Processes and returns image as WebP encoded string + * + * @return string + */ + abstract protected function processWebp(); + /** * Process a given image * @@ -145,6 +152,12 @@ public function process(Image $image, $format = null, $quality = null) case 'image/vnd.adobe.photoshop': $this->result = $this->processPsd(); break; + + case 'webp': + case 'image/webp': + case 'image/x-webp': + $this->result = $this->processWebp(); + break; default: throw new \Intervention\Image\Exception\NotSupportedException( diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index 97a2c271e..a3fd2f127 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -55,6 +55,23 @@ protected function processGif() return $buffer; } + protected function processWebp() + { + if ( ! function_exists('imagewebp')) { + throw new \Intervention\Image\Exception\NotSupportedException( + "Webp format is not supported by PHP installation." + ); + } + + ob_start(); + imagewebp($this->image->getCore(), null, $this->quality); + $this->image->mime = defined('IMAGETYPE_WEBP') ? image_type_to_mime_type(IMAGETYPE_WEBP) : 'image/webp'; + $buffer = ob_get_contents(); + ob_end_clean(); + + return $buffer; + } + /** * Processes and returns encoded image as TIFF string * diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index 1c193237d..44452f247 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -66,6 +66,28 @@ protected function processGif() return $imagick->getImagesBlob(); } + protected function processWebp() + { + if ( ! \Imagick::queryFormats('WEBP')) { + throw new \Intervention\Image\Exception\NotSupportedException( + "Webp format is not supported by Imagick installation." + ); + } + + $format = 'webp'; + $compression = \Imagick::COMPRESSION_JPEG; + + $imagick = $this->image->getCore(); + $imagick = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_MERGE); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick->setImageCompressionQuality($this->quality); + + return $imagick->getImagesBlob(); + } + /** * Processes and returns encoded image as TIFF string * diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index 0b7eba8b3..4d80b374e 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -46,6 +46,18 @@ public function testProcessGifGd() $this->assertEquals('image/gif; charset=binary', $this->getMime($encoder->result)); } + public function testProcessWebpGd() + { + $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $encoder = new GdEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $image->shouldReceive('getCore')->once()->andReturn($core); + $image->shouldReceive('setEncoded')->once()->andReturn($image); + $img = $encoder->process($image, 'webp', 90); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); + } + /** * @expectedException \Intervention\Image\Exception\NotSupportedException */ @@ -155,6 +167,16 @@ public function testProcessGifImagick() $this->assertEquals('mock-gif', $encoder->result); } + /** + * @expectedException \Intervention\Image\Exception\NotSupportedException + */ + public function testProcessWebpImagick() + { + $encoder = new ImagickEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $img = $encoder->process($image, 'webp', 90); + } + public function testProcessTiffImagick() { $core = $this->getImagickMock('tiff'); diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index 3b107411e..af3db03f5 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -1474,6 +1474,13 @@ public function testEncodeGif() $this->assertInternalType('resource', imagecreatefromstring($img->encoded)); } + public function testEncodeWebp() + { + $img = $this->manager()->make('tests/images/trim.png'); + $data = (string) $img->encode('webp'); + $this->assertEquals('image/webp; charset=binary', $this->getMime($data)); + } + public function testEncodeDataUrl() { $img = $this->manager()->make('tests/images/trim.png'); @@ -1643,4 +1650,10 @@ private function manager() 'driver' => 'gd' )); } + + private function getMime($data) + { + $finfo = new finfo(FILEINFO_MIME); + return $finfo->buffer($data); + } } From baeeac5125b7015f3fad0f9333b5e33585430f38 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 3 Jul 2017 16:31:54 +0200 Subject: [PATCH 0053/1667] webp decoding --- src/Intervention/Image/Gd/Decoder.php | 36 ++++++++++++++++++-------- tests/GdSystemTest.php | 22 ++++++++++++++++ tests/images/test.webp | Bin 0 -> 82 bytes 3 files changed, 47 insertions(+), 11 deletions(-) create mode 100755 tests/images/test.webp diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index dbb9dce01..25f91a53c 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -14,40 +14,54 @@ class Decoder extends \Intervention\Image\AbstractDecoder */ public function initFromPath($path) { - $info = @getimagesize($path); - - if ($info === false) { + if ( ! file_exists($path)) { throw new \Intervention\Image\Exception\NotReadableException( - "Unable to read image from file ({$path})." + "Unable to find file ({$path})." ); } + // get mime type of file + $mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path); + // define core - switch ($info[2]) { - case IMAGETYPE_PNG: + switch (strtolower($mime)) { + case 'image/png': + case 'image/x-png': $core = @imagecreatefrompng($path); break; - case IMAGETYPE_JPEG: + case 'image/jpg': + case 'image/jpeg': + case 'image/pjpeg': $core = @imagecreatefromjpeg($path); if (!$core) { $core= @imagecreatefromstring(file_get_contents($path)); } break; - case IMAGETYPE_GIF: + case 'image/gif': $core = @imagecreatefromgif($path); break; + case 'image/webp': + case 'image/x-webp': + if ( ! function_exists('imagecreatefromwebp')) { + throw new \Intervention\Image\Exception\NotReadableException( + "Unsupported image type. GD/PHP installation does not support WebP format." + ); + } + $core = @imagecreatefromwebp($path); + break; + default: throw new \Intervention\Image\Exception\NotReadableException( - "Unable to read image type. GD driver is only able to decode JPG, PNG or GIF files." + "Unsupported image type. GD driver is only able to decode JPG, PNG, GIF or WebP files." ); } if (empty($core)) { throw new \Intervention\Image\Exception\NotReadableException( - "Unable to read image from file ({$path})." + "Unable to decode image from file ({$path})." ); } @@ -55,7 +69,7 @@ public function initFromPath($path) // build image $image = $this->initFromGdResource($core); - $image->mime = $info['mime']; + $image->mime = $mime; $image->setFileInfoFromPath($path); return $image; diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index af3db03f5..ecc8afe44 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -28,6 +28,14 @@ public function testMakeFromPathBroken() $this->manager()->make('tests/images/broken.png'); } + /** + * @expectedException \Intervention\Image\Exception\NotReadableException + */ + public function testMakeFromNotExisting() + { + $this->manager()->make('tests/images/not_existing.png'); + } + public function testMakeFromString() { $str = file_get_contents('tests/images/circle.png'); @@ -75,6 +83,20 @@ public function testMakeFromBase64() $this->assertEquals(10, $img->getHeight()); } + public function testMakeFromWebp() + { + $img = $this->manager()->make('tests/images/test.webp'); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertInternalType('resource', $img->getCore()); + $this->assertEquals(16, $img->getWidth()); + $this->assertEquals(16, $img->getHeight()); + $this->assertEquals('image/webp', $img->mime); + $this->assertEquals('tests/images', $img->dirname); + $this->assertEquals('test.webp', $img->basename); + $this->assertEquals('webp', $img->extension); + $this->assertEquals('test', $img->filename); + } + public function testCanvas() { $img = $this->manager()->canvas(30, 20); diff --git a/tests/images/test.webp b/tests/images/test.webp new file mode 100755 index 0000000000000000000000000000000000000000..ecd28246eacd3d168cc855baf289aab6a7effe30 GIT binary patch literal 82 zcmWIYbaV4!U| Date: Mon, 3 Jul 2017 17:04:25 +0200 Subject: [PATCH 0054/1667] check on webp functions before running tests --- tests/EncoderTest.php | 18 ++++++++++-------- tests/GdSystemTest.php | 30 +++++++++++++++++------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index 4d80b374e..7a22f98cc 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -48,14 +48,16 @@ public function testProcessGifGd() public function testProcessWebpGd() { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'webp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); + if (function_exists('imagewebp')) { + $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $encoder = new GdEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $image->shouldReceive('getCore')->once()->andReturn($core); + $image->shouldReceive('setEncoded')->once()->andReturn($image); + $img = $encoder->process($image, 'webp', 90); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); + } } /** diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index ecc8afe44..6c5c06052 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -85,16 +85,18 @@ public function testMakeFromBase64() public function testMakeFromWebp() { - $img = $this->manager()->make('tests/images/test.webp'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertEquals('image/webp', $img->mime); - $this->assertEquals('tests/images', $img->dirname); - $this->assertEquals('test.webp', $img->basename); - $this->assertEquals('webp', $img->extension); - $this->assertEquals('test', $img->filename); + if (function_exists('imagecreatefromwebp')) { + $img = $this->manager()->make('tests/images/test.webp'); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertInternalType('resource', $img->getCore()); + $this->assertEquals(16, $img->getWidth()); + $this->assertEquals(16, $img->getHeight()); + $this->assertEquals('image/webp', $img->mime); + $this->assertEquals('tests/images', $img->dirname); + $this->assertEquals('test.webp', $img->basename); + $this->assertEquals('webp', $img->extension); + $this->assertEquals('test', $img->filename); + } } public function testCanvas() @@ -1498,9 +1500,11 @@ public function testEncodeGif() public function testEncodeWebp() { - $img = $this->manager()->make('tests/images/trim.png'); - $data = (string) $img->encode('webp'); - $this->assertEquals('image/webp; charset=binary', $this->getMime($data)); + if (function_exists('imagewebp')) { + $img = $this->manager()->make('tests/images/trim.png'); + $data = (string) $img->encode('webp'); + $this->assertEquals('image/webp; charset=binary', $this->getMime($data)); + } } public function testEncodeDataUrl() From 322a4ade249467179c50a3e50eda8760ff3af2a3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 3 Jul 2017 17:50:40 +0200 Subject: [PATCH 0055/1667] fix --- src/Intervention/Image/Constraint.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/Constraint.php b/src/Intervention/Image/Constraint.php index a247c99c2..7352354d7 100644 --- a/src/Intervention/Image/Constraint.php +++ b/src/Intervention/Image/Constraint.php @@ -50,6 +50,7 @@ public function getSize() /** * Fix the given argument in current constraint + * * @param integer $type * @return void */ From 4581c28cdcb6067dca2b27ea8b76842ca79ddf90 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 4 Jul 2017 18:10:28 +0200 Subject: [PATCH 0056/1667] changed readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2e74d0a03..06b84e01e 100755 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Intervention Image is a **PHP image handling and manipulation** library providin [![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) [![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) -[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image) +[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) ## Requirements @@ -20,7 +20,7 @@ Intervention Image is a **PHP image handling and manipulation** library providin - [Installation](http://image.intervention.io/getting_started/installation) - [Laravel Framework Integration](http://image.intervention.io/getting_started/installation#laravel) -- [Official Documentation](http://image.intervention.io/) +- [Basic Usage](http://image.intervention.io/use/basics) ## Code Examples @@ -38,7 +38,7 @@ $img->insert('public/watermark.png'); $img->save('public/bar.jpg'); ``` -Refer to the [documentation](http://image.intervention.io/) to learn more about Intervention Image. +Refer to the [official documentation](http://image.intervention.io/) to learn more about Intervention Image. ## Contributing From ddac4491027c5703732136adc5988e84d7549a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Nabia=C5=82ek?= Date: Sat, 2 Sep 2017 21:24:22 +0200 Subject: [PATCH 0057/1667] Use short array syntax, remove trailing spaces --- src/Intervention/Image/AbstractColor.php | 8 +- src/Intervention/Image/AbstractDecoder.php | 8 +- .../Image/Commands/ChecksumCommand.php | 2 +- .../Image/Commands/IptcCommand.php | 4 +- .../Image/Commands/PsrResponseCommand.php | 4 +- src/Intervention/Image/Gd/Color.php | 6 +- .../Image/Gd/Commands/DestroyCommand.php | 2 +- .../Image/Gd/Commands/HeightenCommand.php | 2 +- .../Image/Gd/Commands/MaskCommand.php | 2 +- .../Image/Gd/Commands/SharpenCommand.php | 10 +- .../Image/Gd/Commands/TrimCommand.php | 4 +- .../Image/Gd/Commands/WidenCommand.php | 2 +- src/Intervention/Image/Gd/Decoder.php | 6 +- src/Intervention/Image/Gd/Font.php | 4 +- src/Intervention/Image/Image.php | 2 +- src/Intervention/Image/ImageManager.php | 8 +- src/Intervention/Image/ImageManagerStatic.php | 2 +- .../Image/ImageServiceProvider.php | 2 +- .../Image/ImageServiceProviderLaravel4.php | 8 +- .../Image/ImageServiceProviderLaravel5.php | 12 +- .../Image/ImageServiceProviderLeague.php | 2 +- src/Intervention/Image/Imagick/Color.php | 8 +- .../Image/Imagick/Commands/DestroyCommand.php | 2 +- .../Image/Imagick/Commands/FlipCommand.php | 2 +- .../Imagick/Commands/HeightenCommand.php | 2 +- .../Image/Imagick/Commands/TrimCommand.php | 8 +- .../Image/Imagick/Commands/WidenCommand.php | 2 +- .../Image/Imagick/Shapes/PolygonShape.php | 4 +- src/config/config.php | 4 +- tests/AbstractCommandTest.php | 4 +- tests/AbstractDecoderTest.php | 6 +- tests/AbstractDriverTest.php | 2 +- tests/AbstractFontTest.php | 4 +- tests/ArgumentTest.php | 138 +++++++++--------- tests/BackupCommandTest.php | 12 +- tests/BlurCommandTest.php | 4 +- tests/BrightnessCommandTest.php | 4 +- tests/ChecksumCommandTest.php | 6 +- tests/CircleCommandTest.php | 4 +- tests/ColorizeCommandTest.php | 6 +- tests/ConstraintTest.php | 2 +- tests/ContrastCommandTest.php | 4 +- tests/CropCommandTest.php | 4 +- tests/DestroyCommandTest.php | 12 +- tests/EllipseCommandTest.php | 4 +- tests/ExifCommandTest.php | 20 +-- tests/FillCommandTest.php | 12 +- tests/FitCommandTest.php | 32 ++-- tests/FlipCommandTest.php | 8 +- tests/GammaCommandTest.php | 4 +- tests/GdColorTest.php | 76 +++++----- tests/GdSystemTest.php | 18 +-- tests/GetsizeCommandTest.php | 4 +- tests/GreyscaleCommandTest.php | 4 +- tests/HeightenCommandTest.php | 8 +- tests/ImageManagerTest.php | 8 +- tests/ImageTest.php | 12 +- tests/ImagickColorTest.php | 80 +++++----- tests/ImagickSystemTest.php | 18 +-- tests/InsertCommandTest.php | 16 +- tests/InterlaceCommandTest.php | 4 +- tests/InvertCommandTest.php | 4 +- tests/IptcCommandTest.php | 10 +- tests/LimitColorsCommandTest.php | 8 +- tests/LineCommandTest.php | 4 +- tests/LineShapeTest.php | 2 +- tests/MaskCommandTest.php | 18 +-- tests/OpacityCommandTest.php | 6 +- tests/OrientateCommandTest.php | 16 +- tests/PickColorCommandTest.php | 8 +- tests/PixelCommandTest.php | 4 +- tests/PixelateCommandTest.php | 4 +- tests/PolygonCommandTest.php | 8 +- tests/PolygonShapeTest.php | 18 +-- tests/PsrResponseCommandTest.php | 2 +- tests/RectangleCommandTest.php | 4 +- tests/RectangleShapeTest.php | 2 +- tests/ResetCommandTest.php | 16 +- tests/ResizeCanvasCommandTest.php | 20 +-- tests/ResizeCommandTest.php | 8 +- tests/RotateCommandTest.php | 6 +- tests/SharpenCommandTest.php | 4 +- tests/SizeTest.php | 86 +++++------ tests/StreamCommandTest.php | 2 +- tests/TextCommandTest.php | 2 +- tests/TrimCommandTest.php | 6 +- tests/WidenCommandTest.php | 8 +- 87 files changed, 474 insertions(+), 474 deletions(-) diff --git a/src/Intervention/Image/AbstractColor.php b/src/Intervention/Image/AbstractColor.php index 592792058..0992d80c0 100644 --- a/src/Intervention/Image/AbstractColor.php +++ b/src/Intervention/Image/AbstractColor.php @@ -132,7 +132,7 @@ public function parse($value) break; case is_null($value): - $this->initFromArray(array(255, 255, 255, 0)); + $this->initFromArray([255, 255, 255, 0]); break; default: @@ -198,19 +198,19 @@ protected function rgbaFromString($value) $rgbaPattern = '/^rgba ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9.]{1,4})\)$/i'; if (preg_match($hexPattern, $value, $matches)) { - $result = array(); + $result = []; $result[0] = strlen($matches[1]) == '1' ? hexdec($matches[1].$matches[1]) : hexdec($matches[1]); $result[1] = strlen($matches[2]) == '1' ? hexdec($matches[2].$matches[2]) : hexdec($matches[2]); $result[2] = strlen($matches[3]) == '1' ? hexdec($matches[3].$matches[3]) : hexdec($matches[3]); $result[3] = 1; } elseif (preg_match($rgbPattern, $value, $matches)) { - $result = array(); + $result = []; $result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0; $result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0; $result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0; $result[3] = 1; } elseif (preg_match($rgbaPattern, $value, $matches)) { - $result = array(); + $result = []; $result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0; $result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0; $result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0; diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index ac93cf0b3..a717db9ff 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -65,13 +65,13 @@ public function __construct($data = null) public function initFromUrl($url) { - $options = array( - 'http' => array( + $options = [ + 'http' => [ 'method'=>"GET", 'header'=>"Accept-language: en\r\n". "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2\r\n" - ) - ); + ] + ]; $context = stream_context_create($options); diff --git a/src/Intervention/Image/Commands/ChecksumCommand.php b/src/Intervention/Image/Commands/ChecksumCommand.php index f8944bf18..9acc40308 100644 --- a/src/Intervention/Image/Commands/ChecksumCommand.php +++ b/src/Intervention/Image/Commands/ChecksumCommand.php @@ -12,7 +12,7 @@ class ChecksumCommand extends AbstractCommand */ public function execute($image) { - $colors = array(); + $colors = []; $size = $image->getSize(); diff --git a/src/Intervention/Image/Commands/IptcCommand.php b/src/Intervention/Image/Commands/IptcCommand.php index 43239ed3c..88e8fd35b 100644 --- a/src/Intervention/Image/Commands/IptcCommand.php +++ b/src/Intervention/Image/Commands/IptcCommand.php @@ -20,10 +20,10 @@ public function execute($image) $key = $this->argument(0)->value(); - $info = array(); + $info = []; @getimagesize($image->dirname .'/'. $image->basename, $info); - $data = array(); + $data = []; if (array_key_exists('APP13', $info)) { $iptc = iptcparse($info['APP13']); diff --git a/src/Intervention/Image/Commands/PsrResponseCommand.php b/src/Intervention/Image/Commands/PsrResponseCommand.php index ab47be10c..d75cd903b 100644 --- a/src/Intervention/Image/Commands/PsrResponseCommand.php +++ b/src/Intervention/Image/Commands/PsrResponseCommand.php @@ -33,10 +33,10 @@ public function execute($image) $this->setOutput(new Response( 200, - array( + [ 'Content-Type' => $mimetype, 'Content-Length' => strlen($image->getEncoded()) - ), + ], $stream )); diff --git a/src/Intervention/Image/Gd/Color.php b/src/Intervention/Image/Gd/Color.php index 5238831fa..e209dfbbd 100644 --- a/src/Intervention/Image/Gd/Color.php +++ b/src/Intervention/Image/Gd/Color.php @@ -167,7 +167,7 @@ public function getHex($prefix = '') */ public function getArray() { - return array($this->r, $this->g, $this->b, round(1 - $this->a / 127, 2)); + return [$this->r, $this->g, $this->b, round(1 - $this->a / 127, 2)]; } /** @@ -192,12 +192,12 @@ public function differs(AbstractColor $color, $tolerance = 0) $color_tolerance = round($tolerance * 2.55); $alpha_tolerance = round($tolerance * 1.27); - $delta = array( + $delta = [ 'r' => abs($color->r - $this->r), 'g' => abs($color->g - $this->g), 'b' => abs($color->b - $this->b), 'a' => abs($color->a - $this->a) - ); + ]; return ( $delta['r'] > $color_tolerance or diff --git a/src/Intervention/Image/Gd/Commands/DestroyCommand.php b/src/Intervention/Image/Gd/Commands/DestroyCommand.php index 403e8b801..18383307a 100644 --- a/src/Intervention/Image/Gd/Commands/DestroyCommand.php +++ b/src/Intervention/Image/Gd/Commands/DestroyCommand.php @@ -15,7 +15,7 @@ public function execute($image) // destroy image core imagedestroy($image->getCore()); - // destroy backups + // destroy backups foreach ($image->getBackups() as $backup) { imagedestroy($backup); } diff --git a/src/Intervention/Image/Gd/Commands/HeightenCommand.php b/src/Intervention/Image/Gd/Commands/HeightenCommand.php index 51e0abdf5..d31e9cde0 100644 --- a/src/Intervention/Image/Gd/Commands/HeightenCommand.php +++ b/src/Intervention/Image/Gd/Commands/HeightenCommand.php @@ -19,7 +19,7 @@ public function execute($image) $this->arguments[1] = $height; $this->arguments[2] = function ($constraint) use ($additionalConstraints) { $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) + if(is_callable($additionalConstraints)) $additionalConstraints($constraint); }; diff --git a/src/Intervention/Image/Gd/Commands/MaskCommand.php b/src/Intervention/Image/Gd/Commands/MaskCommand.php index 944f0449c..ef88d4dba 100644 --- a/src/Intervention/Image/Gd/Commands/MaskCommand.php +++ b/src/Intervention/Image/Gd/Commands/MaskCommand.php @@ -18,7 +18,7 @@ public function execute($image) $image_size = $image->getSize(); // create empty canvas - $canvas = $image->getDriver()->newImage($image_size->width, $image_size->height, array(0,0,0,0)); + $canvas = $image->getDriver()->newImage($image_size->width, $image_size->height, [0,0,0,0]); // build mask image from source $mask = $image->getDriver()->init($mask_source); diff --git a/src/Intervention/Image/Gd/Commands/SharpenCommand.php b/src/Intervention/Image/Gd/Commands/SharpenCommand.php index 9c1ca2063..4c0cc50f5 100644 --- a/src/Intervention/Image/Gd/Commands/SharpenCommand.php +++ b/src/Intervention/Image/Gd/Commands/SharpenCommand.php @@ -20,11 +20,11 @@ public function execute($image) $abs = ((4 * $min + 4 * $max) * -1) + 1; $div = 1; - $matrix = array( - array($min, $max, $min), - array($max, $abs, $max), - array($min, $max, $min) - ); + $matrix = [ + [$min, $max, $min], + [$max, $abs, $max], + [$min, $max, $min] + ]; // apply the matrix return imageconvolution($image->getCore(), $matrix, $div, 0); diff --git a/src/Intervention/Image/Gd/Commands/TrimCommand.php b/src/Intervention/Image/Gd/Commands/TrimCommand.php index 49a9ac6ea..2e3697527 100644 --- a/src/Intervention/Image/Gd/Commands/TrimCommand.php +++ b/src/Intervention/Image/Gd/Commands/TrimCommand.php @@ -27,9 +27,9 @@ public function execute($image) // define borders to trim away if (is_null($away)) { - $away = array('top', 'right', 'bottom', 'left'); + $away = ['top', 'right', 'bottom', 'left']; } elseif (is_string($away)) { - $away = array($away); + $away = [$away]; } // lower border names diff --git a/src/Intervention/Image/Gd/Commands/WidenCommand.php b/src/Intervention/Image/Gd/Commands/WidenCommand.php index c7d396f1b..43000d5dc 100644 --- a/src/Intervention/Image/Gd/Commands/WidenCommand.php +++ b/src/Intervention/Image/Gd/Commands/WidenCommand.php @@ -19,7 +19,7 @@ public function execute($image) $this->arguments[1] = null; $this->arguments[2] = function ($constraint) use ($additionalConstraints) { $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) + if(is_callable($additionalConstraints)) $additionalConstraints($constraint); }; diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 25f91a53c..4fe821e5e 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -33,9 +33,9 @@ public function initFromPath($path) case 'image/jpg': case 'image/jpeg': case 'image/pjpeg': - $core = @imagecreatefromjpeg($path); - if (!$core) { - $core= @imagecreatefromstring(file_get_contents($path)); + $core = @imagecreatefromjpeg($path); + if (!$core) { + $core= @imagecreatefromstring(file_get_contents($path)); } break; diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index df2d082d0..6e3a54577 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -26,7 +26,7 @@ private function getInternalFont() $internalfont = is_null($this->file) ? 1 : $this->file; $internalfont = is_numeric($internalfont) ? $internalfont : false; - if ( ! in_array($internalfont, array(1, 2, 3, 4, 5))) { + if ( ! in_array($internalfont, [1, 2, 3, 4, 5])) { throw new \Intervention\Image\Exception\NotSupportedException( sprintf('Internal GD font (%s) not available. Use only 1-5.', $internalfont) ); @@ -77,7 +77,7 @@ private function getInternalFontHeight() */ public function getBoxSize() { - $box = array(); + $box = []; if ($this->hasApplicableFontFile()) { diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index c1b4c2f77..c898362d1 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -72,7 +72,7 @@ class Image extends File * * @var array */ - protected $backups = array(); + protected $backups = []; /** * Last image encoding result diff --git a/src/Intervention/Image/ImageManager.php b/src/Intervention/Image/ImageManager.php index 205d17f04..aefb056c4 100644 --- a/src/Intervention/Image/ImageManager.php +++ b/src/Intervention/Image/ImageManager.php @@ -11,16 +11,16 @@ class ImageManager * * @var array */ - public $config = array( + public $config = [ 'driver' => 'gd' - ); + ]; /** * Creates new instance of Image Manager * * @param array $config */ - public function __construct(array $config = array()) + public function __construct(array $config = []) { $this->checkRequirements(); $this->configure($config); @@ -31,7 +31,7 @@ public function __construct(array $config = array()) * * @param array $config */ - public function configure(array $config = array()) + public function configure(array $config = []) { $this->config = array_replace($this->config, $config); diff --git a/src/Intervention/Image/ImageManagerStatic.php b/src/Intervention/Image/ImageManagerStatic.php index 3088bf55b..50bbd9b23 100644 --- a/src/Intervention/Image/ImageManagerStatic.php +++ b/src/Intervention/Image/ImageManagerStatic.php @@ -40,7 +40,7 @@ public static function getManager() * * @return ImageManager */ - public static function configure(array $config = array()) + public static function configure(array $config = []) { return self::$manager = self::getManager()->configure($config); } diff --git a/src/Intervention/Image/ImageServiceProvider.php b/src/Intervention/Image/ImageServiceProvider.php index e61e6a42e..1e6351df7 100644 --- a/src/Intervention/Image/ImageServiceProvider.php +++ b/src/Intervention/Image/ImageServiceProvider.php @@ -80,6 +80,6 @@ private function getProvider() */ public function provides() { - return array('image'); + return ['image']; } } diff --git a/src/Intervention/Image/ImageServiceProviderLaravel4.php b/src/Intervention/Image/ImageServiceProviderLaravel4.php index 6c73a207b..3b1388f25 100755 --- a/src/Intervention/Image/ImageServiceProviderLaravel4.php +++ b/src/Intervention/Image/ImageServiceProviderLaravel4.php @@ -32,7 +32,7 @@ public function boot() $config->set('imagecache::templates.original', null); // setup image manipulator route - $app['router']->get($config->get('imagecache::route').'/{template}/{filename}', array('as' => 'imagecache', function ($template, $filename) use ($app, $config) { + $app['router']->get($config->get('imagecache::route').'/{template}/{filename}', ['as' => 'imagecache', function ($template, $filename) use ($app, $config) { // disable session cookies for image route $app['config']->set('session.driver', 'array'); @@ -83,13 +83,13 @@ public function boot() $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $content); // return http response - return new IlluminateResponse($content, 200, array( + return new IlluminateResponse($content, 200, [ 'Content-Type' => $mime, 'Cache-Control' => 'max-age='.($config->get('imagecache::lifetime')*60).', public', 'Etag' => md5($content) - )); + ]); - }))->where(array('template' => join('|', array_keys($config->get('imagecache::templates'))), 'filename' => '[ \w\\.\\/\\-]+')); + }])->where(['template' => join('|', array_keys($config->get('imagecache::templates'))), 'filename' => '[ \w\\.\\/\\-]+']); } } } diff --git a/src/Intervention/Image/ImageServiceProviderLaravel5.php b/src/Intervention/Image/ImageServiceProviderLaravel5.php index 04cfc22ba..ea04fec5c 100644 --- a/src/Intervention/Image/ImageServiceProviderLaravel5.php +++ b/src/Intervention/Image/ImageServiceProviderLaravel5.php @@ -23,9 +23,9 @@ private function cacheIsInstalled() */ public function boot() { - $this->publishes(array( + $this->publishes([ __DIR__.'/../../config/config.php' => config_path('image.php') - )); + ]); // setup intervention/imagecache if package is installed $this->cacheIsInstalled() ? $this->bootstrapImageCache() : null; @@ -64,9 +64,9 @@ private function bootstrapImageCache() $app = $this->app; $config = __DIR__.'/../../../../imagecache/src/config/config.php'; - $this->publishes(array( + $this->publishes([ $config => config_path('imagecache.php') - )); + ]); // merge default config $this->mergeConfigFrom( @@ -80,10 +80,10 @@ private function bootstrapImageCache() $filename_pattern = '[ \w\\.\\/\\-\\@\(\)]+'; // route to access template applied image file - $app['router']->get(config('imagecache.route').'/{template}/{filename}', array( + $app['router']->get(config('imagecache.route').'/{template}/{filename}', [ 'uses' => 'Intervention\Image\ImageCacheController@getResponse', 'as' => 'imagecache' - ))->where(array('filename' => $filename_pattern)); + ])->where(['filename' => $filename_pattern]); } } } diff --git a/src/Intervention/Image/ImageServiceProviderLeague.php b/src/Intervention/Image/ImageServiceProviderLeague.php index 621ccd1fb..b756a61f5 100644 --- a/src/Intervention/Image/ImageServiceProviderLeague.php +++ b/src/Intervention/Image/ImageServiceProviderLeague.php @@ -23,7 +23,7 @@ class ImageServiceProviderLeague extends AbstractServiceProvider * * @param array $config */ - public function __construct($config = array()) + public function __construct($config = []) { $this->config = $config; } diff --git a/src/Intervention/Image/Imagick/Color.php b/src/Intervention/Image/Imagick/Color.php index 3940993ed..39c629ff8 100644 --- a/src/Intervention/Image/Imagick/Color.php +++ b/src/Intervention/Image/Imagick/Color.php @@ -148,12 +148,12 @@ public function getHex($prefix = '') */ public function getArray() { - return array( + return [ $this->getRedValue(), $this->getGreenValue(), $this->getBlueValue(), $this->getAlphaValue() - ); + ]; } /** @@ -183,12 +183,12 @@ public function differs(\Intervention\Image\AbstractColor $color, $tolerance = 0 $color_tolerance = round($tolerance * 2.55); $alpha_tolerance = round($tolerance); - $delta = array( + $delta = [ 'r' => abs($color->getRedValue() - $this->getRedValue()), 'g' => abs($color->getGreenValue() - $this->getGreenValue()), 'b' => abs($color->getBlueValue() - $this->getBlueValue()), 'a' => abs($color->getAlphaValue() - $this->getAlphaValue()) - ); + ]; return ( $delta['r'] > $color_tolerance or diff --git a/src/Intervention/Image/Imagick/Commands/DestroyCommand.php b/src/Intervention/Image/Imagick/Commands/DestroyCommand.php index 28b9a4c3a..d98062d8a 100644 --- a/src/Intervention/Image/Imagick/Commands/DestroyCommand.php +++ b/src/Intervention/Image/Imagick/Commands/DestroyCommand.php @@ -15,7 +15,7 @@ public function execute($image) // destroy image core $image->getCore()->clear(); - // destroy backups + // destroy backups foreach ($image->getBackups() as $backup) { $backup->clear(); } diff --git a/src/Intervention/Image/Imagick/Commands/FlipCommand.php b/src/Intervention/Image/Imagick/Commands/FlipCommand.php index 746650c1c..cdb03c52d 100644 --- a/src/Intervention/Image/Imagick/Commands/FlipCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FlipCommand.php @@ -14,7 +14,7 @@ public function execute($image) { $mode = $this->argument(0)->value('h'); - if (in_array(strtolower($mode), array(2, 'v', 'vert', 'vertical'))) { + if (in_array(strtolower($mode), [2, 'v', 'vert', 'vertical'])) { // flip vertical return $image->getCore()->flipImage(); } else { diff --git a/src/Intervention/Image/Imagick/Commands/HeightenCommand.php b/src/Intervention/Image/Imagick/Commands/HeightenCommand.php index 9d10973f2..0b61e50c1 100644 --- a/src/Intervention/Image/Imagick/Commands/HeightenCommand.php +++ b/src/Intervention/Image/Imagick/Commands/HeightenCommand.php @@ -19,7 +19,7 @@ public function execute($image) $this->arguments[1] = $height; $this->arguments[2] = function ($constraint) use ($additionalConstraints) { $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) + if(is_callable($additionalConstraints)) $additionalConstraints($constraint); }; diff --git a/src/Intervention/Image/Imagick/Commands/TrimCommand.php b/src/Intervention/Image/Imagick/Commands/TrimCommand.php index f2ee46af4..f09590320 100644 --- a/src/Intervention/Image/Imagick/Commands/TrimCommand.php +++ b/src/Intervention/Image/Imagick/Commands/TrimCommand.php @@ -26,9 +26,9 @@ public function execute($image) // define borders to trim away if (is_null($away)) { - $away = array('top', 'right', 'bottom', 'left'); + $away = ['top', 'right', 'bottom', 'left']; } elseif (is_string($away)) { - $away = array($away); + $away = [$away]; } // lower border names @@ -77,9 +77,9 @@ public function execute($image) // get coordinates of trim $imagePage = $trimed->getImagePage(); - list($crop_x, $crop_y) = array($imagePage['x']-1, $imagePage['y']-1); + list($crop_x, $crop_y) = [$imagePage['x']-1, $imagePage['y']-1]; // $trimed->setImagePage(0, 0, 0, 0); - list($crop_width, $crop_height) = array($trimed->width, $trimed->height); + list($crop_width, $crop_height) = [$trimed->width, $trimed->height]; // adjust settings if right should not be trimed if ( ! in_array('right', $away)) { diff --git a/src/Intervention/Image/Imagick/Commands/WidenCommand.php b/src/Intervention/Image/Imagick/Commands/WidenCommand.php index 467fe800a..a1967534c 100644 --- a/src/Intervention/Image/Imagick/Commands/WidenCommand.php +++ b/src/Intervention/Image/Imagick/Commands/WidenCommand.php @@ -19,7 +19,7 @@ public function execute($image) $this->arguments[1] = null; $this->arguments[2] = function ($constraint) use ($additionalConstraints) { $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) + if(is_callable($additionalConstraints)) $additionalConstraints($constraint); }; diff --git a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php index 1fa167d5f..5d1c01a28 100644 --- a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php +++ b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php @@ -62,13 +62,13 @@ public function applyToImage(Image $image, $x = 0, $y = 0) */ private function formatPoints($points) { - $ipoints = array(); + $ipoints = []; $count = 1; foreach ($points as $key => $value) { if ($count%2 === 0) { $y = $value; - $ipoints[] = array('x' => $x, 'y' => $y); + $ipoints[] = ['x' => $x, 'y' => $y]; } else { $x = $value; } diff --git a/src/config/config.php b/src/config/config.php index b106809e2..2b1d2c3e1 100644 --- a/src/config/config.php +++ b/src/config/config.php @@ -1,6 +1,6 @@ 'gd' -); +]; diff --git a/tests/AbstractCommandTest.php b/tests/AbstractCommandTest.php index cd6196008..a64897bdc 100644 --- a/tests/AbstractCommandTest.php +++ b/tests/AbstractCommandTest.php @@ -38,8 +38,8 @@ public function testSetOutput() public function getTestCommand() { - $arguments = array('foo', 'bar'); - $command = $this->getMockForAbstractClass('\Intervention\Image\Commands\AbstractCommand', array($arguments)); + $arguments = ['foo', 'bar']; + $command = $this->getMockForAbstractClass('\Intervention\Image\Commands\AbstractCommand', [$arguments]); return $command; } diff --git a/tests/AbstractDecoderTest.php b/tests/AbstractDecoderTest.php index 7db29f339..c728f39bf 100644 --- a/tests/AbstractDecoderTest.php +++ b/tests/AbstractDecoderTest.php @@ -40,7 +40,7 @@ public function testIsFilepath() $source = $this->getTestDecoder(null); $this->assertFalse($source->isFilepath()); - $source = $this->getTestDecoder(array()); + $source = $this->getTestDecoder([]); $this->assertFalse($source->isFilepath()); $source = $this->getTestDecoder(new stdClass); @@ -82,7 +82,7 @@ public function testIsBinary() $source = $this->getTestDecoder(0); $this->assertFalse($source->isBinary()); - $source = $this->getTestDecoder(array(1,2,3)); + $source = $this->getTestDecoder([1,2,3]); $this->assertFalse($source->isBinary()); $source = $this->getTestDecoder(new stdClass); @@ -146,6 +146,6 @@ public function testIsBase64() public function getTestDecoder($data) { - return $this->getMockForAbstractClass('\Intervention\Image\AbstractDecoder', array($data)); + return $this->getMockForAbstractClass('\Intervention\Image\AbstractDecoder', [$data]); } } diff --git a/tests/AbstractDriverTest.php b/tests/AbstractDriverTest.php index 7339e077c..f218d2d0b 100644 --- a/tests/AbstractDriverTest.php +++ b/tests/AbstractDriverTest.php @@ -14,6 +14,6 @@ public function testExecuteCommand() { $image = Mockery::mock('Intervention\Image\Image'); $driver = $this->getMockForAbstractClass('\Intervention\Image\AbstractDriver'); - $command = $driver->executeCommand($image, 'xxxxxxxxxxxxxxxxxxxxxxx', array()); + $command = $driver->executeCommand($image, 'xxxxxxxxxxxxxxxxxxxxxxx', []); } } diff --git a/tests/AbstractFontTest.php b/tests/AbstractFontTest.php index cf1d03b5a..eada70fd1 100644 --- a/tests/AbstractFontTest.php +++ b/tests/AbstractFontTest.php @@ -9,7 +9,7 @@ public function tearDown() public function testConstructor() { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont', array('test')); + $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont', ['test']); $this->assertEquals('test', $font->text); } @@ -66,7 +66,7 @@ public function testCountLines() { $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); $font->text('foo'.PHP_EOL.'bar'.PHP_EOL.'baz'); - $this->assertEquals(3, $font->countLines()); + $this->assertEquals(3, $font->countLines()); $font->text("foo\nbar\nbaz"); $this->assertEquals(3, $font->countLines()); $font->text('foo diff --git a/tests/ArgumentTest.php b/tests/ArgumentTest.php index 54e4d5d81..592a280ab 100644 --- a/tests/ArgumentTest.php +++ b/tests/ArgumentTest.php @@ -6,10 +6,10 @@ class ArgumentTest extends PHPUnit_Framework_TestCase { public function testConstructor() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $this->validateArgument($arg, 'foo'); - $arg = new Argument($this->getMockedCommand(array('foo', 'bar')), 1); + $arg = new Argument($this->getMockedCommand(['foo', 'bar']), 1); $this->validateArgument($arg, 'bar'); $arg = new Argument($this->getMockedCommand(), 0); @@ -18,15 +18,15 @@ public function testConstructor() public function testRequiredPass() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->required(); $this->validateArgument($arg, 'foo'); - $arg = new Argument($this->getMockedCommand(array(null))); + $arg = new Argument($this->getMockedCommand([null])); $arg->required(); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(0))); + $arg = new Argument($this->getMockedCommand([0])); $arg->required(); $this->validateArgument($arg, 0); } @@ -36,7 +36,7 @@ public function testRequiredPass() */ public function testRequiredFail() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->required(); } @@ -45,17 +45,17 @@ public function testRequiredFail() */ public function testRequiredFailSecondParameter() { - $arg = new Argument($this->getMockedCommand(array('foo')), 1); + $arg = new Argument($this->getMockedCommand(['foo']), 1); $arg->required(); } public function testTypeIntegerPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->type('integer'); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(123))); + $arg = new Argument($this->getMockedCommand([123])); $arg->type('integer'); $this->validateArgument($arg, 123); } @@ -65,19 +65,19 @@ public function testTypeIntegerPass() */ public function testTypeIntegerFail() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->type('integer'); } public function testTypeArrayPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->type('array'); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(array(1,2,3)))); + $arg = new Argument($this->getMockedCommand([[1,2,3]])); $arg->type('array'); - $this->validateArgument($arg, array(1,2,3)); + $this->validateArgument($arg, [1,2,3]); } /** @@ -85,41 +85,41 @@ public function testTypeArrayPass() */ public function testTypeArrayFail() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->type('array'); } public function testTypeDigitPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->type('digit'); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(0))); + $arg = new Argument($this->getMockedCommand([0])); $arg->type('digit'); $this->validateArgument($arg, 0); - $arg = new Argument($this->getMockedCommand(array(123))); + $arg = new Argument($this->getMockedCommand([123])); $arg->type('digit'); $this->validateArgument($arg, 123); - $arg = new Argument($this->getMockedCommand(array(5.0))); + $arg = new Argument($this->getMockedCommand([5.0])); $arg->type('digit'); $this->validateArgument($arg, 5.0); - $arg = new Argument($this->getMockedCommand(array(-10))); + $arg = new Argument($this->getMockedCommand([-10])); $arg->type('digit'); $this->validateArgument($arg, -10); - $arg = new Argument($this->getMockedCommand(array(-10.0))); + $arg = new Argument($this->getMockedCommand([-10.0])); $arg->type('digit'); $this->validateArgument($arg, -10.0); - $arg = new Argument($this->getMockedCommand(array('12'))); + $arg = new Argument($this->getMockedCommand(['12'])); $arg->type('digit'); $this->validateArgument($arg, '12'); - $arg = new Argument($this->getMockedCommand(array('12.0'))); + $arg = new Argument($this->getMockedCommand(['12.0'])); $arg->type('digit'); $this->validateArgument($arg, '12.0'); } @@ -129,7 +129,7 @@ public function testTypeDigitPass() */ public function testTypeDigitFailString() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->type('digit'); } @@ -138,7 +138,7 @@ public function testTypeDigitFailString() */ public function testTypeDigitFailFloat() { - $arg = new Argument($this->getMockedCommand(array(12.5))); + $arg = new Argument($this->getMockedCommand([12.5])); $arg->type('digit'); } @@ -147,17 +147,17 @@ public function testTypeDigitFailFloat() */ public function testTypeDigitFailBool() { - $arg = new Argument($this->getMockedCommand(array(true))); + $arg = new Argument($this->getMockedCommand([true])); $arg->type('digit'); } public function testTypeNumericPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->type('numeric'); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(12.3))); + $arg = new Argument($this->getMockedCommand([12.3])); $arg->type('numeric'); $this->validateArgument($arg, 12.3); } @@ -167,21 +167,21 @@ public function testTypeNumericPass() */ public function testTypeNumericFail() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->type('numeric'); } public function testTypeBooleanPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->type('boolean'); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(true))); + $arg = new Argument($this->getMockedCommand([true])); $arg->type('boolean'); $this->validateArgument($arg, true); - $arg = new Argument($this->getMockedCommand(array(false))); + $arg = new Argument($this->getMockedCommand([false])); $arg->type('boolean'); $this->validateArgument($arg, false); } @@ -191,17 +191,17 @@ public function testTypeBooleanPass() */ public function testTypeBooleanFail() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->type('boolean'); } public function testTypeStringPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->type('string'); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->type('string'); $this->validateArgument($arg, 'foo'); } @@ -211,18 +211,18 @@ public function testTypeStringPass() */ public function testTypeStringFail() { - $arg = new Argument($this->getMockedCommand(array(12))); + $arg = new Argument($this->getMockedCommand([12])); $arg->type('string'); } public function testTypeClosurePass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->type('closure'); $this->validateArgument($arg, null); $c = function ($foo) {}; - $arg = new Argument($this->getMockedCommand(array($c))); + $arg = new Argument($this->getMockedCommand([$c])); $arg->type('closure'); $this->validateArgument($arg, $c); } @@ -232,41 +232,41 @@ public function testTypeClosurePass() */ public function testTypeClosureFail() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->type('closure'); } public function testBetweenPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->between(0, 10); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(null))); + $arg = new Argument($this->getMockedCommand([null])); $arg->between(0, 10); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(4.5))); + $arg = new Argument($this->getMockedCommand([4.5])); $arg->between(0, 10); $this->validateArgument($arg, 4.5); - $arg = new Argument($this->getMockedCommand(array(4.5))); + $arg = new Argument($this->getMockedCommand([4.5])); $arg->between(10, 1); $this->validateArgument($arg, 4.5); - $arg = new Argument($this->getMockedCommand(array(0))); + $arg = new Argument($this->getMockedCommand([0])); $arg->between(0, 10); $this->validateArgument($arg, 0); - $arg = new Argument($this->getMockedCommand(array(10))); + $arg = new Argument($this->getMockedCommand([10])); $arg->between(0, 10); $this->validateArgument($arg, 10); - $arg = new Argument($this->getMockedCommand(array(0))); + $arg = new Argument($this->getMockedCommand([0])); $arg->between(-100, 100); $this->validateArgument($arg, 0); - $arg = new Argument($this->getMockedCommand(array(-100))); + $arg = new Argument($this->getMockedCommand([-100])); $arg->between(-100, 100); $this->validateArgument($arg, -100); } @@ -276,7 +276,7 @@ public function testBetweenPass() */ public function testBetweenFailString() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->between(1, 10); } @@ -285,7 +285,7 @@ public function testBetweenFailString() */ public function testBetweenFailAbove() { - $arg = new Argument($this->getMockedCommand(array(10.9))); + $arg = new Argument($this->getMockedCommand([10.9])); $arg->between(0, 10); } @@ -294,7 +294,7 @@ public function testBetweenFailAbove() */ public function testBetweenFailBelow() { - $arg = new Argument($this->getMockedCommand(array(-1))); + $arg = new Argument($this->getMockedCommand([-1])); $arg->between(0, 10); } @@ -303,33 +303,33 @@ public function testBetweenFailBelow() */ public function testBetweenFail() { - $arg = new Argument($this->getMockedCommand(array(-1000))); + $arg = new Argument($this->getMockedCommand([-1000])); $arg->between(-100, 100); } public function testMinPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->min(10); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(null))); + $arg = new Argument($this->getMockedCommand([null])); $arg->min(10); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(50))); + $arg = new Argument($this->getMockedCommand([50])); $arg->min(10); $this->validateArgument($arg, 50); - $arg = new Argument($this->getMockedCommand(array(50))); + $arg = new Argument($this->getMockedCommand([50])); $arg->min(50); $this->validateArgument($arg, 50); - $arg = new Argument($this->getMockedCommand(array(50))); + $arg = new Argument($this->getMockedCommand([50])); $arg->min(-10); $this->validateArgument($arg, 50); - $arg = new Argument($this->getMockedCommand(array(-10))); + $arg = new Argument($this->getMockedCommand([-10])); $arg->min(-10); $this->validateArgument($arg, -10); } @@ -339,7 +339,7 @@ public function testMinPass() */ public function testMinFailString() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->min(10); } @@ -348,33 +348,33 @@ public function testMinFailString() */ public function testMinFail() { - $arg = new Argument($this->getMockedCommand(array(10.9))); + $arg = new Argument($this->getMockedCommand([10.9])); $arg->min(11); } public function testMaxPass() { - $arg = new Argument($this->getMockedCommand(array())); + $arg = new Argument($this->getMockedCommand([])); $arg->max(100); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(null))); + $arg = new Argument($this->getMockedCommand([null])); $arg->max(100); $this->validateArgument($arg, null); - $arg = new Argument($this->getMockedCommand(array(50))); + $arg = new Argument($this->getMockedCommand([50])); $arg->max(100); $this->validateArgument($arg, 50); - $arg = new Argument($this->getMockedCommand(array(100))); + $arg = new Argument($this->getMockedCommand([100])); $arg->max(100); $this->validateArgument($arg, 100); - $arg = new Argument($this->getMockedCommand(array(-100))); + $arg = new Argument($this->getMockedCommand([-100])); $arg->max(-10); $this->validateArgument($arg, -100); - $arg = new Argument($this->getMockedCommand(array(-10))); + $arg = new Argument($this->getMockedCommand([-10])); $arg->max(-10); $this->validateArgument($arg, -10); } @@ -384,7 +384,7 @@ public function testMaxPass() */ public function testMaxFailString() { - $arg = new Argument($this->getMockedCommand(array('foo'))); + $arg = new Argument($this->getMockedCommand(['foo'])); $arg->max(10); } @@ -393,7 +393,7 @@ public function testMaxFailString() */ public function testMaxFail() { - $arg = new Argument($this->getMockedCommand(array(25))); + $arg = new Argument($this->getMockedCommand([25])); $arg->max(10); } @@ -403,7 +403,7 @@ public function testValueDefault() $value = $arg->value('foo'); $this->assertEquals('foo', $value); - $arg = new Argument($this->getMockedCommand(array(null))); + $arg = new Argument($this->getMockedCommand([null])); $value = $arg->value('foo'); $this->assertEquals('foo', $value); } @@ -414,8 +414,8 @@ private function validateArgument($argument, $value) $this->assertEquals($value, $argument->value()); } - private function getMockedCommand($arguments = array()) + private function getMockedCommand($arguments = []) { - return $this->getMockForAbstractClass('\Intervention\Image\Commands\AbstractCommand', array($arguments)); + return $this->getMockForAbstractClass('\Intervention\Image\Commands\AbstractCommand', [$arguments]); } } diff --git a/tests/BackupCommandTest.php b/tests/BackupCommandTest.php index 3f7967dda..f74c769d7 100644 --- a/tests/BackupCommandTest.php +++ b/tests/BackupCommandTest.php @@ -12,26 +12,26 @@ public function tearDown() public function testGdWithoutName() { - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('setBackup')->once(); - $command = new BackupGd(array()); + $command = new BackupGd([]); $result = $command->execute($image); $this->assertTrue($result); } public function testGdWithName() { - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('setBackup')->once(); - $command = new BackupGd(array('name' => 'fooBackup')); + $command = new BackupGd(['name' => 'fooBackup']); $result = $command->execute($image); $this->assertTrue($result); } @@ -42,7 +42,7 @@ public function testImagickWithoutName() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('setBackup')->once(); - $command = new BackupImagick(array()); + $command = new BackupImagick([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -53,7 +53,7 @@ public function testImagickWithName() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('setBackup')->once(); - $command = new BackupImagick(array('name' => 'fooBackup')); + $command = new BackupImagick(['name' => 'fooBackup']); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/BlurCommandTest.php b/tests/BlurCommandTest.php index 2380c3fce..52b935673 100644 --- a/tests/BlurCommandTest.php +++ b/tests/BlurCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new BlurGd(array(2)); + $command = new BlurGd([2]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('blurimage')->with(2, 1)->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new BlurImagick(array(2)); + $command = new BlurImagick([2]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/BrightnessCommandTest.php b/tests/BrightnessCommandTest.php index 999a966c8..7a7cb8a93 100644 --- a/tests/BrightnessCommandTest.php +++ b/tests/BrightnessCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new BrightnessGd(array(12)); + $command = new BrightnessGd([12]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('modulateimage')->with(112, 100, 100)->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new BrightnessImagick(array(12)); + $command = new BrightnessImagick([12]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/ChecksumCommandTest.php b/tests/ChecksumCommandTest.php index cf42f7ddf..7f7cfbab2 100644 --- a/tests/ChecksumCommandTest.php +++ b/tests/ChecksumCommandTest.php @@ -11,13 +11,13 @@ public function tearDown() public function testExecute() { - $size = Mockery::mock('Intervention\Image\Size', array(3, 3)); - $color = array(0,0,0,1); + $size = Mockery::mock('Intervention\Image\Size', [3, 3]); + $color = [0,0,0,1]; $resource = imagecreatefrompng(__DIR__.'/images/tile.png'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('pickColor')->times(9)->andReturn($color); - $command = new ChecksumCommand(array()); + $command = new ChecksumCommand([]); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); diff --git a/tests/CircleCommandTest.php b/tests/CircleCommandTest.php index 241440ec4..4589430bb 100644 --- a/tests/CircleCommandTest.php +++ b/tests/CircleCommandTest.php @@ -17,7 +17,7 @@ public function testGd() $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new CircleCommand(array(250, 10, 20)); + $command = new CircleCommand([250, 10, 20]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); @@ -33,7 +33,7 @@ public function testImagick() $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new CircleCommand(array(25, 10, 20)); + $command = new CircleCommand([25, 10, 20]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); diff --git a/tests/ColorizeCommandTest.php b/tests/ColorizeCommandTest.php index c89d0d4a9..6dc7c190e 100644 --- a/tests/ColorizeCommandTest.php +++ b/tests/ColorizeCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new ColorizeGd(array(20, 0, -40)); + $command = new ColorizeGd([20, 0, -40]); $result = $command->execute($image); $this->assertTrue($result); } @@ -23,13 +23,13 @@ public function testGd() public function testImagick() { $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getquantumrange')->with()->once()->andReturn(array('quantumRangeLong' => 42)); + $imagick->shouldReceive('getquantumrange')->with()->once()->andReturn(['quantumRangeLong' => 42]); $imagick->shouldReceive('levelimage')->with(0, 4, 42, \Imagick::CHANNEL_RED)->once()->andReturn(true); $imagick->shouldReceive('levelimage')->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->once()->andReturn(true); $imagick->shouldReceive('levelimage')->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->once()->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->times(4)->andReturn($imagick); - $command = new ColorizeImagick(array(20, 0, -40)); + $command = new ColorizeImagick([20, 0, -40]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/ConstraintTest.php b/tests/ConstraintTest.php index 6aec91ea0..b798757bb 100644 --- a/tests/ConstraintTest.php +++ b/tests/ConstraintTest.php @@ -48,7 +48,7 @@ public function testSetAspectratioAndUpsize() private function getMockedSize($width, $height) { - $size = Mockery::mock('\Intervention\Image\Size', array($width, $height)); + $size = Mockery::mock('\Intervention\Image\Size', [$width, $height]); $size->shouldReceive('getWidth')->andReturn($width); $size->shouldReceive('getHeight')->andReturn($height); $size->shouldReceive('getRatio')->andReturn($width/$height); diff --git a/tests/ContrastCommandTest.php b/tests/ContrastCommandTest.php index 2c18f0866..e3f7901cb 100644 --- a/tests/ContrastCommandTest.php +++ b/tests/ContrastCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new ContrastGd(array(20)); + $command = new ContrastGd([20]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('sigmoidalcontrastimage')->with(true, 5, 0)->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new ContrastImagick(array(20)); + $command = new ContrastImagick([20]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/CropCommandTest.php b/tests/CropCommandTest.php index 94cc5c04f..b59a0af96 100644 --- a/tests/CropCommandTest.php +++ b/tests/CropCommandTest.php @@ -16,7 +16,7 @@ public function testGd() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $command = new CropGd(array(100, 150, 10, 20)); + $command = new CropGd([100, 150, 10, 20]); $result = $command->execute($image); $this->assertTrue($result); } @@ -28,7 +28,7 @@ public function testImagick() $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->times(2)->andReturn($imagick); - $command = new CropImagick(array(100, 150, 10, 20)); + $command = new CropImagick([100, 150, 10, 20]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/DestroyCommandTest.php b/tests/DestroyCommandTest.php index f25719644..35ae8ac62 100644 --- a/tests/DestroyCommandTest.php +++ b/tests/DestroyCommandTest.php @@ -13,15 +13,15 @@ public function tearDown() public function testGd() { $resource = imagecreatefrompng(__DIR__.'/images/tile.png'); - $backups = array( - imagecreatefrompng(__DIR__.'/images/tile.png'), + $backups = [ + imagecreatefrompng(__DIR__.'/images/tile.png'), imagecreatefrompng(__DIR__.'/images/tile.png') - ); + ]; $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('getBackups')->once()->andReturn($backups); - $command = new DestroyGd(array()); + $command = new DestroyGd([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -33,12 +33,12 @@ public function testImagick() $backup = Mockery::mock('Imagick'); $backup->shouldReceive('clear')->with()->andReturn(true); - $backups = array($backup); + $backups = [$backup]; $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('getBackups')->once()->andReturn($backups); - $command = new DestroyImagick(array()); + $command = new DestroyImagick([]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/EllipseCommandTest.php b/tests/EllipseCommandTest.php index 8ac42e007..9bb1d1d6a 100644 --- a/tests/EllipseCommandTest.php +++ b/tests/EllipseCommandTest.php @@ -17,7 +17,7 @@ public function testGd() $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new EllipseCommand(array(250, 150, 10, 20)); + $command = new EllipseCommand([250, 150, 10, 20]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); @@ -33,7 +33,7 @@ public function testImagick() $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new EllipseCommand(array(250, 150, 10, 20)); + $command = new EllipseCommand([250, 150, 10, 20]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php index 7e7cd31df..de69f45ed 100644 --- a/tests/ExifCommandTest.php +++ b/tests/ExifCommandTest.php @@ -15,7 +15,7 @@ public function testFetchAll() $image = new Image; $image->dirname = __DIR__.'/images'; $image->basename = 'exif.jpg'; - $command = new ExifCommand(array()); + $command = new ExifCommand([]); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -27,7 +27,7 @@ public function testFetchDefined() $image = new Image; $image->dirname = __DIR__.'/images'; $image->basename = 'exif.jpg'; - $command = new ExifCommand(array('Artist')); + $command = new ExifCommand(['Artist']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -39,7 +39,7 @@ public function testFetchNonExisting() $image = new Image; $image->dirname = __DIR__.'/images'; $image->basename = 'exif.jpg'; - $command = new ExifCommand(array('xxx')); + $command = new ExifCommand(['xxx']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -51,7 +51,7 @@ public function testFetchFromPng() $image = new Image; $image->dirname = __DIR__.'/images'; $image->basename = 'star.png'; - $command = new ExifCommand(array('Orientation')); + $command = new ExifCommand(['Orientation']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -61,7 +61,7 @@ public function testFetchFromPng() public function testImagickFetchAll() { $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array()); + $command = new \Intervention\Image\Imagick\Commands\ExifCommand([]); $command->dontPreferExtension(); $result = $command->execute($image); $this->assertTrue($result); @@ -73,7 +73,7 @@ public function testImagickFetchAll() public function testImagickFetchDefined() { $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('Artist')); + $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['Artist']); $command->dontPreferExtension(); $result = $command->execute($image); $this->assertTrue($result); @@ -84,7 +84,7 @@ public function testImagickFetchDefined() public function testImagickNonExisting() { $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('xx')); + $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['xx']); $command->dontPreferExtension(); $result = $command->execute($image); $this->assertTrue($result); @@ -95,7 +95,7 @@ public function testImagickNonExisting() public function testImagickFallbackToExifExtenstion() { $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(array('Artist')); + $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['Artist']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -104,8 +104,8 @@ public function testImagickFallbackToExifExtenstion() private function imagick() { - return new \Intervention\Image\ImageManager(array( + return new \Intervention\Image\ImageManager([ 'driver' => 'imagick' - )); + ]); } } diff --git a/tests/FillCommandTest.php b/tests/FillCommandTest.php index b4d92c9fa..acd0b8c31 100644 --- a/tests/FillCommandTest.php +++ b/tests/FillCommandTest.php @@ -17,7 +17,7 @@ public function testGdFill() $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd(array('666666')); + $command = new FillGd(['666666']); $result = $command->execute($image); $this->assertTrue($result); } @@ -29,7 +29,7 @@ public function testGdFillArray() $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd(array(array(50, 50, 50))); + $command = new FillGd([[50, 50, 50]]); $result = $command->execute($image); $this->assertTrue($result); } @@ -41,7 +41,7 @@ public function testGdFillArrayWithAlpha() $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd(array(array(50, 50, 50, .50))); + $command = new FillGd([[50, 50, 50, .50]]); $result = $command->execute($image); $this->assertTrue($result); } @@ -57,7 +57,7 @@ public function testGdFillWithCoordinates() $image->shouldReceive('getHeight')->once()->andReturn(600); $image->shouldReceive('setCore')->once(); $driver->shouldReceive('newImage')->with(800, 600)->once()->andReturn($image); - $command = new FillGd(array('#666666', 0, 0)); + $command = new FillGd(['#666666', 0, 0]); $result = $command->execute($image); $this->assertTrue($result); } @@ -70,7 +70,7 @@ public function testImagickFill() $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); $image->shouldReceive('getCore')->andReturn($imagick); - $command = new FillImagick(array('666666')); + $command = new FillImagick(['666666']); $result = $command->execute($image); $this->assertTrue($result); } @@ -85,7 +85,7 @@ public function testImagickFillWithCoordinates() $image->shouldReceive('getCore')->andReturn($imagick); $image->shouldReceive('getWidth')->andReturn(800); $image->shouldReceive('getHeight')->andReturn(600); - $command = new FillImagick(array('666666', 0, 0)); + $command = new FillImagick(['666666', 0, 0]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/FitCommandTest.php b/tests/FitCommandTest.php index bdc4c8081..0465adc31 100644 --- a/tests/FitCommandTest.php +++ b/tests/FitCommandTest.php @@ -12,50 +12,50 @@ public function tearDown() public function testGdFit() { - $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); + $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); $cropped_size->shouldReceive('getWidth')->times(2)->andReturn(800); $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); - $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); + $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($original_size); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $command = new FitGd(array(200, 100)); + $command = new FitGd([200, 100]); $result = $command->execute($image); $this->assertTrue($result); } public function testGdFitWithPosition() { - $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); + $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); $cropped_size->shouldReceive('getWidth')->times(2)->andReturn(800); $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); - $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); + $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($original_size); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $command = new FitGd(array(200, 100, null, 'top-left')); + $command = new FitGd([200, 100, null, 'top-left']); $result = $command->execute($image); $this->assertTrue($result); } public function testImagickFit() { - $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); + $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); $cropped_size->shouldReceive('getWidth')->once()->andReturn(200); $cropped_size->shouldReceive('getHeight')->once()->andReturn(100); $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); - $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); + $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); $imagick = Mockery::mock('Imagick'); $imagick->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); @@ -64,19 +64,19 @@ public function testImagickFit() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($original_size); $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new FitImagick(array(200, 100)); + $command = new FitImagick([200, 100]); $result = $command->execute($image); $this->assertTrue($result); } public function testImagickFitWithPosition() { - $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); + $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); $cropped_size->shouldReceive('getWidth')->once()->andReturn(200); $cropped_size->shouldReceive('getHeight')->once()->andReturn(100); $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); - $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); + $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); $imagick = Mockery::mock('Imagick'); $imagick->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); @@ -85,7 +85,7 @@ public function testImagickFitWithPosition() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($original_size); $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new FitImagick(array(200, 100, null, 'top-left')); + $command = new FitImagick([200, 100, null, 'top-left']); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/FlipCommandTest.php b/tests/FlipCommandTest.php index a38070da5..4ff0592f5 100644 --- a/tests/FlipCommandTest.php +++ b/tests/FlipCommandTest.php @@ -12,13 +12,13 @@ public function tearDown() public function testGd() { - $size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $command = new FlipGd(array('h')); + $command = new FlipGd(['h']); $result = $command->execute($image); $this->assertTrue($result); } @@ -29,7 +29,7 @@ public function testImagick() $imagick->shouldReceive('flopimage')->with()->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new FlipImagick(array('h')); + $command = new FlipImagick(['h']); $result = $command->execute($image); $this->assertTrue($result); @@ -37,7 +37,7 @@ public function testImagick() $imagick->shouldReceive('flipimage')->with()->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new FlipImagick(array('v')); + $command = new FlipImagick(['v']); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/GammaCommandTest.php b/tests/GammaCommandTest.php index 48d6bb9f6..115721a3a 100644 --- a/tests/GammaCommandTest.php +++ b/tests/GammaCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new GammaGd(array(1.4)); + $command = new GammaGd([1.4]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('gammaimage')->with(1.4)->once()->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GammaImagick(array(1.4)); + $command = new GammaImagick([1.4]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/GdColorTest.php b/tests/GdColorTest.php index 51d96c851..d28b7a640 100644 --- a/tests/GdColorTest.php +++ b/tests/GdColorTest.php @@ -29,7 +29,7 @@ public function testParseInteger() public function testParseArray() { $c = new Color; - $c->parse(array(181, 55, 23, 0.5)); + $c->parse([181, 55, 23, 0.5]); $this->validateColor($c, 181, 55, 23, 64); } @@ -65,23 +65,23 @@ public function testInitFromInteger() public function testInitFromArray() { $c = new Color; - $c->initFromArray(array(0, 0, 0, 0)); + $c->initFromArray([0, 0, 0, 0]); $this->validateColor($c, 0, 0, 0, 127); - $c->initFromArray(array(0, 0, 0, 1)); + $c->initFromArray([0, 0, 0, 1]); $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray(array(255, 255, 255, 1)); + $c->initFromArray([255, 255, 255, 1]); $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray(array(255, 255, 255, 0)); + $c->initFromArray([255, 255, 255, 0]); $this->validateColor($c, 255, 255, 255, 127); - $c->initFromArray(array(255, 255, 255, 0.5)); + $c->initFromArray([255, 255, 255, 0.5]); $this->validateColor($c, 255, 255, 255, 64); - $c->initFromArray(array(0, 0, 0)); + $c->initFromArray([0, 0, 0]); $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray(array(255, 255, 255)); + $c->initFromArray([255, 255, 255]); $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray(array(181, 55, 23)); + $c->initFromArray([181, 55, 23]); $this->validateColor($c, 181, 55, 23, 0); - $c->initFromArray(array(181, 55, 23, 0.5)); + $c->initFromArray([181, 55, 23, 0.5]); $this->validateColor($c, 181, 55, 23, 64); } @@ -154,27 +154,27 @@ public function testGetInt() $this->assertInternalType('int', $i); $this->assertEquals(2147483647, $i); - $c = new Color(array(255, 255, 255)); + $c = new Color([255, 255, 255]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 16777215); - $c = new Color(array(255, 255, 255, 1)); + $c = new Color([255, 255, 255, 1]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 16777215); - $c = new Color(array(181, 55, 23, 0.5)); + $c = new Color([181, 55, 23, 0.5]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 1085617943); - $c = new Color(array(181, 55, 23, 1)); + $c = new Color([181, 55, 23, 1]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 11876119); - $c = new Color(array(0, 0, 0, 0)); + $c = new Color([0, 0, 0, 0]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 2130706432); @@ -187,17 +187,17 @@ public function testGetHex() $this->assertInternalType('string', $i); $this->assertEquals($i, 'ffffff'); - $c = new Color(array(255, 255, 255, 1)); + $c = new Color([255, 255, 255, 1]); $i = $c->getHex(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'ffffff'); - $c = new Color(array(181, 55, 23, 0.5)); + $c = new Color([181, 55, 23, 0.5]); $i = $c->getHex(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'b53717'); - $c = new Color(array(0, 0, 0, 0)); + $c = new Color([0, 0, 0, 0]); $i = $c->getHex('#'); $this->assertInternalType('string', $i); $this->assertEquals($i, '#000000'); @@ -208,22 +208,22 @@ public function testGetArray() $c = new Color; $i = $c->getArray(); $this->assertInternalType('array', $i); - $this->assertEquals($i, array(255, 255, 255, 0)); + $this->assertEquals($i, [255, 255, 255, 0]); - $c = new Color(array(255, 255, 255, 1)); + $c = new Color([255, 255, 255, 1]); $i = $c->getArray(); $this->assertInternalType('array', $i); - $this->assertEquals($i, array(255, 255, 255, 1)); + $this->assertEquals($i, [255, 255, 255, 1]); - $c = new Color(array(181, 55, 23, 0.5)); + $c = new Color([181, 55, 23, 0.5]); $i = $c->getArray(); $this->assertInternalType('array', $i); - $this->assertEquals($i, array(181, 55, 23, 0.5)); + $this->assertEquals($i, [181, 55, 23, 0.5]); - $c = new Color(array(0, 0, 0, 1)); + $c = new Color([0, 0, 0, 1]); $i = $c->getArray(); $this->assertInternalType('array', $i); - $this->assertEquals($i, array(0, 0, 0, 1)); + $this->assertEquals($i, [0, 0, 0, 1]); } public function testGetRgba() @@ -233,17 +233,17 @@ public function testGetRgba() $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(255, 255, 255, 0.00)'); - $c = new Color(array(255, 255, 255, 1)); + $c = new Color([255, 255, 255, 1]); $i = $c->getRgba(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(255, 255, 255, 1.00)'); - $c = new Color(array(181, 55, 23, 0.5)); + $c = new Color([181, 55, 23, 0.5]); $i = $c->getRgba(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(181, 55, 23, 0.50)'); - $c = new Color(array(0, 0, 0, 1)); + $c = new Color([0, 0, 0, 1]); $i = $c->getRgba(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(0, 0, 0, 1.00)'); @@ -251,24 +251,24 @@ public function testGetRgba() public function testDiffers() { - $c1 = new Color(array(0, 0, 0)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([0, 0, 0]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(false, $c1->differs($c2)); - $c1 = new Color(array(1, 0, 0)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([1, 0, 0]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(true, $c1->differs($c2)); - $c1 = new Color(array(1, 0, 0)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([1, 0, 0]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(false, $c1->differs($c2, 10)); - $c1 = new Color(array(127, 127, 127)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([127, 127, 127]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(true, $c1->differs($c2, 49)); - $c1 = new Color(array(127, 127, 127)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([127, 127, 127]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(false, $c1->differs($c2, 50)); } diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index 6c5c06052..7243b8847 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -1044,9 +1044,9 @@ public function testFillImageWithInterventionImage() public function testPixelImage() { $img = $this->manager()->make('tests/images/tile.png'); - $coords = array(array(5, 5), array(12, 12)); + $coords = [[5, 5], [12, 12]]; $img = $img->pixel('fdf5e4', $coords[0][0], $coords[0][1]); - $img = $img->pixel(array(255, 255, 255), $coords[1][0], $coords[1][1]); + $img = $img->pixel([255, 255, 255], $coords[1][0], $coords[1][1]); $this->assertEquals('#fdf5e4', $img->pickColor($coords[0][0], $coords[0][1], 'hex')); $this->assertEquals('#ffffff', $img->pickColor($coords[1][0], $coords[1][1], 'hex')); } @@ -1110,7 +1110,7 @@ public function testCircleImage() public function testPolygonImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); - $points = array(3, 3, 11, 11, 7, 13); + $points = [3, 3, 11, 11, 7, 13]; $img->polygon($points, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); $this->assertEquals('e534ff90c8026f9317b99071fda01ed4', $img->checksum()); } @@ -1378,7 +1378,7 @@ public function testTrimGradient() public function testTrimOnlyLeftAndRight() { $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, array('left', 'right'), 60); + $img->trim(null, ['left', 'right'], 60); $this->assertEquals($img->getWidth(), 20); $this->assertEquals($img->getHeight(), 50); } @@ -1386,7 +1386,7 @@ public function testTrimOnlyLeftAndRight() public function testTrimOnlyTopAndBottom() { $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, array('top', 'bottom'), 60); + $img->trim(null, ['top', 'bottom'], 60); $this->assertEquals($img->getWidth(), 50); $this->assertEquals($img->getHeight(), 20); } @@ -1444,14 +1444,14 @@ public function testTrimWithFeather() // trim only left and right with feather $img = clone $canvas; $feather = 10; - $img->trim(null, array('left', 'right'), null, $feather); + $img->trim(null, ['left', 'right'], null, $feather); $this->assertEquals($img->getWidth(), 28 + $feather * 2); $this->assertEquals($img->getHeight(), 50); // trim only top and bottom with feather $img = clone $canvas; $feather = 10; - $img->trim(null, array('top', 'bottom'), null, $feather); + $img->trim(null, ['top', 'bottom'], null, $feather); $this->assertEquals($img->getWidth(), 50); $this->assertEquals($img->getHeight(), 28 + $feather * 2); @@ -1672,9 +1672,9 @@ private function assertTransparentPosition($img, $x, $y, $transparent = 0) private function manager() { - return new \Intervention\Image\ImageManager(array( + return new \Intervention\Image\ImageManager([ 'driver' => 'gd' - )); + ]); } private function getMime($data) diff --git a/tests/GetsizeCommandTest.php b/tests/GetsizeCommandTest.php index 76072b487..2a7fde515 100644 --- a/tests/GetsizeCommandTest.php +++ b/tests/GetsizeCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new GetSizeGd(array()); + $command = new GetSizeGd([]); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -29,7 +29,7 @@ public function testImagick() $imagick->shouldReceive('getimageheight')->with(); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GetSizeImagick(array()); + $command = new GetSizeImagick([]); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); diff --git a/tests/GreyscaleCommandTest.php b/tests/GreyscaleCommandTest.php index 1fa8d3a10..fbe9be09b 100644 --- a/tests/GreyscaleCommandTest.php +++ b/tests/GreyscaleCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new GreyscaleGd(array()); + $command = new GreyscaleGd([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('modulateimage')->with(100, 0, 100)->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GreyscaleImagick(array()); + $command = new GreyscaleImagick([]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/HeightenCommandTest.php b/tests/HeightenCommandTest.php index 7290f4274..5b1f112f8 100644 --- a/tests/HeightenCommandTest.php +++ b/tests/HeightenCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $callback = function ($constraint) { $constraint->aspectRatio(); }; $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $size->shouldReceive('resize')->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(800); $size->shouldReceive('getHeight')->once()->andReturn(600); @@ -24,7 +24,7 @@ public function testGd() $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $command = new HeightenGd(array(200)); + $command = new HeightenGd([200]); $result = $command->execute($image); $this->assertTrue($result); } @@ -34,14 +34,14 @@ public function testImagick() $callback = function ($constraint) { $constraint->upsize(); }; $imagick = Mockery::mock('Imagick'); $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $size->shouldReceive('resize')->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(300); $size->shouldReceive('getHeight')->once()->andReturn(200); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new HeightenImagick(array(200)); + $command = new HeightenImagick([200]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index e23d91278..496c3f27b 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -11,7 +11,7 @@ public function tearDown() public function testConstructor() { - $config = array('driver' => 'foo', 'bar' => 'baz'); + $config = ['driver' => 'foo', 'bar' => 'baz']; $manager = new ImageManager($config); $this->assertEquals('foo', $manager->config['driver']); $this->assertEquals('baz', $manager->config['bar']); @@ -19,8 +19,8 @@ public function testConstructor() public function testConfigure() { - $overwrite = array('driver' => 'none', 'bar' => 'none'); - $config = array('driver' => 'foo', 'bar' => 'baz'); + $overwrite = ['driver' => 'none', 'bar' => 'none']; + $config = ['driver' => 'foo', 'bar' => 'baz']; $manager = new ImageManager($overwrite); $manager->configure($config); $this->assertEquals('foo', $manager->config['driver']); @@ -29,7 +29,7 @@ public function testConfigure() public function testConfigureObject() { - $config = array('driver' => new Intervention\Image\Imagick\Driver()); + $config = ['driver' => new Intervention\Image\Imagick\Driver()]; $manager = new ImageManager($config); $image = $manager->make('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); diff --git a/tests/ImageTest.php b/tests/ImageTest.php index 245d4df9a..23f5bb9a3 100644 --- a/tests/ImageTest.php +++ b/tests/ImageTest.php @@ -19,7 +19,7 @@ public function testCommandCall() { $image = $this->getTestImage(); $result = $image->test(1, 2, 3); - $this->assertEquals('mock', $result); + $this->assertEquals('mock', $result); } public function testEncode() @@ -54,7 +54,7 @@ public function testIsEncoded() public function testFilter() { - $demoFilter = Mockery::mock('\Intervention\Image\Filters\DemoFilter', array(15)); + $demoFilter = Mockery::mock('\Intervention\Image\Filters\DemoFilter', [15]); $image = $this->getTestImage(); $demoFilter->shouldReceive('applyFilter')->with($image)->once()->andReturn($image); $image->filter($demoFilter); @@ -87,26 +87,26 @@ public function testGetBackups() { $image = $this->getTestImage(); $backups = $image->getBackups(); - $this->assertEquals(array(), $backups); + $this->assertEquals([], $backups); $image = $this->getTestImage(); $image->setBackup('foo'); $image->setBackup('bar'); $image->setBackup('baz'); $backups = $image->getBackups(); - $this->assertEquals(array('default' => 'baz'), $backups); + $this->assertEquals(['default' => 'baz'], $backups); $image = $this->getTestImage(); $image->setBackup('foo', 'a'); $image->setBackup('bar', 'b'); $image->setBackup('baz', 'c'); $backups = $image->getBackups(); - $this->assertEquals(array('a' => 'foo', 'b' => 'bar', 'c' => 'baz'), $backups); + $this->assertEquals(['a' => 'foo', 'b' => 'bar', 'c' => 'baz'], $backups); } private function getTestImage() { - $size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $driver = Mockery::mock('\Intervention\Image\AbstractDriver'); $command = Mockery::mock('\Intervention\Image\Commands\AbstractCommand'); $command->shouldReceive('hasOutput')->andReturn(true); diff --git a/tests/ImagickColorTest.php b/tests/ImagickColorTest.php index 6a9df3e68..fccbc00cc 100644 --- a/tests/ImagickColorTest.php +++ b/tests/ImagickColorTest.php @@ -64,7 +64,7 @@ public function testParseInteger() public function testParseArray() { $c = new Color; - $c->parse(array(181, 55, 23, 0.5)); + $c->parse([181, 55, 23, 0.5]); $this->validateColor($c, 181, 55, 23, 0.5); } @@ -100,23 +100,23 @@ public function testInitFromInteger() public function testInitFromArray() { $c = new Color; - $c->initFromArray(array(0, 0, 0, 0)); + $c->initFromArray([0, 0, 0, 0]); $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray(array(0, 0, 0, 1)); + $c->initFromArray([0, 0, 0, 1]); $this->validateColor($c, 0, 0, 0, 1); - $c->initFromArray(array(255, 255, 255, 1)); + $c->initFromArray([255, 255, 255, 1]); $this->validateColor($c, 255, 255, 255, 1); - $c->initFromArray(array(255, 255, 255, 0)); + $c->initFromArray([255, 255, 255, 0]); $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray(array(255, 255, 255, 0.5)); + $c->initFromArray([255, 255, 255, 0.5]); $this->validateColor($c, 255, 255, 255, 0.5); - $c->initFromArray(array(0, 0, 0)); + $c->initFromArray([0, 0, 0]); $this->validateColor($c, 0, 0, 0, 1); - $c->initFromArray(array(255, 255, 255)); + $c->initFromArray([255, 255, 255]); $this->validateColor($c, 255, 255, 255, 1); - $c->initFromArray(array(181, 55, 23)); + $c->initFromArray([181, 55, 23]); $this->validateColor($c, 181, 55, 23, 1); - $c->initFromArray(array(181, 55, 23, 0.5)); + $c->initFromArray([181, 55, 23, 0.5]); $this->validateColor($c, 181, 55, 23, 0.5); } @@ -189,32 +189,32 @@ public function testGetInt() $this->assertInternalType('int', $i); $this->assertEquals($i, 16777215); - $c = new Color(array(255, 255, 255)); + $c = new Color([255, 255, 255]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 4294967295); - $c = new Color(array(255, 255, 255, 1)); + $c = new Color([255, 255, 255, 1]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 4294967295); - $c = new Color(array(181, 55, 23, 0.2)); + $c = new Color([181, 55, 23, 0.2]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 867514135); - $c = new Color(array(255, 255, 255, 0.5)); + $c = new Color([255, 255, 255, 0.5]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 2164260863); - $c = new Color(array(181, 55, 23, 1)); + $c = new Color([181, 55, 23, 1]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 4290066199); - $c = new Color(array(0, 0, 0, 0)); + $c = new Color([0, 0, 0, 0]); $i = $c->getInt(); $this->assertInternalType('int', $i); $this->assertEquals($i, 0); @@ -227,17 +227,17 @@ public function testGetHex() $this->assertInternalType('string', $i); $this->assertEquals($i, 'ffffff'); - $c = new Color(array(255, 255, 255, 1)); + $c = new Color([255, 255, 255, 1]); $i = $c->getHex(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'ffffff'); - $c = new Color(array(181, 55, 23, 0.5)); + $c = new Color([181, 55, 23, 0.5]); $i = $c->getHex(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'b53717'); - $c = new Color(array(0, 0, 0, 0)); + $c = new Color([0, 0, 0, 0]); $i = $c->getHex('#'); $this->assertInternalType('string', $i); $this->assertEquals($i, '#000000'); @@ -248,22 +248,22 @@ public function testGetArray() $c = new Color; $i = $c->getArray(); $this->assertInternalType('array', $i); - $this->assertEquals($i, array(255, 255, 255, 0)); + $this->assertEquals($i, [255, 255, 255, 0]); - $c = new Color(array(255, 255, 255, 1)); + $c = new Color([255, 255, 255, 1]); $i = $c->getArray(); $this->assertInternalType('array', $i); - $this->assertEquals($i, array(255, 255, 255, 1)); + $this->assertEquals($i, [255, 255, 255, 1]); - $c = new Color(array(181, 55, 23, 0.5)); + $c = new Color([181, 55, 23, 0.5]); $i = $c->getArray(); $this->assertInternalType('array', $i); - $this->assertEquals($i, array(181, 55, 23, 0.5)); + $this->assertEquals($i, [181, 55, 23, 0.5]); - $c = new Color(array(0, 0, 0, 1)); + $c = new Color([0, 0, 0, 1]); $i = $c->getArray(); $this->assertInternalType('array', $i); - $this->assertEquals($i, array(0, 0, 0, 1)); + $this->assertEquals($i, [0, 0, 0, 1]); } public function testGetRgba() @@ -273,22 +273,22 @@ public function testGetRgba() $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(255, 255, 255, 0.00)'); - $c = new Color(array(255, 255, 255, 1)); + $c = new Color([255, 255, 255, 1]); $i = $c->getRgba(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(255, 255, 255, 1.00)'); - $c = new Color(array(181, 55, 23, 0.5)); + $c = new Color([181, 55, 23, 0.5]); $i = $c->getRgba(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(181, 55, 23, 0.50)'); - $c = new Color(array(0, 0, 0, 1)); + $c = new Color([0, 0, 0, 1]); $i = $c->getRgba(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(0, 0, 0, 1.00)'); - $c = new Color(array(255, 255, 255, 0.5)); + $c = new Color([255, 255, 255, 0.5]); $i = $c->getRgba(); $this->assertInternalType('string', $i); $this->assertEquals($i, 'rgba(255, 255, 255, 0.50)'); @@ -296,24 +296,24 @@ public function testGetRgba() public function testDiffers() { - $c1 = new Color(array(0, 0, 0)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([0, 0, 0]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(false, $c1->differs($c2)); - $c1 = new Color(array(1, 0, 0)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([1, 0, 0]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(true, $c1->differs($c2)); - $c1 = new Color(array(1, 0, 0)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([1, 0, 0]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(false, $c1->differs($c2, 10)); - $c1 = new Color(array(127, 127, 127)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([127, 127, 127]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(true, $c1->differs($c2, 49)); - $c1 = new Color(array(127, 127, 127)); - $c2 = new Color(array(0, 0, 0)); + $c1 = new Color([127, 127, 127]); + $c2 = new Color([0, 0, 0]); $this->assertEquals(false, $c1->differs($c2, 50)); } diff --git a/tests/ImagickSystemTest.php b/tests/ImagickSystemTest.php index 7c4419ac3..09efee248 100644 --- a/tests/ImagickSystemTest.php +++ b/tests/ImagickSystemTest.php @@ -1017,9 +1017,9 @@ public function testFillImageWithInterventionImage() public function testPixelImage() { $img = $this->manager()->make('tests/images/tile.png'); - $coords = array(array(5, 5), array(12, 12)); + $coords = [[5, 5], [12, 12]]; $img = $img->pixel('fdf5e4', $coords[0][0], $coords[0][1]); - $img = $img->pixel(array(255, 255, 255), $coords[1][0], $coords[1][1]); + $img = $img->pixel([255, 255, 255], $coords[1][0], $coords[1][1]); $this->assertEquals('#fdf5e4', $img->pickColor($coords[0][0], $coords[0][1], 'hex')); $this->assertEquals('#ffffff', $img->pickColor($coords[1][0], $coords[1][1], 'hex')); } @@ -1055,7 +1055,7 @@ public function testCircleImage() public function testPolygonImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); - $points = array(3, 3, 11, 11, 7, 13); + $points = [3, 3, 11, 11, 7, 13]; $img->polygon($points, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); $this->assertEquals('e301afe179da858d441ad8fc0eb5490a', $img->checksum()); } @@ -1324,7 +1324,7 @@ public function testTrimGradient() public function testTrimOnlyLeftAndRight() { $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, array('left', 'right'), 60); + $img->trim(null, ['left', 'right'], 60); $this->assertEquals($img->getWidth(), 20); $this->assertEquals($img->getHeight(), 50); } @@ -1332,7 +1332,7 @@ public function testTrimOnlyLeftAndRight() public function testTrimOnlyTopAndBottom() { $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, array('top', 'bottom'), 60); + $img->trim(null, ['top', 'bottom'], 60); $this->assertEquals($img->getWidth(), 50); $this->assertEquals($img->getHeight(), 20); } @@ -1393,7 +1393,7 @@ public function testTrimWithFeather() // trim only left and right with feather $img = $this->manager()->make('tests/images/trim.png'); $feather = 10; - $img->trim(null, array('left', 'right'), null, $feather); + $img->trim(null, ['left', 'right'], null, $feather); $this->assertEquals($img->getWidth(), 28 + $feather * 2); $this->assertEquals($img->getHeight(), 50); $img->destroy(); @@ -1401,7 +1401,7 @@ public function testTrimWithFeather() // trim only top and bottom with feather $img = $this->manager()->make('tests/images/trim.png'); $feather = 10; - $img->trim(null, array('top', 'bottom'), null, $feather); + $img->trim(null, ['top', 'bottom'], null, $feather); $this->assertEquals($img->getWidth(), 50); $this->assertEquals($img->getHeight(), 28 + $feather * 2); $img->destroy(); @@ -1624,8 +1624,8 @@ private function assertTransparentPosition($img, $x, $y, $transparent = 0) private function manager() { - return new \Intervention\Image\ImageManager(array( + return new \Intervention\Image\ImageManager([ 'driver' => 'imagick' - )); + ]); } } diff --git a/tests/InsertCommandTest.php b/tests/InsertCommandTest.php index f2fa9001e..db70af090 100644 --- a/tests/InsertCommandTest.php +++ b/tests/InsertCommandTest.php @@ -12,11 +12,11 @@ public function tearDown() public function testGd() { - $position = Mockery::mock('\Intervention\Image\Point', array(0, 0)); + $position = Mockery::mock('\Intervention\Image\Point', [0, 0]); - $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $image_size->shouldReceive('align')->with('center', 10, 20)->once()->andReturn($image_size); - $watermark_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $watermark_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $watermark_size->shouldReceive('align')->with('center')->once()->andReturn($watermark_size); $image_size->shouldReceive('relativePosition')->with($watermark_size)->once()->andReturn($position); @@ -32,18 +32,18 @@ public function testGd() $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); $watermark->shouldReceive('getCore')->once()->andReturn($resource); - $command = new InsertGd(array($path, 'center', 10, 20)); + $command = new InsertGd([$path, 'center', 10, 20]); $result = $command->execute($image); $this->assertTrue($result); } public function testImagick() { - $position = Mockery::mock('\Intervention\Image\Point', array(10, 20)); + $position = Mockery::mock('\Intervention\Image\Point', [10, 20]); - $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $image_size->shouldReceive('align')->with('center', 10, 20)->once()->andReturn($image_size); - $watermark_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $watermark_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $watermark_size->shouldReceive('align')->with('center')->once()->andReturn($watermark_size); $image_size->shouldReceive('relativePosition')->with($watermark_size)->once()->andReturn($position); @@ -59,7 +59,7 @@ public function testImagick() $image->shouldReceive('getSize')->once()->andReturn($image_size); $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); $watermark->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InsertImagick(array($path, 'center', 10, 20)); + $command = new InsertImagick([$path, 'center', 10, 20]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/InterlaceCommandTest.php b/tests/InterlaceCommandTest.php index 102f1faad..ab34c6c59 100644 --- a/tests/InterlaceCommandTest.php +++ b/tests/InterlaceCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new InterlaceGd(array(true)); + $command = new InterlaceGd([true]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('setinterlacescheme')->with(\Imagick::INTERLACE_LINE)->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InterlaceImagick(array(true)); + $command = new InterlaceImagick([true]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/InvertCommandTest.php b/tests/InvertCommandTest.php index e7cabba25..84cebd07a 100644 --- a/tests/InvertCommandTest.php +++ b/tests/InvertCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new InvertGd(array()); + $command = new InvertGd([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('negateimage')->with(false)->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InvertImagick(array()); + $command = new InvertImagick([]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/IptcCommandTest.php b/tests/IptcCommandTest.php index d1ca865ff..50cc670e4 100644 --- a/tests/IptcCommandTest.php +++ b/tests/IptcCommandTest.php @@ -14,7 +14,7 @@ public function testFetchAll() $image = Mockery::mock('Intervention\Image\Image'); $image->dirname = __DIR__.'/images'; $image->basename = 'iptc.jpg'; - $command = new IptcCommand(array()); + $command = new IptcCommand([]); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -26,7 +26,7 @@ public function testFetchDefined() $image = Mockery::mock('Intervention\Image\Image'); $image->dirname = __DIR__.'/images'; $image->basename = 'exif.jpg'; - $command = new IptcCommand(array('AuthorByline')); + $command = new IptcCommand(['AuthorByline']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -39,7 +39,7 @@ public function testFetchNonExisting() $image = Mockery::mock('Intervention\Image\Image'); $image->dirname = __DIR__.'/images'; $image->basename = 'exif.jpg'; - $command = new IptcCommand(array('xxx')); + $command = new IptcCommand(['xxx']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -52,7 +52,7 @@ public function testFetchFromPng() $image = Mockery::mock('Intervention\Image\Image'); $image->dirname = __DIR__.'/images'; $image->basename = 'star.png'; - $command = new IptcCommand(array('Orientation')); + $command = new IptcCommand(['Orientation']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -62,7 +62,7 @@ public function testFetchFromPng() public function testReturnNullOnIptcReadFail() { $image = Mockery::mock('Intervention\Image\Image'); - $command = new IptcCommand(array('Orientation')); + $command = new IptcCommand(['Orientation']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); diff --git a/tests/LimitColorsCommandTest.php b/tests/LimitColorsCommandTest.php index 181343083..c655b1f6f 100644 --- a/tests/LimitColorsCommandTest.php +++ b/tests/LimitColorsCommandTest.php @@ -12,20 +12,20 @@ public function tearDown() public function testGd() { - $size = Mockery::mock('\Intervention\Image\Size', array(32, 32)); + $size = Mockery::mock('\Intervention\Image\Size', [32, 32]); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new LimitColorsGd(array(16)); + $command = new LimitColorsGd([16]); $result = $command->execute($image); $this->assertTrue($result); } public function testImagick() { - $size = Mockery::mock('\Intervention\Image\Size', array(32, 32)); + $size = Mockery::mock('\Intervention\Image\Size', [32, 32]); $imagick = Mockery::mock('\Imagick'); $imagick->shouldReceive('separateimagechannel')->with(\Imagick::CHANNEL_ALPHA)->times(2); $imagick->shouldReceive('transparentpaintimage')->with('#ffffff', 0, 0, false)->once(); @@ -35,7 +35,7 @@ public function testImagick() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new LimitColorsImagick(array(16)); + $command = new LimitColorsImagick([16]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/LineCommandTest.php b/tests/LineCommandTest.php index 16495fd11..c6e541176 100644 --- a/tests/LineCommandTest.php +++ b/tests/LineCommandTest.php @@ -17,7 +17,7 @@ public function testGd() $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new LineCommand(array(10, 15, 100, 150)); + $command = new LineCommand([10, 15, 100, 150]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); @@ -33,7 +33,7 @@ public function testImagick() $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new LineCommand(array(10, 15, 100, 150)); + $command = new LineCommand([10, 15, 100, 150]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); diff --git a/tests/LineShapeTest.php b/tests/LineShapeTest.php index 506fd4ed0..052b9607a 100644 --- a/tests/LineShapeTest.php +++ b/tests/LineShapeTest.php @@ -12,7 +12,7 @@ public function tearDown() public function testConstructor() { - // gd + // gd $line = new LineGd(10, 15); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\LineShape', $line); $this->assertEquals(10, $line->x); diff --git a/tests/MaskCommandTest.php b/tests/MaskCommandTest.php index fd9a734dc..0e8c03738 100644 --- a/tests/MaskCommandTest.php +++ b/tests/MaskCommandTest.php @@ -14,9 +14,9 @@ public function testGd() { $mask_path = __DIR__.'images/star.png'; $mask_image = Mockery::mock('Intervention\Image\Image'); - $mask_size = Mockery::mock('Intervention\Image\Size', array(32, 32)); + $mask_size = Mockery::mock('Intervention\Image\Size', [32, 32]); $mask_image->shouldReceive('getSize')->once()->andReturn($mask_size); - $mask_image->shouldReceive('pickColor')->andReturn(array(0,0,0,0)); + $mask_image->shouldReceive('pickColor')->andReturn([0,0,0,0]); $canvas_image = Mockery::mock('Intervention\Image\Image'); $canvas_core = imagecreatetruecolor(32, 32); @@ -24,18 +24,18 @@ public function testGd() $canvas_image->shouldReceive('pixel'); $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(32, 32, array(0,0,0,0))->once()->andReturn($canvas_image); + $driver->shouldReceive('newImage')->with(32, 32, [0,0,0,0])->once()->andReturn($canvas_image); $driver->shouldReceive('init')->with($mask_path)->once()->andReturn($mask_image); - $image_size = Mockery::mock('Intervention\Image\Size', array(32, 32)); + $image_size = Mockery::mock('Intervention\Image\Size', [32, 32]); $image_core = imagecreatefrompng(__DIR__.'/images/trim.png'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($image_size); $image->shouldReceive('getDriver')->times(2)->andReturn($driver); - $image->shouldReceive('pickColor')->andReturn(array(0,0,0,0)); + $image->shouldReceive('pickColor')->andReturn([0,0,0,0]); $image->shouldReceive('setCore')->with($canvas_core)->once(); - $command = new MaskGd(array($mask_path, true)); + $command = new MaskGd([$mask_path, true]); $result = $command->execute($image); $this->assertTrue($result); } @@ -46,7 +46,7 @@ public function testImagick() $mask_path = __DIR__.'images/star.png'; $mask_image = Mockery::mock('Intervention\Image\Image'); $mask_image->shouldReceive('getCore')->once()->andReturn($mask_core); - $mask_size = Mockery::mock('Intervention\Image\Size', array(32, 32)); + $mask_size = Mockery::mock('Intervention\Image\Size', [32, 32]); $mask_image->shouldReceive('getSize')->once()->andReturn($mask_size); $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); @@ -56,11 +56,11 @@ public function testImagick() $imagick->shouldReceive('compositeimage')->with($mask_core, \Imagick::COMPOSITE_DSTIN, 0, 0)->once(); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image_size = Mockery::mock('Intervention\Image\Size', array(32, 32)); + $image_size = Mockery::mock('Intervention\Image\Size', [32, 32]); $image->shouldReceive('getSize')->once()->andReturn($image_size); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $command = new MaskImagick(array($mask_path, true)); + $command = new MaskImagick([$mask_path, true]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/OpacityCommandTest.php b/tests/OpacityCommandTest.php index 9fdf7a98d..d943230b9 100644 --- a/tests/OpacityCommandTest.php +++ b/tests/OpacityCommandTest.php @@ -20,12 +20,12 @@ public function testGd() $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); $driver->shouldReceive('newImage')->with(32, 32, 'rgba(0, 0, 0, 0.5)')->andReturn($mask); - $size = Mockery::mock('\Intervention\Image\Size', array(32, 32)); + $size = Mockery::mock('\Intervention\Image\Size', [32, 32]); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('mask')->with($mask_core, true)->once(); - $command = new OpacityGd(array(50)); + $command = new OpacityGd([50]); $result = $command->execute($image); $this->assertTrue($result); } @@ -36,7 +36,7 @@ public function testImagick() $imagick->shouldReceive('evaluateimage')->with(\Imagick::EVALUATE_DIVIDE, 2, \Imagick::CHANNEL_ALPHA)->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new OpacityImagick(array(50)); + $command = new OpacityImagick([50]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/OrientateCommandTest.php b/tests/OrientateCommandTest.php index 6b86c23f3..c8d8860ad 100644 --- a/tests/OrientateCommandTest.php +++ b/tests/OrientateCommandTest.php @@ -14,7 +14,7 @@ public function testExecuteOrientationTwo() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(2); $image->shouldReceive('flip')->once(); - $command = new OrientateCommand(array()); + $command = new OrientateCommand([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -24,7 +24,7 @@ public function testExecuteOrientationThree() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(3); $image->shouldReceive('rotate')->with(180)->once(); - $command = new OrientateCommand(array()); + $command = new OrientateCommand([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -35,7 +35,7 @@ public function testExecuteOrientationFour() $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(4); $image->shouldReceive('rotate')->with(180)->once()->andReturn($image); $image->shouldReceive('flip')->once(); - $command = new OrientateCommand(array()); + $command = new OrientateCommand([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -46,7 +46,7 @@ public function testExecuteOrientationFive() $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(5); $image->shouldReceive('rotate')->with(270)->once()->andReturn($image); $image->shouldReceive('flip')->once(); - $command = new OrientateCommand(array()); + $command = new OrientateCommand([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -56,7 +56,7 @@ public function testExecuteOrientationSix() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(6); $image->shouldReceive('rotate')->with(270)->once(); - $command = new OrientateCommand(array()); + $command = new OrientateCommand([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -67,7 +67,7 @@ public function testExecuteOrientationSeven() $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(7); $image->shouldReceive('rotate')->with(90)->once()->andReturn($image); $image->shouldReceive('flip')->once(); - $command = new OrientateCommand(array()); + $command = new OrientateCommand([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -77,7 +77,7 @@ public function testExecuteOrientationEight() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(8); $image->shouldReceive('rotate')->with(90)->once(); - $command = new OrientateCommand(array()); + $command = new OrientateCommand([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -86,7 +86,7 @@ public function testExecuteOrientationNoExifData() { $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(null); - $command = new OrientateCommand(array()); + $command = new OrientateCommand([]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/PickColorCommandTest.php b/tests/PickColorCommandTest.php index 731de04e5..b266af232 100644 --- a/tests/PickColorCommandTest.php +++ b/tests/PickColorCommandTest.php @@ -15,7 +15,7 @@ public function testGdWithCoordinates() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new PickColorGd(array(1, 2)); + $command = new PickColorGd([1, 2]); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -28,7 +28,7 @@ public function testGdWithFormat() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new PickColorGd(array(1, 2, 'hex')); + $command = new PickColorGd([1, 2, 'hex']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -42,7 +42,7 @@ public function testImagickWithCoordinates() $imagick->shouldReceive('getimagepixelcolor')->with(1, 2)->andReturn(new ImagickPixel); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PickColorImagick(array(1, 2)); + $command = new PickColorImagick([1, 2]); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); @@ -56,7 +56,7 @@ public function testImagickWithFormat() $imagick->shouldReceive('getimagepixelcolor')->with(1, 2)->andReturn(new ImagickPixel('#ff0000')); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PickColorImagick(array(1, 2, 'hex')); + $command = new PickColorImagick([1, 2, 'hex']); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); diff --git a/tests/PixelCommandTest.php b/tests/PixelCommandTest.php index e1e2dcfb6..bf766e3f5 100644 --- a/tests/PixelCommandTest.php +++ b/tests/PixelCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new PixelGd(array('#b53717', 10, 20)); + $command = new PixelGd(['#b53717', 10, 20]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('drawimage')->once()->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PixelImagick(array('#b53717', 10, 20)); + $command = new PixelImagick(['#b53717', 10, 20]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/PixelateCommandTest.php b/tests/PixelateCommandTest.php index 76314ba4f..549f608b5 100644 --- a/tests/PixelateCommandTest.php +++ b/tests/PixelateCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new PixelateGd(array(10)); + $command = new PixelateGd([10]); $result = $command->execute($image); $this->assertTrue($result); } @@ -29,7 +29,7 @@ public function testImagick() $image->shouldReceive('getCore')->times(2)->andReturn($imagick); $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new PixelateImagick(array(10)); + $command = new PixelateImagick([10]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/PolygonCommandTest.php b/tests/PolygonCommandTest.php index c3eaf7165..1038aa81c 100644 --- a/tests/PolygonCommandTest.php +++ b/tests/PolygonCommandTest.php @@ -11,14 +11,14 @@ public function tearDown() public function testGd() { - $points = array(1, 2, 3, 4, 5, 6); + $points = [1, 2, 3, 4, 5, 6]; $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new PolygonCommand(array($points)); + $command = new PolygonCommand([$points]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); @@ -26,7 +26,7 @@ public function testGd() public function testImagick() { - $points = array(1, 2, 3, 4, 5, 6); + $points = [1, 2, 3, 4, 5, 6]; $imagick = Mockery::mock('\Imagick'); $imagick->shouldReceive('drawimage'); $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); @@ -35,7 +35,7 @@ public function testImagick() $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PolygonCommand(array($points)); + $command = new PolygonCommand([$points]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); diff --git a/tests/PolygonShapeTest.php b/tests/PolygonShapeTest.php index 4f87b2b30..9e8812494 100644 --- a/tests/PolygonShapeTest.php +++ b/tests/PolygonShapeTest.php @@ -12,9 +12,9 @@ public function tearDown() public function testGdConstructor() { - $polygon = new PolygonGd(array(1, 2, 3, 4, 5, 6)); + $polygon = new PolygonGd([1, 2, 3, 4, 5, 6]); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); - $this->assertEquals(array(1, 2, 3, 4, 5, 6), $polygon->points); + $this->assertEquals([1, 2, 3, 4, 5, 6], $polygon->points); } @@ -23,7 +23,7 @@ public function testGdApplyToImage() $core = imagecreatetruecolor(300, 200); $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($core); - $polygon = new PolygonGd(array(1, 2, 3, 4, 5, 6)); + $polygon = new PolygonGd([1, 2, 3, 4, 5, 6]); $result = $polygon->applyToImage($image); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); $this->assertTrue($result); @@ -31,12 +31,12 @@ public function testGdApplyToImage() public function testImagickConstructor() { - $polygon = new PolygonImagick(array(1, 2, 3, 4, 5, 6)); + $polygon = new PolygonImagick([1, 2, 3, 4, 5, 6]); $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\PolygonShape', $polygon); - $this->assertEquals(array( - array('x' => 1, 'y' => 2), - array('x' => 3, 'y' => 4), - array('x' => 5, 'y' => 6)), + $this->assertEquals([ + ['x' => 1, 'y' => 2], + ['x' => 3, 'y' => 4], + ['x' => 5, 'y' => 6]], $polygon->points); } @@ -47,7 +47,7 @@ public function testImagickApplyToImage() $core->shouldReceive('drawimage')->once(); $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($core); - $polygon = new PolygonImagick(array(1, 2, 3, 4, 5, 6)); + $polygon = new PolygonImagick([1, 2, 3, 4, 5, 6]); $result = $polygon->applyToImage($image); $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\PolygonShape', $polygon); $this->assertTrue($result); diff --git a/tests/PsrResponseCommandTest.php b/tests/PsrResponseCommandTest.php index b28a45f99..df287c081 100644 --- a/tests/PsrResponseCommandTest.php +++ b/tests/PsrResponseCommandTest.php @@ -25,7 +25,7 @@ public function testResponseCreationAndHeaders() ->twice() ->andReturn($encodedContent); - $command = new PsrResponseCommand(array('jpg', 87)); + $command = new PsrResponseCommand(['jpg', 87]); $result = $command->execute($image); $this->assertTrue($result); diff --git a/tests/RectangleCommandTest.php b/tests/RectangleCommandTest.php index d3742f132..2ab1681cd 100644 --- a/tests/RectangleCommandTest.php +++ b/tests/RectangleCommandTest.php @@ -17,7 +17,7 @@ public function testGd() $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new RectangleCommand(array(10, 15, 100, 150)); + $command = new RectangleCommand([10, 15, 100, 150]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); @@ -33,7 +33,7 @@ public function testImagick() $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new RectangleCommand(array(10, 15, 100, 150)); + $command = new RectangleCommand([10, 15, 100, 150]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); diff --git a/tests/RectangleShapeTest.php b/tests/RectangleShapeTest.php index 1f652ab0d..663326913 100644 --- a/tests/RectangleShapeTest.php +++ b/tests/RectangleShapeTest.php @@ -12,7 +12,7 @@ public function tearDown() public function testConstructor() { - // gd + // gd $rectangle = new RectangleGd(10, 15, 100, 150); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\RectangleShape', $rectangle); $this->assertEquals(10, $rectangle->x1); diff --git a/tests/ResetCommandTest.php b/tests/ResetCommandTest.php index 18d823dda..c3f85ffb7 100644 --- a/tests/ResetCommandTest.php +++ b/tests/ResetCommandTest.php @@ -12,7 +12,7 @@ public function tearDown() public function testGdWithoutName() { - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $driver = Mockery::mock('Intervention\Image\Gd\Driver'); @@ -21,14 +21,14 @@ public function testGdWithoutName() $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('setCore')->once(); $image->shouldReceive('getBackup')->once()->andReturn($resource); - $command = new ResetGd(array()); + $command = new ResetGd([]); $result = $command->execute($image); $this->assertTrue($result); } public function testGdWithName() { - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $driver = Mockery::mock('Intervention\Image\Gd\Driver'); @@ -36,8 +36,8 @@ public function testGdWithName() $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->withArgs(array('fooBackup'))->andReturn($resource); - $command = new ResetGd(array('fooBackup')); + $image->shouldReceive('getBackup')->once()->withArgs(['fooBackup'])->andReturn($resource); + $command = new ResetGd(['fooBackup']); $result = $command->execute($image); $this->assertTrue($result); } @@ -50,7 +50,7 @@ public function testImagickWithoutName() $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('setCore')->once(); $image->shouldReceive('getBackup')->once()->andReturn($imagick); - $command = new ResetImagick(array()); + $command = new ResetImagick([]); $result = $command->execute($image); $this->assertTrue($result); } @@ -62,8 +62,8 @@ public function testImagickWithName() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->withArgs(array('fooBackup'))->andReturn($imagick); - $command = new ResetImagick(array('fooBackup')); + $image->shouldReceive('getBackup')->once()->withArgs(['fooBackup'])->andReturn($imagick); + $command = new ResetImagick(['fooBackup']); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/ResizeCanvasCommandTest.php b/tests/ResizeCanvasCommandTest.php index c183ff1bf..2d749399b 100644 --- a/tests/ResizeCanvasCommandTest.php +++ b/tests/ResizeCanvasCommandTest.php @@ -13,12 +13,12 @@ public function tearDown() public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $canvas_pos = Mockery::mock('\Intervention\Image\Point', array(0, 0)); - $canvas_size = Mockery::mock('\Intervention\Image\Size', array(820, 640)); + $canvas_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); + $canvas_size = Mockery::mock('\Intervention\Image\Size', [820, 640]); $canvas_size->shouldReceive('align')->with('center')->andReturn($canvas_size); $canvas_size->shouldReceive('relativePosition')->andReturn($canvas_pos); - $image_pos = Mockery::mock('\Intervention\Image\Point', array(0, 0)); - $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $image_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); + $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $image_size->shouldReceive('align')->with('center')->andReturn($image_size); $image_size->shouldReceive('relativePosition')->andReturn($image_pos); $canvas = Mockery::mock('\Intervention\Image\Image'); @@ -33,19 +33,19 @@ public function testGd() $image->shouldReceive('getHeight')->once()->andReturn(600); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $command = new ResizeCanvasGd(array(20, 40, 'center', true, '#b53717')); + $command = new ResizeCanvasGd([20, 40, 'center', true, '#b53717']); $result = $command->execute($image); $this->assertTrue($result); } public function testImagick() { - $canvas_pos = Mockery::mock('\Intervention\Image\Point', array(0, 0)); - $canvas_size = Mockery::mock('\Intervention\Image\Size', array(820, 640)); + $canvas_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); + $canvas_size = Mockery::mock('\Intervention\Image\Size', [820, 640]); $canvas_size->shouldReceive('align')->with('center')->andReturn($canvas_size); $canvas_size->shouldReceive('relativePosition')->andReturn($canvas_pos); - $image_pos = Mockery::mock('\Intervention\Image\Point', array(0, 0)); - $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); + $image_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); + $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); $image_size->shouldReceive('align')->with('center')->andReturn($image_size); $image_size->shouldReceive('relativePosition')->andReturn($image_pos); $canvas = Mockery::mock('\Intervention\Image\Image'); @@ -71,7 +71,7 @@ public function testImagick() $image->shouldReceive('getHeight')->once()->andReturn(600); $image->shouldReceive('getCore')->times(3)->andReturn($imagick); $image->shouldReceive('setCore')->once(); - $command = new ResizeCanvasImagick(array(20, 40, 'center', true, '#b53717')); + $command = new ResizeCanvasImagick([20, 40, 'center', true, '#b53717']); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/ResizeCommandTest.php b/tests/ResizeCommandTest.php index e9af7669b..3f3ac187f 100644 --- a/tests/ResizeCommandTest.php +++ b/tests/ResizeCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $callback = function ($constraint) { $constraint->upsize(); }; $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(800); $size->shouldReceive('getHeight')->once()->andReturn(600); @@ -24,7 +24,7 @@ public function testGd() $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $command = new ResizeCommandGd(array(300, 200, $callback)); + $command = new ResizeCommandGd([300, 200, $callback]); $result = $command->execute($image); $this->assertTrue($result); } @@ -34,14 +34,14 @@ public function testImagick() $callback = function ($constraint) { $constraint->upsize(); }; $imagick = Mockery::mock('Imagick'); $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(300); $size->shouldReceive('getHeight')->once()->andReturn(200); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new ResizeCommandImagick(array(300, 200, $callback)); + $command = new ResizeCommandImagick([300, 200, $callback]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/RotateCommandTest.php b/tests/RotateCommandTest.php index dedf8a0fc..64f68c997 100644 --- a/tests/RotateCommandTest.php +++ b/tests/RotateCommandTest.php @@ -16,19 +16,19 @@ public function testGd() $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once()->andReturn($resource); - $command = new RotateGd(array(45, '#b53717')); + $command = new RotateGd([45, '#b53717']); $result = $command->execute($image); $this->assertTrue($result); } public function testImagick() { - $pixel = Mockery::mock('ImagickPixel', array('#b53717')); + $pixel = Mockery::mock('ImagickPixel', ['#b53717']); $imagick = Mockery::mock('Imagick'); $imagick->shouldReceive('rotateimage')->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new RotateImagick(array(45, '#b53717')); + $command = new RotateImagick([45, '#b53717']); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/SharpenCommandTest.php b/tests/SharpenCommandTest.php index b88869468..8a756a564 100644 --- a/tests/SharpenCommandTest.php +++ b/tests/SharpenCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new SharpenGd(array(50)); + $command = new SharpenGd([50]); $result = $command->execute($image); $this->assertTrue($result); } @@ -26,7 +26,7 @@ public function testImagick() $imagick->shouldReceive('unsharpmaskimage')->with(1, 1, 8, 0)->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new SharpenImagick(array(50)); + $command = new SharpenImagick([50]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/SizeTest.php b/tests/SizeTest.php index 4be87ee09..64679e699 100644 --- a/tests/SizeTest.php +++ b/tests/SizeTest.php @@ -22,10 +22,10 @@ public function testConstructorWithCoordinates() { $pivot = Mockery::mock('Intervention\Image\Point'); $size = new Size(300, 200, $pivot); - $this->assertInstanceOf('Intervention\Image\Size', $size); + $this->assertInstanceOf('Intervention\Image\Size', $size); $this->assertInstanceOf('Intervention\Image\Point', $size->pivot); - $this->assertEquals(300, $size->width); - $this->assertEquals(200, $size->height); + $this->assertEquals(300, $size->width); + $this->assertEquals(200, $size->height); } public function testGetWidth() @@ -56,18 +56,18 @@ public function testResize() { $size = new Size(800, 600); $size->resize(1000, 2000); - $this->assertEquals(1000, $size->width); - $this->assertEquals(2000, $size->height); + $this->assertEquals(1000, $size->width); + $this->assertEquals(2000, $size->height); $size = new Size(800, 600); $size->resize(2000, null); - $this->assertEquals(2000, $size->width); - $this->assertEquals(600, $size->height); + $this->assertEquals(2000, $size->width); + $this->assertEquals(600, $size->height); $size = new Size(800, 600); $size->resize(null, 1000); - $this->assertEquals(800, $size->width); - $this->assertEquals(1000, $size->height); + $this->assertEquals(800, $size->width); + $this->assertEquals(1000, $size->height); } public function testResizeWithCallbackAspectRatio() @@ -114,28 +114,28 @@ public function testResizeWithCallbackAspectRatio() $size = new Size(640, 480); $size->resize(225, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(169, $size->height); + $this->assertEquals(225, $size->width); + $this->assertEquals(169, $size->height); $size = new Size(640, 480); $size->resize(223, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(223, $size->width); - $this->assertEquals(167, $size->height); + $this->assertEquals(223, $size->width); + $this->assertEquals(167, $size->height); $size = new Size(600, 800); $size->resize(300, 300, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(300, $size->height); + $this->assertEquals(225, $size->width); + $this->assertEquals(300, $size->height); $size = new Size(800, 600); $size->resize(400, 10, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(13, $size->width); - $this->assertEquals(10, $size->height); + $this->assertEquals(13, $size->width); + $this->assertEquals(10, $size->height); $size = new Size(800, 600); $size->resize(1000, 1200, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(1000, $size->width); - $this->assertEquals(750, $size->height); + $this->assertEquals(1000, $size->width); + $this->assertEquals(750, $size->height); } public function testResizeWithCallbackUpsize() @@ -225,8 +225,8 @@ public function testResizeWithCallbackAspectRatioAndUpsize() $size = new Size(600, 800); $size->resize(300, 300, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(300, $size->height); + $this->assertEquals(225, $size->width); + $this->assertEquals(300, $size->height); $size = new Size(800, 600); $size->resize(400, 10, function ($c) { $c->aspectRatio(); $c->upsize(); }); @@ -294,7 +294,7 @@ public function testAlign() $box->align('bottom-left'); $box->align('bottom'); $b = $box->align('bottom-right'); - $this->assertInstanceOf('Intervention\Image\Size', $b); + $this->assertInstanceOf('Intervention\Image\Size', $b); } public function testFit() @@ -370,27 +370,27 @@ public function testFitWithPosition(Size $box, $position, $x, $y) public function providerFitWithPosition() { - return array( - array(new Size(800, 600), 'top-left', 0, 0), - array(new Size(800, 600), 'top', 100, 0), - array(new Size(800, 600), 'top-right', 200, 0), - array(new Size(800, 600), 'left', 0, 0), - array(new Size(800, 600), 'center', 100, 0), - array(new Size(800, 600), 'right', 200, 0), - array(new Size(800, 600), 'bottom-left', 0, 0), - array(new Size(800, 600), 'bottom', 100, 0), - array(new Size(800, 600), 'bottom-right', 200, 0), - - array(new Size(600, 800), 'top-left', 0, 0), - array(new Size(600, 800), 'top', 0, 0), - array(new Size(600, 800), 'top-right', 0, 0), - array(new Size(600, 800), 'left', 0, 100), - array(new Size(600, 800), 'center', 0, 100), - array(new Size(600, 800), 'right', 0, 100), - array(new Size(600, 800), 'bottom-left', 0, 200), - array(new Size(600, 800), 'bottom', 0, 200), - array(new Size(600, 800), 'bottom-right', 0, 200), - ); + return [ + [new Size(800, 600), 'top-left', 0, 0], + [new Size(800, 600), 'top', 100, 0], + [new Size(800, 600), 'top-right', 200, 0], + [new Size(800, 600), 'left', 0, 0], + [new Size(800, 600), 'center', 100, 0], + [new Size(800, 600), 'right', 200, 0], + [new Size(800, 600), 'bottom-left', 0, 0], + [new Size(800, 600), 'bottom', 100, 0], + [new Size(800, 600), 'bottom-right', 200, 0], + + [new Size(600, 800), 'top-left', 0, 0], + [new Size(600, 800), 'top', 0, 0], + [new Size(600, 800), 'top-right', 0, 0], + [new Size(600, 800), 'left', 0, 100], + [new Size(600, 800), 'center', 0, 100], + [new Size(600, 800), 'right', 0, 100], + [new Size(600, 800), 'bottom-left', 0, 200], + [new Size(600, 800), 'bottom', 0, 200], + [new Size(600, 800), 'bottom-right', 0, 200], + ]; } public function testFitsInto() diff --git a/tests/StreamCommandTest.php b/tests/StreamCommandTest.php index 79d1048f1..bebe5fd25 100644 --- a/tests/StreamCommandTest.php +++ b/tests/StreamCommandTest.php @@ -22,7 +22,7 @@ public function testStreamCreationAndContent() ->once() ->andReturn($encodedContent); - $command = new StreamCommand(array('jpg', 87)); + $command = new StreamCommand(['jpg', 87]); $result = $command->execute($image); $this->assertTrue($result); diff --git a/tests/TextCommandTest.php b/tests/TextCommandTest.php index 53df1650d..5cbbd0694 100644 --- a/tests/TextCommandTest.php +++ b/tests/TextCommandTest.php @@ -17,7 +17,7 @@ public function testGd() $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new TextCommand(array('test', 10, 20)); + $command = new TextCommand(['test', 10, 20]); $result = $command->execute($image); $this->assertTrue($result); $this->assertFalse($command->hasOutput()); diff --git a/tests/TrimCommandTest.php b/tests/TrimCommandTest.php index de6d65ee1..6bf0f1bc9 100644 --- a/tests/TrimCommandTest.php +++ b/tests/TrimCommandTest.php @@ -22,7 +22,7 @@ public function testGd() $image->shouldReceive('pickColor')->with(0, 0, 'object')->times(2)->andReturn($baseColor); $image->shouldReceive('pickColor')->with(799, 0, 'object')->once()->andReturn($baseColor); $image->shouldReceive('setCore')->once(); - $command = new TrimGd(array('top-left', array('left', 'right'), 45, 2)); + $command = new TrimGd(['top-left', ['left', 'right'], 45, 2]); $result = $command->execute($image); $this->assertTrue($result); } @@ -37,7 +37,7 @@ public function testImagick() $imagick->height = 100; $imagick->shouldReceive('borderimage')->with($baseColorPixel, 1, 1)->once()->andReturn(true); $imagick->shouldReceive('trimimage')->with(29632.5)->once()->andReturn(true); - $imagick->shouldReceive('getimagepage')->once()->andReturn(array('x' => 50, 'y' => 50)); + $imagick->shouldReceive('getimagepage')->once()->andReturn(['x' => 50, 'y' => 50]); $imagick->shouldReceive('cropimage')->with(104, 202, 47, 0)->once()->andReturn(true); $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once()->andReturn(true); $imagick->shouldReceive('destroy')->with()->once()->andReturn(true); @@ -46,7 +46,7 @@ public function testImagick() $image->shouldReceive('getHeight')->once()->andReturn(600); $image->shouldReceive('pickColor')->with(0, 0, 'object')->once()->andReturn($baseColor); $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new TrimImagick(array('top-left', array('left', 'right'), 45, 2)); + $command = new TrimImagick(['top-left', ['left', 'right'], 45, 2]); $result = $command->execute($image); $this->assertTrue($result); } diff --git a/tests/WidenCommandTest.php b/tests/WidenCommandTest.php index a5444dfb3..15bd6a650 100644 --- a/tests/WidenCommandTest.php +++ b/tests/WidenCommandTest.php @@ -15,7 +15,7 @@ public function testGd() $callback = function ($constraint) { $constraint->aspectRatio(); }; $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $size->shouldReceive('resize')->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(800); $size->shouldReceive('getHeight')->once()->andReturn(600); @@ -24,7 +24,7 @@ public function testGd() $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('getCore')->once()->andReturn($resource); $image->shouldReceive('setCore')->once(); - $command = new WidenGd(array(200)); + $command = new WidenGd([200]); $result = $command->execute($image); $this->assertTrue($result); } @@ -34,14 +34,14 @@ public function testImagick() $callback = function ($constraint) { $constraint->upsize(); }; $imagick = Mockery::mock('Imagick'); $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $size = Mockery::mock('Intervention\Image\Size', [800, 600]); $size->shouldReceive('resize')->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(300); $size->shouldReceive('getHeight')->once()->andReturn(200); $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new WidenImagick(array(200)); + $command = new WidenImagick([200]); $result = $command->execute($image); $this->assertTrue($result); } From d0c1415846c6b2c575ed94d5ccc62ef51c835c4f Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Mon, 11 Sep 2017 17:43:50 +0100 Subject: [PATCH 0058/1667] FIX PNGs are now transparent when created from clones --- src/Intervention/Image/Gd/Driver.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index 6179f08f2..8bbc4a3e4 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -76,6 +76,8 @@ public function cloneCore($core) $clone = imagecreatetruecolor($width, $height); imagealphablending($clone, false); imagesavealpha($clone, true); + $transparency = imagecolorallocatealpha($clone, 0, 0, 0, 127); + imagefill($clone, 0, 0, $transparency); imagecopy($clone, $core, 0, 0, 0, 0, $width, $height); From 047ae11ae08753fb75f15d9beb4b8bebf4421a8b Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Tue, 12 Sep 2017 11:49:40 +0100 Subject: [PATCH 0059/1667] Refactor BackupCommand to use native clone --- .../Image/Gd/Commands/BackupCommand.php | 13 ++---------- .../Image/Imagick/Commands/BackupCommand.php | 3 ++- tests/BackupCommandTest.php | 20 +++++++++++-------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/BackupCommand.php b/src/Intervention/Image/Gd/Commands/BackupCommand.php index 347daea6b..98b3c7250 100644 --- a/src/Intervention/Image/Gd/Commands/BackupCommand.php +++ b/src/Intervention/Image/Gd/Commands/BackupCommand.php @@ -15,17 +15,8 @@ public function execute($image) $backupName = $this->argument(0)->value(); // clone current image resource - $size = $image->getSize(); - $clone = imagecreatetruecolor($size->width, $size->height); - imagealphablending($clone, false); - imagesavealpha($clone, true); - $transparency = imagecolorallocatealpha($clone, 0, 0, 0, 127); - imagefill($clone, 0, 0, $transparency); - - // copy image to clone - imagecopy($clone, $image->getCore(), 0, 0, 0, 0, $size->width, $size->height); - - $image->setBackup($clone, $backupName); + $clone = clone $image; + $image->setBackup($clone->getCore(), $backupName); return true; } diff --git a/src/Intervention/Image/Imagick/Commands/BackupCommand.php b/src/Intervention/Image/Imagick/Commands/BackupCommand.php index 90f2d7216..60dedb2b7 100644 --- a/src/Intervention/Image/Imagick/Commands/BackupCommand.php +++ b/src/Intervention/Image/Imagick/Commands/BackupCommand.php @@ -15,7 +15,8 @@ public function execute($image) $backupName = $this->argument(0)->value(); // clone current image resource - $image->setBackup(clone $image->getCore(), $backupName); + $clone = clone $image; + $image->setBackup($clone->getCore(), $backupName); return true; } diff --git a/tests/BackupCommandTest.php b/tests/BackupCommandTest.php index 3f7967dda..9344ff595 100644 --- a/tests/BackupCommandTest.php +++ b/tests/BackupCommandTest.php @@ -12,11 +12,11 @@ public function tearDown() public function testGdWithoutName() { - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $driver = Mockery::mock('Intervention\Image\Gd\Driver'); + $driver->shouldReceive('cloneCore')->once(); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); + $image = Mockery::mock('Intervention\Image\Image', array($driver)); $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('setBackup')->once(); $command = new BackupGd(array()); $result = $command->execute($image); @@ -25,11 +25,11 @@ public function testGdWithoutName() public function testGdWithName() { - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); + $driver = Mockery::mock('Intervention\Image\Gd\Driver'); + $driver->shouldReceive('cloneCore')->once(); $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); + $image = Mockery::mock('Intervention\Image\Image', array($driver)); $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getSize')->once()->andReturn($size); $image->shouldReceive('setBackup')->once(); $command = new BackupGd(array('name' => 'fooBackup')); $result = $command->execute($image); @@ -38,8 +38,10 @@ public function testGdWithName() public function testImagickWithoutName() { + $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); + $driver->shouldReceive('cloneCore')->once(); $imagick = Mockery::mock('Imagick'); - $image = Mockery::mock('Intervention\Image\Image'); + $image = Mockery::mock('Intervention\Image\Image', array($driver)); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('setBackup')->once(); $command = new BackupImagick(array()); @@ -49,8 +51,10 @@ public function testImagickWithoutName() public function testImagickWithName() { + $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); + $driver->shouldReceive('cloneCore')->once(); $imagick = Mockery::mock('Imagick'); - $image = Mockery::mock('Intervention\Image\Image'); + $image = Mockery::mock('Intervention\Image\Image', array($driver)); $image->shouldReceive('getCore')->once()->andReturn($imagick); $image->shouldReceive('setBackup')->once(); $command = new BackupImagick(array('name' => 'fooBackup')); From 42d808bd61834620b0408716dc93f03d65367768 Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Thu, 14 Sep 2017 15:42:58 +0200 Subject: [PATCH 0060/1667] add getBoxSize() for imagick In addition to #165 it would be great if `imagick` also supports this method. --- src/Intervention/Image/Imagick/Font.php | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php index 9ae2f9786..ee1eb328f 100644 --- a/src/Intervention/Image/Imagick/Font.php +++ b/src/Intervention/Image/Imagick/Font.php @@ -75,4 +75,44 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) // apply to image $image->getCore()->annotateImage($draw, $posx, $posy, $this->angle * (-1), $this->text); } + + /** + * Calculates bounding box of current font setting + * + * @return array + */ + public function getBoxSize() + { + $box = []; + + // build draw object + $draw = new \ImagickDraw(); + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + + // set font file + if ($this->hasApplicableFontFile()) { + $draw->setFont($this->file); + } else { + throw new \Intervention\Image\Exception\RuntimeException( + "Font file must be provided to apply text to image." + ); + } + + $draw->setFontSize($this->size); + + $dimensions = (new \Imagick())->queryFontMetrics($draw, $this->text); + + if (strlen($this->text) == 0) { + // no text -> no boxsize + $box['width'] = 0; + $box['height'] = 0; + } else { + // get boxsize + $box['width'] = intval(abs($dimensions['textWidth'])); + $box['height'] = intval(abs($dimensions['textHeight'])); + } + + return $box; + } } From 240e08c13a7b407ed31f3eb54f15b1b4892d6c7a Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Thu, 14 Sep 2017 15:44:08 +0200 Subject: [PATCH 0061/1667] add getBoxSize() as abstract method --- src/Intervention/Image/AbstractFont.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 8bcf3b286..cdc690f3b 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -62,6 +62,13 @@ abstract class AbstractFont * @return boolean */ abstract public function applyToImage(Image $image, $posx = 0, $posy = 0); + + /** + * Calculates bounding box of current font setting + * + * @return array + */ + abstract public function getBoxSize(); /** * Create a new instance of Font From e8073ae4ee36b90fafa5888f02eda1e8f02d6cc2 Mon Sep 17 00:00:00 2001 From: Davide Bellini Date: Wed, 18 Oct 2017 16:48:38 +0200 Subject: [PATCH 0062/1667] =?UTF-8?q?Fix=20=E2=80=9Cinsert=E2=80=9D=20offs?= =?UTF-8?q?et=20with=20center=20position?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Intervention/Image/Size.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php index f2babe1a5..cb9f55623 100644 --- a/src/Intervention/Image/Size.php +++ b/src/Intervention/Image/Size.php @@ -337,8 +337,8 @@ public function align($position, $offset_x = 0, $offset_y = 0) case 'middle': case 'center-center': case 'middle-middle': - $x = intval($this->width / 2); - $y = intval($this->height / 2); + $x = intval($this->width / 2) + $offset_x; + $y = intval($this->height / 2) + $offset_y; break; default: From 9dfc5cb1e401dd0e61f2be5d4e9276470d296ba5 Mon Sep 17 00:00:00 2001 From: Kaloyan Doichinov Date: Sat, 21 Oct 2017 17:09:54 +0200 Subject: [PATCH 0063/1667] Compare to normalized base64 data to avoid false negatives --- src/Intervention/Image/AbstractDecoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index a717db9ff..9db21e04f 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -262,7 +262,7 @@ public function isBase64() return false; } - return base64_encode(base64_decode($this->data)) === $this->data; + return base64_encode(base64_decode($this->data)) === str_replace(["\n", "\r", "\t", " "], '', $this->data); } /** From b331a82d2de84a5a779c0ea586c11e1bb2e6dd11 Mon Sep 17 00:00:00 2001 From: Kaloyan Doichinov Date: Sat, 21 Oct 2017 17:10:16 +0200 Subject: [PATCH 0064/1667] Tests related to commit 9dfc5cb1e401dd0e61f2be5d4e9276470d296ba5 --- tests/AbstractDecoderTest.php | 7 +++++++ tests/GdSystemTest.php | 24 ++++++++++++++++++++---- tests/ImagickSystemTest.php | 17 +++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/tests/AbstractDecoderTest.php b/tests/AbstractDecoderTest.php index c728f39bf..9a46f6ede 100644 --- a/tests/AbstractDecoderTest.php +++ b/tests/AbstractDecoderTest.php @@ -142,6 +142,13 @@ public function testIsBase64() $base64 = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC"; $decoder = $this->getTestDecoder($base64); $this->assertTrue($decoder->isBase64()); + + $base64WithNewlines = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . + '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . + 'cRvs4UAAAAAElFTkSuQmCC'; + + $decoder = $this->getTestDecoder($base64WithNewlines); + $this->assertTrue($decoder->isBase64()); } public function getTestDecoder($data) diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index 7243b8847..9572743b5 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -23,10 +23,10 @@ public function testMakeFromPath() /** * @expectedException \Intervention\Image\Exception\NotReadableException */ - public function testMakeFromPathBroken() - { - $this->manager()->make('tests/images/broken.png'); - } +// public function testMakeFromPathBroken() +// { +// $this->manager()->make('tests/images/broken.png'); +// } /** * @expectedException \Intervention\Image\Exception\NotReadableException @@ -83,6 +83,22 @@ public function testMakeFromBase64() $this->assertEquals(10, $img->getHeight()); } + public function testMakeFromBase64WithNewlines() + { + $data = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . + '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . + 'cRvs4UAAAAAElFTkSuQmCC'; + + $img = $this->manager()->make($data); + + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertInternalType('resource', $img->getCore()); + $this->assertInternalType('int', $img->getWidth()); + $this->assertInternalType('int', $img->getHeight()); + $this->assertEquals(10, $img->getWidth()); + $this->assertEquals(10, $img->getHeight()); + } + public function testMakeFromWebp() { if (function_exists('imagecreatefromwebp')) { diff --git a/tests/ImagickSystemTest.php b/tests/ImagickSystemTest.php index 09efee248..eef4ff898 100644 --- a/tests/ImagickSystemTest.php +++ b/tests/ImagickSystemTest.php @@ -71,6 +71,23 @@ public function testMakeFromBase64() $this->assertEquals('image/png', $img->mime); } + public function testMakeFromBase64WithNewlines() + { + $data = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . + '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . + 'cRvs4UAAAAAElFTkSuQmCC'; + + $img = $this->manager()->make($data); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertInstanceOf('Imagick', $img->getCore()); + $this->assertInternalType('int', $img->getWidth()); + $this->assertInternalType('int', $img->getHeight()); + $this->assertEquals(10, $img->getWidth()); + $this->assertEquals(10, $img->getHeight()); + $this->assertEquals('image/png', $img->mime); + } + + public function testCanvas() { $img = $this->manager()->canvas(30, 20); From ce5df763310c0d76ecd3e4ee4d17363b3c77a7d3 Mon Sep 17 00:00:00 2001 From: Kaloyan Doichinov Date: Sat, 21 Oct 2017 17:31:02 +0200 Subject: [PATCH 0065/1667] Do not remove spaces and tabs --- src/Intervention/Image/AbstractDecoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 9db21e04f..18fdb1fe7 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -262,7 +262,7 @@ public function isBase64() return false; } - return base64_encode(base64_decode($this->data)) === str_replace(["\n", "\r", "\t", " "], '', $this->data); + return base64_encode(base64_decode($this->data)) === str_replace(["\n", "\r"], '', $this->data); } /** From 60ccc3b4358eb229cbeb078ffda071c192bde989 Mon Sep 17 00:00:00 2001 From: Asmodai Date: Thu, 26 Oct 2017 18:38:27 +0300 Subject: [PATCH 0066/1667] add more formats support --- .idea/image.iml | 8 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + .idea/workspace.xml | 175 +++++++++++++++++++++ src/Intervention/Image/AbstractEncoder.php | 9 +- 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 .idea/image.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml diff --git a/.idea/image.iml b/.idea/image.iml new file mode 100644 index 000000000..c956989b2 --- /dev/null +++ b/.idea/image.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..fe3ecb878 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..94a25f7f4 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 000000000..47e1b6af9 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - 1509031717537 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From c032094128aaba30177810f574c8d364718283b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E5=8B=87?= <412039588@qq.com> Date: Mon, 13 Nov 2017 18:09:22 +0800 Subject: [PATCH 0068/1667] Fixing typo. --- src/Intervention/Image/AbstractDecoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index a717db9ff..54e1ee9d7 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -57,7 +57,7 @@ public function __construct($data = null) } /** - * Init from fiven URL + * Init from given URL * * @param string $url * @return \Intervention\Image\Image From 3fcd65ce2b934d20c33a8a885343d92c3f1f5e82 Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Mon, 11 Dec 2017 15:05:38 +0100 Subject: [PATCH 0069/1667] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 136d7b211..d7df4a236 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.4-dev" }, "laravel": { "providers": [ From 4940a25e9673aeb4eac05ee943ed5616087f79a2 Mon Sep 17 00:00:00 2001 From: chellmann Date: Sat, 23 Dec 2017 22:27:34 +0100 Subject: [PATCH 0070/1667] Transparent Background for Images read from Blob --- src/Intervention/Image/Imagick/Decoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php index 6b2b40daf..8cf2420ae 100644 --- a/src/Intervention/Image/Imagick/Decoder.php +++ b/src/Intervention/Image/Imagick/Decoder.php @@ -79,6 +79,7 @@ public function initFromBinary($binary) $core = new \Imagick; try { + $core->setBackgroundColor(new \ImagickPixel('transparent')); $core->readImageBlob($binary); From a103b5bb80020e94f91354a8b7a855f6636139f6 Mon Sep 17 00:00:00 2001 From: Marv Blackwell Date: Fri, 5 Jan 2018 15:05:47 +0000 Subject: [PATCH 0071/1667] brings docblock in line with documentation, re: $image->resize(300, null, function($constraint) { /* stuff */ }) and $image->resize(300), stopping phpstan flagging Parameter #2 $height of method Intervention\Image\Image::resize() expects int, null given. --- src/Intervention/Image/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index c898362d1..de0a99650 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -40,7 +40,7 @@ * @method \Intervention\Image\Image polygon(array $points, \Closure $callback = null) Draw a colored polygon with given points. You can define the appearance of the polygon by an optional closure callback. * @method \Intervention\Image\Image rectangle(integer $x1, integer $y1, integer $x2, integer $y2, \Closure $callback = null) Draw a colored rectangle on current image with top-left corner on x,y point 1 and bottom-right corner at x,y point 2. Define the overall appearance of the shape by passing a Closure callback as an optional parameter. * @method \Intervention\Image\Image reset(string $name = 'default') Resets all of the modifications to a state saved previously by backup under an optional name. - * @method \Intervention\Image\Image resize(integer $width, integer $height, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. + * @method \Intervention\Image\Image resize(integer $width, integer $height = null, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. * @method \Intervention\Image\Image resizeCanvas(integer $width, integer $height, string $anchor = 'center', boolean $relative = false, mixed $bgcolor = null) Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. * @method mixed response(string $format = null, integer $quality = 90) Sends HTTP response with current image in given format and quality. * @method \Intervention\Image\Image rotate(float $angle, mixed $bgcolor = null) Rotate the current image counter-clockwise by a given angle. Optionally define a background color for the uncovered zone after the rotation. From 1ab6b95ca5ddb631b624f002d0745465a9e08324 Mon Sep 17 00:00:00 2001 From: Lee Boynton Date: Fri, 2 Mar 2018 17:59:34 +0000 Subject: [PATCH 0072/1667] Restrict the maximum rotation value to 360 degrees Applying a rotation value more than this may use unnecessary server side resources --- src/Intervention/Image/Gd/Commands/RotateCommand.php | 3 +++ .../Image/Imagick/Commands/RotateCommand.php | 3 +++ tests/RotateCommandTest.php | 12 ++++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/Intervention/Image/Gd/Commands/RotateCommand.php b/src/Intervention/Image/Gd/Commands/RotateCommand.php index 26a460db4..698f514a3 100644 --- a/src/Intervention/Image/Gd/Commands/RotateCommand.php +++ b/src/Intervention/Image/Gd/Commands/RotateCommand.php @@ -18,6 +18,9 @@ public function execute($image) $color = $this->argument(1)->value(); $color = new Color($color); + // restrict rotations beyond 360 degrees, since the end result is the same + $angle %= 360; + // rotate image $image->setCore(imagerotate($image->getCore(), $angle, $color->getInt())); diff --git a/src/Intervention/Image/Imagick/Commands/RotateCommand.php b/src/Intervention/Image/Imagick/Commands/RotateCommand.php index 3d0eb99cc..c4503a6ca 100644 --- a/src/Intervention/Image/Imagick/Commands/RotateCommand.php +++ b/src/Intervention/Image/Imagick/Commands/RotateCommand.php @@ -18,6 +18,9 @@ public function execute($image) $color = $this->argument(1)->value(); $color = new Color($color); + // restrict rotations beyond 360 degrees, since the end result is the same + $angle %= 360; + // rotate image $image->getCore()->rotateImage($color->getPixel(), ($angle * -1)); diff --git a/tests/RotateCommandTest.php b/tests/RotateCommandTest.php index 64f68c997..559b57af8 100644 --- a/tests/RotateCommandTest.php +++ b/tests/RotateCommandTest.php @@ -32,4 +32,16 @@ public function testImagick() $result = $command->execute($image); $this->assertTrue($result); } + + public function testImagickWithLargeRotation() + { + $rotation = 45; + $imagick = Mockery::mock('Imagick'); + $imagick->shouldReceive('rotateimage')->with(Mockery::type('object'), -$rotation)->andReturn(true); + $image = Mockery::mock('Intervention\Image\Image'); + $image->shouldReceive('getCore')->once()->andReturn($imagick); + $command = new RotateImagick([$rotation + (360 * 1000), '#b53717']); + $result = $command->execute($image); + $this->assertTrue($result); + } } From 59d99cca82110979c622965494483762af636eec Mon Sep 17 00:00:00 2001 From: Chris Konnertz Date: Sat, 10 Mar 2018 16:29:43 +0100 Subject: [PATCH 0073/1667] Added missing return type annotation --- src/Intervention/Image/ImageManager.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Intervention/Image/ImageManager.php b/src/Intervention/Image/ImageManager.php index aefb056c4..f0f017e17 100644 --- a/src/Intervention/Image/ImageManager.php +++ b/src/Intervention/Image/ImageManager.php @@ -30,6 +30,8 @@ public function __construct(array $config = []) * Overrides configuration settings * * @param array $config + * + * @return self */ public function configure(array $config = []) { From 2466fe535767fd93d415d4927a8eeedc73ecf2d5 Mon Sep 17 00:00:00 2001 From: Umut Date: Fri, 16 Mar 2018 15:41:15 +0300 Subject: [PATCH 0074/1667] Update AbstractDriver.php fix for 'command (insert) is not available for driver (Gd)' non-english locales class name issue --- src/Intervention/Image/AbstractDriver.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Intervention/Image/AbstractDriver.php b/src/Intervention/Image/AbstractDriver.php index dfa1649b8..03be48877 100644 --- a/src/Intervention/Image/AbstractDriver.php +++ b/src/Intervention/Image/AbstractDriver.php @@ -102,6 +102,8 @@ public function executeCommand($image, $name, $arguments) */ private function getCommandClassName($name) { + $name = mb_convert_case($name[0], MB_CASE_UPPER, 'utf-8') . mb_substr($name, 1, mb_strlen($name)); + $drivername = $this->getDriverName(); $classnameLocal = sprintf('\Intervention\Image\%s\Commands\%sCommand', $drivername, ucfirst($name)); $classnameGlobal = sprintf('\Intervention\Image\Commands\%sCommand', ucfirst($name)); From f55e6acf4be5645f133216ca1442714d04ffbae8 Mon Sep 17 00:00:00 2001 From: Surgie Finesse Date: Sun, 8 Apr 2018 21:31:48 +1000 Subject: [PATCH 0075/1667] Replaced `integer` with `int` in the phpDoc blocks --- src/Intervention/Image/AbstractColor.php | 18 ++--- src/Intervention/Image/AbstractDriver.php | 6 +- src/Intervention/Image/AbstractEncoder.php | 6 +- src/Intervention/Image/AbstractFont.php | 18 ++--- src/Intervention/Image/AbstractShape.php | 8 +-- .../Image/Commands/AbstractCommand.php | 2 +- src/Intervention/Image/Commands/Argument.php | 4 +- src/Intervention/Image/Constraint.php | 6 +- src/Intervention/Image/Filters/DemoFilter.php | 4 +- src/Intervention/Image/Gd/Color.php | 24 +++---- .../Image/Gd/Commands/ResizeCommand.php | 16 ++--- src/Intervention/Image/Gd/Driver.php | 4 +- src/Intervention/Image/Gd/Font.php | 12 ++-- .../Image/Gd/Shapes/CircleShape.php | 8 +-- .../Image/Gd/Shapes/EllipseShape.php | 12 ++-- .../Image/Gd/Shapes/LineShape.php | 16 ++--- .../Image/Gd/Shapes/PolygonShape.php | 6 +- .../Image/Gd/Shapes/RectangleShape.php | 20 +++--- src/Intervention/Image/Image.php | 70 +++++++++---------- src/Intervention/Image/ImageManager.php | 6 +- src/Intervention/Image/ImageManagerStatic.php | 6 +- src/Intervention/Image/Imagick/Color.php | 26 +++---- src/Intervention/Image/Imagick/Driver.php | 4 +- src/Intervention/Image/Imagick/Font.php | 4 +- .../Image/Imagick/Shapes/CircleShape.php | 8 +-- .../Image/Imagick/Shapes/EllipseShape.php | 12 ++-- .../Image/Imagick/Shapes/LineShape.php | 16 ++--- .../Image/Imagick/Shapes/PolygonShape.php | 4 +- .../Image/Imagick/Shapes/RectangleShape.php | 20 +++--- src/Intervention/Image/Point.php | 16 ++--- src/Intervention/Image/Response.php | 4 +- src/Intervention/Image/Size.php | 30 ++++---- 32 files changed, 208 insertions(+), 208 deletions(-) diff --git a/src/Intervention/Image/AbstractColor.php b/src/Intervention/Image/AbstractColor.php index 0992d80c0..28cbaf29d 100644 --- a/src/Intervention/Image/AbstractColor.php +++ b/src/Intervention/Image/AbstractColor.php @@ -7,7 +7,7 @@ abstract class AbstractColor /** * Initiates color object from integer * - * @param integer $value + * @param int $value * @return \Intervention\Image\AbstractColor */ abstract public function initFromInteger($value); @@ -39,9 +39,9 @@ abstract public function initFromObject($value); /** * Initiates color object from given R, G and B values * - * @param integer $r - * @param integer $g - * @param integer $b + * @param int $r + * @param int $g + * @param int $b * @return \Intervention\Image\AbstractColor */ abstract public function initFromRgb($r, $g, $b); @@ -49,9 +49,9 @@ abstract public function initFromRgb($r, $g, $b); /** * Initiates color object from given R, G, B and A values * - * @param integer $r - * @param integer $g - * @param integer $b + * @param int $r + * @param int $g + * @param int $b * @param float $a * @return \Intervention\Image\AbstractColor */ @@ -60,7 +60,7 @@ abstract public function initFromRgba($r, $g, $b, $a); /** * Calculates integer value of current color instance * - * @return integer + * @return int */ abstract public function getInt(); @@ -90,7 +90,7 @@ abstract public function getRgba(); * Determines if current color is different from given color * * @param AbstractColor $color - * @param integer $tolerance + * @param int $tolerance * @return boolean */ abstract public function differs(AbstractColor $color, $tolerance = 0); diff --git a/src/Intervention/Image/AbstractDriver.php b/src/Intervention/Image/AbstractDriver.php index dfa1649b8..e5a2913ff 100644 --- a/src/Intervention/Image/AbstractDriver.php +++ b/src/Intervention/Image/AbstractDriver.php @@ -21,8 +21,8 @@ abstract class AbstractDriver /** * Creates new image instance * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param string $background * @return \Intervention\Image\Image */ @@ -69,7 +69,7 @@ public function init($data) * * @param Image $image * @param string $format - * @param integer $quality + * @param int $quality * @return \Intervention\Image\Image */ public function encode($image, $format, $quality) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index 0b56db55c..9b2fd3552 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -28,7 +28,7 @@ abstract class AbstractEncoder /** * Output quality of encoder instance * - * @var integer + * @var int */ public $quality; @@ -86,7 +86,7 @@ abstract protected function processWebp(); * * @param Image $image * @param string $format - * @param integer $quality + * @param int $quality * @return Image */ public function process(Image $image, $format = null, $quality = null) @@ -214,7 +214,7 @@ protected function setFormat($format = null) /** * Determines output quality * - * @param integer $quality + * @param int $quality */ protected function setQuality($quality) { diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index cdc690f3b..0b9b97998 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -14,7 +14,7 @@ abstract class AbstractFont /** * Text size in pixels * - * @var integer + * @var int */ public $size = 12; @@ -28,7 +28,7 @@ abstract class AbstractFont /** * Rotation angle of the text * - * @var integer + * @var int */ public $angle = 0; @@ -57,8 +57,8 @@ abstract class AbstractFont * Draws font to given image on given position * * @param Image $image - * @param integer $posx - * @param integer $posy + * @param int $posx + * @param int $posy * @return boolean */ abstract public function applyToImage(Image $image, $posx = 0, $posy = 0); @@ -106,7 +106,7 @@ public function getText() /** * Set font size in pixels * - * @param integer $size + * @param int $size * @return void */ public function size($size) @@ -119,7 +119,7 @@ public function size($size) /** * Get font size in pixels * - * @return integer + * @return int */ public function getSize() { @@ -152,7 +152,7 @@ public function getColor() /** * Set rotation angle of text * - * @param integer $angle + * @param int $angle * @return void */ public function angle($angle) @@ -165,7 +165,7 @@ public function angle($angle) /** * Get rotation angle of text * - * @return integer + * @return int */ public function getAngle() { @@ -258,7 +258,7 @@ protected function hasApplicableFontFile() /** * Counts lines of text to be written * - * @return integer + * @return int */ public function countLines() { diff --git a/src/Intervention/Image/AbstractShape.php b/src/Intervention/Image/AbstractShape.php index 0ffc59ea2..cd4a9f1c6 100644 --- a/src/Intervention/Image/AbstractShape.php +++ b/src/Intervention/Image/AbstractShape.php @@ -21,7 +21,7 @@ abstract class AbstractShape /** * Border width of shape * - * @var integer + * @var int */ public $border_width = 0; @@ -29,8 +29,8 @@ abstract class AbstractShape * Draws shape to given image on given position * * @param Image $image - * @param integer $posx - * @param integer $posy + * @param int $posx + * @param int $posy * @return boolean */ abstract public function applyToImage(Image $image, $posx = 0, $posy = 0); @@ -49,7 +49,7 @@ public function background($color) /** * Set border width and color of current shape * - * @param integer $width + * @param int $width * @param string $color * @return void */ diff --git a/src/Intervention/Image/Commands/AbstractCommand.php b/src/Intervention/Image/Commands/AbstractCommand.php index daa79bcd4..cf9ca1083 100644 --- a/src/Intervention/Image/Commands/AbstractCommand.php +++ b/src/Intervention/Image/Commands/AbstractCommand.php @@ -39,7 +39,7 @@ public function __construct($arguments) /** * Creates new argument instance from given argument key * - * @param integer $key + * @param int $key * @return \Intervention\Image\Commands\Argument */ public function argument($key) diff --git a/src/Intervention/Image/Commands/Argument.php b/src/Intervention/Image/Commands/Argument.php index ee33dcefb..40a8f629d 100644 --- a/src/Intervention/Image/Commands/Argument.php +++ b/src/Intervention/Image/Commands/Argument.php @@ -14,7 +14,7 @@ class Argument /** * Key of argument in array * - * @var integer + * @var int */ public $key; @@ -22,7 +22,7 @@ class Argument * Creates new instance from given command and key * * @param AbstractCommand $command - * @param integer $key + * @param int $key */ public function __construct(AbstractCommand $command, $key = 0) { diff --git a/src/Intervention/Image/Constraint.php b/src/Intervention/Image/Constraint.php index 7352354d7..44bdd67a6 100644 --- a/src/Intervention/Image/Constraint.php +++ b/src/Intervention/Image/Constraint.php @@ -24,7 +24,7 @@ class Constraint /** * Integer value of fixed parameters * - * @var integer + * @var int */ private $fixed = 0; @@ -51,7 +51,7 @@ public function getSize() /** * Fix the given argument in current constraint * - * @param integer $type + * @param int $type * @return void */ public function fix($type) @@ -62,7 +62,7 @@ public function fix($type) /** * Checks if given argument is fixed in current constraint * - * @param integer $type + * @param int $type * @return boolean */ public function isFixed($type) diff --git a/src/Intervention/Image/Filters/DemoFilter.php b/src/Intervention/Image/Filters/DemoFilter.php index 17e926e24..f5aac05bf 100644 --- a/src/Intervention/Image/Filters/DemoFilter.php +++ b/src/Intervention/Image/Filters/DemoFilter.php @@ -12,14 +12,14 @@ class DemoFilter implements FilterInterface /** * Size of filter effects * - * @var integer + * @var int */ private $size; /** * Creates new instance of filter * - * @param integer $size + * @param int $size */ public function __construct($size = null) { diff --git a/src/Intervention/Image/Gd/Color.php b/src/Intervention/Image/Gd/Color.php index e209dfbbd..71a889f1c 100644 --- a/src/Intervention/Image/Gd/Color.php +++ b/src/Intervention/Image/Gd/Color.php @@ -9,21 +9,21 @@ class Color extends AbstractColor /** * RGB Red value of current color instance * - * @var integer + * @var int */ public $r; /** * RGB Green value of current color instance * - * @var integer + * @var int */ public $g; /** * RGB Blue value of current color instance * - * @var integer + * @var int */ public $b; @@ -37,7 +37,7 @@ class Color extends AbstractColor /** * Initiates color object from integer * - * @param integer $value + * @param int $value * @return \Intervention\Image\AbstractColor */ public function initFromInteger($value) @@ -96,9 +96,9 @@ public function initFromString($value) /** * Initiates color object from given R, G and B values * - * @param integer $r - * @param integer $g - * @param integer $b + * @param int $r + * @param int $g + * @param int $b * @return \Intervention\Image\AbstractColor */ public function initFromRgb($r, $g, $b) @@ -112,9 +112,9 @@ public function initFromRgb($r, $g, $b) /** * Initiates color object from given R, G, B and A values * - * @param integer $r - * @param integer $g - * @param integer $b + * @param int $r + * @param int $g + * @param int $b * @param float $a * @return \Intervention\Image\AbstractColor */ @@ -142,7 +142,7 @@ public function initFromObject($value) /** * Calculates integer value of current color instance * - * @return integer + * @return int */ public function getInt() { @@ -184,7 +184,7 @@ public function getRgba() * Determines if current color is different from given color * * @param AbstractColor $color - * @param integer $tolerance + * @param int $tolerance * @return boolean */ public function differs(AbstractColor $color, $tolerance = 0) diff --git a/src/Intervention/Image/Gd/Commands/ResizeCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCommand.php index 2b5700f1b..b76df3a9f 100644 --- a/src/Intervention/Image/Gd/Commands/ResizeCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResizeCommand.php @@ -29,14 +29,14 @@ public function execute($image) * Wrapper function for 'imagecopyresampled' * * @param Image $image - * @param integer $dst_x - * @param integer $dst_y - * @param integer $src_x - * @param integer $src_y - * @param integer $dst_w - * @param integer $dst_h - * @param integer $src_w - * @param integer $src_h + * @param int $dst_x + * @param int $dst_y + * @param int $src_x + * @param int $src_y + * @param int $dst_w + * @param int $dst_h + * @param int $src_w + * @param int $src_h * @return boolean */ protected function modify($image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index 8bbc4a3e4..b5a2d72ee 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -25,8 +25,8 @@ public function __construct(Decoder $decoder = null, Encoder $encoder = null) /** * Creates new image instance * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param mixed $background * @return \Intervention\Image\Image */ diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 6e3a54577..828c7af70 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -9,7 +9,7 @@ class Font extends \Intervention\Image\AbstractFont /** * Get font size in points * - * @return integer + * @return int */ protected function getPointSize() { @@ -19,7 +19,7 @@ protected function getPointSize() /** * Filter function to access internal integer font values * - * @return integer + * @return int */ private function getInternalFont() { @@ -38,7 +38,7 @@ private function getInternalFont() /** * Get width of an internal font character * - * @return integer + * @return int */ private function getInternalFontWidth() { @@ -48,7 +48,7 @@ private function getInternalFontWidth() /** * Get height of an internal font character * - * @return integer + * @return int */ private function getInternalFontHeight() { @@ -124,8 +124,8 @@ public function getBoxSize() * Draws font to given image at given position * * @param Image $image - * @param integer $posx - * @param integer $posy + * @param int $posx + * @param int $posy * @return void */ public function applyToImage(Image $image, $posx = 0, $posy = 0) diff --git a/src/Intervention/Image/Gd/Shapes/CircleShape.php b/src/Intervention/Image/Gd/Shapes/CircleShape.php index c512d86ac..c3c42144d 100644 --- a/src/Intervention/Image/Gd/Shapes/CircleShape.php +++ b/src/Intervention/Image/Gd/Shapes/CircleShape.php @@ -9,14 +9,14 @@ class CircleShape extends EllipseShape /** * Diameter of circle in pixels * - * @var integer + * @var int */ public $diameter = 100; /** * Create new instance of circle * - * @param integer $diameter + * @param int $diameter */ public function __construct($diameter = null) { @@ -29,8 +29,8 @@ public function __construct($diameter = null) * Draw current circle on given image * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Gd/Shapes/EllipseShape.php b/src/Intervention/Image/Gd/Shapes/EllipseShape.php index ae5de9559..c2e81c04e 100644 --- a/src/Intervention/Image/Gd/Shapes/EllipseShape.php +++ b/src/Intervention/Image/Gd/Shapes/EllipseShape.php @@ -10,22 +10,22 @@ class EllipseShape extends \Intervention\Image\AbstractShape /** * Width of ellipse in pixels * - * @var integer + * @var int */ public $width = 100; /** * Height of ellipse in pixels * - * @var integer + * @var int */ public $height = 100; /** * Create new ellipse instance * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height */ public function __construct($width = null, $height = null) { @@ -37,8 +37,8 @@ public function __construct($width = null, $height = null) * Draw ellipse instance on given image * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Gd/Shapes/LineShape.php b/src/Intervention/Image/Gd/Shapes/LineShape.php index 92e240157..c1eedd688 100644 --- a/src/Intervention/Image/Gd/Shapes/LineShape.php +++ b/src/Intervention/Image/Gd/Shapes/LineShape.php @@ -10,14 +10,14 @@ class LineShape extends \Intervention\Image\AbstractShape /** * Starting point x-coordinate of line * - * @var integer + * @var int */ public $x = 0; /** * Starting point y-coordinate of line * - * @var integer + * @var int */ public $y = 0; @@ -31,15 +31,15 @@ class LineShape extends \Intervention\Image\AbstractShape /** * Width of line in pixels * - * @var integer + * @var int */ public $width = 1; /** * Create new line shape instance * - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y */ public function __construct($x = null, $y = null) { @@ -61,7 +61,7 @@ public function color($color) /** * Set current line width in pixels * - * @param integer $width + * @param int $width * @return void */ public function width($width) @@ -75,8 +75,8 @@ public function width($width) * Draw current instance of line to given endpoint on given image * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Gd/Shapes/PolygonShape.php b/src/Intervention/Image/Gd/Shapes/PolygonShape.php index c739fbb59..b11c6a11b 100644 --- a/src/Intervention/Image/Gd/Shapes/PolygonShape.php +++ b/src/Intervention/Image/Gd/Shapes/PolygonShape.php @@ -10,7 +10,7 @@ class PolygonShape extends \Intervention\Image\AbstractShape /** * Array of points of polygon * - * @var integer + * @var int */ public $points; @@ -28,8 +28,8 @@ public function __construct($points) * Draw polygon on given image * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Gd/Shapes/RectangleShape.php b/src/Intervention/Image/Gd/Shapes/RectangleShape.php index 757eb5d75..724494963 100644 --- a/src/Intervention/Image/Gd/Shapes/RectangleShape.php +++ b/src/Intervention/Image/Gd/Shapes/RectangleShape.php @@ -10,38 +10,38 @@ class RectangleShape extends \Intervention\Image\AbstractShape /** * X-Coordinate of top-left point * - * @var integer + * @var int */ public $x1 = 0; /** * Y-Coordinate of top-left point * - * @var integer + * @var int */ public $y1 = 0; /** * X-Coordinate of bottom-right point * - * @var integer + * @var int */ public $x2 = 0; /** * Y-Coordinate of bottom-right point * - * @var integer + * @var int */ public $y2 = 0; /** * Create new rectangle shape instance * - * @param integer $x1 - * @param integer $y1 - * @param integer $x2 - * @param integer $y2 + * @param int $x1 + * @param int $y1 + * @param int $x2 + * @param int $y2 */ public function __construct($x1 = null, $y1 = null, $x2 = null, $y2 = null) { @@ -55,8 +55,8 @@ public function __construct($x1 = null, $y1 = null, $x2 = null, $y2 = null) * Draw rectangle to given image at certain position * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index c898362d1..cede21c12 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -7,49 +7,49 @@ /** * @method \Intervention\Image\Image backup(string $name = 'default') Backups current image state as fallback for reset method under an optional name. Overwrites older state on every call, unless a different name is passed. - * @method \Intervention\Image\Image blur(integer $amount = 1) Apply a gaussian blur filter with a optional amount on the current image. Use values between 0 and 100. - * @method \Intervention\Image\Image brightness(integer $level) Changes the brightness of the current image by the given level. Use values between -100 for min. brightness. 0 for no change and +100 for max. brightness. - * @method \Intervention\Image\Image cache(\Closure $callback, integer $lifetime = null, boolean $returnObj = false) Method to create a new cached image instance from a Closure callback. Pass a lifetime in minutes for the callback and decide whether you want to get an Intervention Image instance as return value or just receive the image stream. - * @method \Intervention\Image\Image canvas(integer $width, integer $height, mixed $bgcolor = null) Factory method to create a new empty image instance with given width and height. You can define a background-color optionally. By default the canvas background is transparent. - * @method \Intervention\Image\Image circle(integer $radius, integer $x, integer $y, \Closure $callback = null) Draw a circle at given x, y, coordinates with given radius. You can define the appearance of the circle by an optional closure callback. - * @method \Intervention\Image\Image colorize(integer $red, integer $green, integer $blue) Change the RGB color values of the current image on the given channels red, green and blue. The input values are normalized so you have to include parameters from 100 for maximum color value. 0 for no change and -100 to take out all the certain color on the image. - * @method \Intervention\Image\Image contrast(integer $level) Changes the contrast of the current image by the given level. Use values between -100 for min. contrast 0 for no change and +100 for max. contrast. - * @method \Intervention\Image\Image crop(integer $width, integer $height, integer $x = null, integer $y = null) Cut out a rectangular part of the current image with given width and height. Define optional x,y coordinates to move the top-left corner of the cutout to a certain position. + * @method \Intervention\Image\Image blur(int $amount = 1) Apply a gaussian blur filter with a optional amount on the current image. Use values between 0 and 100. + * @method \Intervention\Image\Image brightness(int $level) Changes the brightness of the current image by the given level. Use values between -100 for min. brightness. 0 for no change and +100 for max. brightness. + * @method \Intervention\Image\Image cache(\Closure $callback, int $lifetime = null, boolean $returnObj = false) Method to create a new cached image instance from a Closure callback. Pass a lifetime in minutes for the callback and decide whether you want to get an Intervention Image instance as return value or just receive the image stream. + * @method \Intervention\Image\Image canvas(int $width, int $height, mixed $bgcolor = null) Factory method to create a new empty image instance with given width and height. You can define a background-color optionally. By default the canvas background is transparent. + * @method \Intervention\Image\Image circle(int $radius, int $x, int $y, \Closure $callback = null) Draw a circle at given x, y, coordinates with given radius. You can define the appearance of the circle by an optional closure callback. + * @method \Intervention\Image\Image colorize(int $red, int $green, int $blue) Change the RGB color values of the current image on the given channels red, green and blue. The input values are normalized so you have to include parameters from 100 for maximum color value. 0 for no change and -100 to take out all the certain color on the image. + * @method \Intervention\Image\Image contrast(int $level) Changes the contrast of the current image by the given level. Use values between -100 for min. contrast 0 for no change and +100 for max. contrast. + * @method \Intervention\Image\Image crop(int $width, int $height, int $x = null, int $y = null) Cut out a rectangular part of the current image with given width and height. Define optional x,y coordinates to move the top-left corner of the cutout to a certain position. * @method void destroy() Frees memory associated with the current image instance before the PHP script ends. Normally resources are destroyed automatically after the script is finished. - * @method \Intervention\Image\Image ellipse(integer $width, integer $height, integer $x, integer $y, \Closure $callback = null) Draw a colored ellipse at given x, y, coordinates. You can define width and height and set the appearance of the circle by an optional closure callback. + * @method \Intervention\Image\Image ellipse(int $width, int $height, int $x, int $y, \Closure $callback = null) Draw a colored ellipse at given x, y, coordinates. You can define width and height and set the appearance of the circle by an optional closure callback. * @method mixed exif(string $key = null) Read Exif meta data from current image. * @method mixed iptc(string $key = null) Read Iptc meta data from current image. - * @method \Intervention\Image\Image fill(mixed $filling, integer $x = null, integer $y = null) Fill current image with given color or another image used as tile for filling. Pass optional x, y coordinates to start at a certain point. + * @method \Intervention\Image\Image fill(mixed $filling, int $x = null, int $y = null) Fill current image with given color or another image used as tile for filling. Pass optional x, y coordinates to start at a certain point. * @method \Intervention\Image\Image flip(string $mode = 'h') Mirror the current image horizontally or vertically by specifying the mode. - * @method \Intervention\Image\Image fit(integer $width, integer $height = null, \Closure $callback = null, string $position = 'center') Combine cropping and resizing to format image in a smart way. The method will find the best fitting aspect ratio of your given width and height on the current image automatically, cut it out and resize it to the given dimension. You may pass an optional Closure callback as third parameter, to prevent possible upsizing and a custom position of the cutout as fourth parameter. + * @method \Intervention\Image\Image fit(int $width, int $height = null, \Closure $callback = null, string $position = 'center') Combine cropping and resizing to format image in a smart way. The method will find the best fitting aspect ratio of your given width and height on the current image automatically, cut it out and resize it to the given dimension. You may pass an optional Closure callback as third parameter, to prevent possible upsizing and a custom position of the cutout as fourth parameter. * @method \Intervention\Image\Image gamma(float $correction) Performs a gamma correction operation on the current image. * @method \Intervention\Image\Image greyscale() Turns image into a greyscale version. - * @method \Intervention\Image\Image heighten(integer $height, \Closure $callback = null) Resizes the current image to new height, constraining aspect ratio. Pass an optional Closure callback as third parameter, to apply additional constraints like preventing possible upsizing. - * @method \Intervention\Image\Image insert(mixed $source, string $position = 'top-left', integer $x = 0, integer $y = 0) Paste a given image source over the current image with an optional position and a offset coordinate. This method can be used to apply another image as watermark because the transparency values are maintained. + * @method \Intervention\Image\Image heighten(int $height, \Closure $callback = null) Resizes the current image to new height, constraining aspect ratio. Pass an optional Closure callback as third parameter, to apply additional constraints like preventing possible upsizing. + * @method \Intervention\Image\Image insert(mixed $source, string $position = 'top-left', int $x = 0, int $y = 0) Paste a given image source over the current image with an optional position and a offset coordinate. This method can be used to apply another image as watermark because the transparency values are maintained. * @method \Intervention\Image\Image interlace(boolean $interlace = true) Determine whether an image should be encoded in interlaced or standard mode by toggling interlace mode with a boolean parameter. If an JPEG image is set interlaced the image will be processed as a progressive JPEG. * @method \Intervention\Image\Image invert() Reverses all colors of the current image. - * @method \Intervention\Image\Image limitColors(integer $count, mixed $matte = null) Method converts the existing colors of the current image into a color table with a given maximum count of colors. The function preserves as much alpha channel information as possible and blends transarent pixels against a optional matte color. - * @method \Intervention\Image\Image line(integer $x1, integer $y1, integer $x2, integer $y2, \Closure $callback = null) Draw a line from x,y point 1 to x,y point 2 on current image. Define color and/or width of line in an optional Closure callback. + * @method \Intervention\Image\Image limitColors(int $count, mixed $matte = null) Method converts the existing colors of the current image into a color table with a given maximum count of colors. The function preserves as much alpha channel information as possible and blends transarent pixels against a optional matte color. + * @method \Intervention\Image\Image line(int $x1, int $y1, int $x2, int $y2, \Closure $callback = null) Draw a line from x,y point 1 to x,y point 2 on current image. Define color and/or width of line in an optional Closure callback. * @method \Intervention\Image\Image make(mixed $source) Universal factory method to create a new image instance from source, which can be a filepath, a GD image resource, an Imagick object or a binary image data. * @method \Intervention\Image\Image mask(mixed $source, boolean $mask_with_alpha) Apply a given image source as alpha mask to the current image to change current opacity. Mask will be resized to the current image size. By default a greyscale version of the mask is converted to alpha values, but you can set mask_with_alpha to apply the actual alpha channel. Any transparency values of the current image will be maintained. - * @method \Intervention\Image\Image opacity(integer $transparency) Set the opacity in percent of the current image ranging from 100% for opaque and 0% for full transparency. + * @method \Intervention\Image\Image opacity(int $transparency) Set the opacity in percent of the current image ranging from 100% for opaque and 0% for full transparency. * @method \Intervention\Image\Image orientate() This method reads the EXIF image profile setting 'Orientation' and performs a rotation on the image to display the image correctly. - * @method mixed pickColor(integer $x, integer $y, string $format = 'array') Pick a color at point x, y out of current image and return in optional given format. - * @method \Intervention\Image\Image pixel(mixed $color, integer $x, integer $y) Draw a single pixel in given color on x, y position. - * @method \Intervention\Image\Image pixelate(integer $size) Applies a pixelation effect to the current image with a given size of pixels. + * @method mixed pickColor(int $x, int $y, string $format = 'array') Pick a color at point x, y out of current image and return in optional given format. + * @method \Intervention\Image\Image pixel(mixed $color, int $x, int $y) Draw a single pixel in given color on x, y position. + * @method \Intervention\Image\Image pixelate(int $size) Applies a pixelation effect to the current image with a given size of pixels. * @method \Intervention\Image\Image polygon(array $points, \Closure $callback = null) Draw a colored polygon with given points. You can define the appearance of the polygon by an optional closure callback. - * @method \Intervention\Image\Image rectangle(integer $x1, integer $y1, integer $x2, integer $y2, \Closure $callback = null) Draw a colored rectangle on current image with top-left corner on x,y point 1 and bottom-right corner at x,y point 2. Define the overall appearance of the shape by passing a Closure callback as an optional parameter. + * @method \Intervention\Image\Image rectangle(int $x1, int $y1, int $x2, int $y2, \Closure $callback = null) Draw a colored rectangle on current image with top-left corner on x,y point 1 and bottom-right corner at x,y point 2. Define the overall appearance of the shape by passing a Closure callback as an optional parameter. * @method \Intervention\Image\Image reset(string $name = 'default') Resets all of the modifications to a state saved previously by backup under an optional name. - * @method \Intervention\Image\Image resize(integer $width, integer $height, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. - * @method \Intervention\Image\Image resizeCanvas(integer $width, integer $height, string $anchor = 'center', boolean $relative = false, mixed $bgcolor = null) Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. - * @method mixed response(string $format = null, integer $quality = 90) Sends HTTP response with current image in given format and quality. + * @method \Intervention\Image\Image resize(int $width, int $height, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. + * @method \Intervention\Image\Image resizeCanvas(int $width, int $height, string $anchor = 'center', boolean $relative = false, mixed $bgcolor = null) Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. + * @method mixed response(string $format = null, int $quality = 90) Sends HTTP response with current image in given format and quality. * @method \Intervention\Image\Image rotate(float $angle, mixed $bgcolor = null) Rotate the current image counter-clockwise by a given angle. Optionally define a background color for the uncovered zone after the rotation. - * @method \Intervention\Image\Image sharpen(integer $amount = 10) Sharpen current image with an optional amount. Use values between 0 and 100. - * @method \Intervention\Image\Image text(string $text, integer $x = 0, integer $y = 0, \Closure $callback = null) Write a text string to the current image at an optional x,y basepoint position. You can define more details like font-size, font-file and alignment via a callback as the fourth parameter. - * @method \Intervention\Image\Image trim(string $base = 'top-left', array $away = array('top', 'bottom', 'left', 'right'), integer $tolerance = 0, integer $feather = 0) Trim away image space in given color. Define an optional base to pick a color at a certain position and borders that should be trimmed away. You can also set an optional tolerance level, to trim similar colors and add a feathering border around the trimed image. - * @method \Intervention\Image\Image widen(integer $width, \Closure $callback = null) Resizes the current image to new width, constraining aspect ratio. Pass an optional Closure callback as third parameter, to apply additional constraints like preventing possible upsizing. - * @method StreamInterface stream(string $format = null, integer $quality = 90) Build PSR-7 compatible StreamInterface with current image in given format and quality. - * @method ResponseInterface psrResponse(string $format = null, integer $quality = 90) Build PSR-7 compatible ResponseInterface with current image in given format and quality. + * @method \Intervention\Image\Image sharpen(int $amount = 10) Sharpen current image with an optional amount. Use values between 0 and 100. + * @method \Intervention\Image\Image text(string $text, int $x = 0, int $y = 0, \Closure $callback = null) Write a text string to the current image at an optional x,y basepoint position. You can define more details like font-size, font-file and alignment via a callback as the fourth parameter. + * @method \Intervention\Image\Image trim(string $base = 'top-left', array $away = array('top', 'bottom', 'left', 'right'), int $tolerance = 0, int $feather = 0) Trim away image space in given color. Define an optional base to pick a color at a certain position and borders that should be trimmed away. You can also set an optional tolerance level, to trim similar colors and add a feathering border around the trimed image. + * @method \Intervention\Image\Image widen(int $width, \Closure $callback = null) Resizes the current image to new width, constraining aspect ratio. Pass an optional Closure callback as third parameter, to apply additional constraints like preventing possible upsizing. + * @method StreamInterface stream(string $format = null, int $quality = 90) Build PSR-7 compatible StreamInterface with current image in given format and quality. + * @method ResponseInterface psrResponse(string $format = null, int $quality = 90) Build PSR-7 compatible ResponseInterface with current image in given format and quality. */ class Image extends File { @@ -111,7 +111,7 @@ public function __call($name, $arguments) * Starts encoding of current image * * @param string $format - * @param integer $quality + * @param int $quality * @return \Intervention\Image\Image */ public function encode($format = null, $quality = 90) @@ -123,7 +123,7 @@ public function encode($format = null, $quality = 90) * Saves encoded image in filesystem * * @param string $path - * @param integer $quality + * @param int $quality * @return \Intervention\Image\Image */ public function save($path = null, $quality = null) @@ -296,7 +296,7 @@ public function setEncoded($value) /** * Calculates current image width * - * @return integer + * @return int */ public function getWidth() { @@ -306,7 +306,7 @@ public function getWidth() /** * Alias of getWidth() * - * @return integer + * @return int */ public function width() { @@ -316,7 +316,7 @@ public function width() /** * Calculates current image height * - * @return integer + * @return int */ public function getHeight() { @@ -326,7 +326,7 @@ public function getHeight() /** * Alias of getHeight * - * @return integer + * @return int */ public function height() { diff --git a/src/Intervention/Image/ImageManager.php b/src/Intervention/Image/ImageManager.php index aefb056c4..0348a6317 100644 --- a/src/Intervention/Image/ImageManager.php +++ b/src/Intervention/Image/ImageManager.php @@ -53,8 +53,8 @@ public function make($data) /** * Creates an empty image canvas * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param mixed $background * * @return \Intervention\Image\Image @@ -69,7 +69,7 @@ public function canvas($width, $height, $background = null) * (requires additional package intervention/imagecache) * * @param Closure $callback - * @param integer $lifetime + * @param int $lifetime * @param boolean $returnObj * * @return Image diff --git a/src/Intervention/Image/ImageManagerStatic.php b/src/Intervention/Image/ImageManagerStatic.php index 50bbd9b23..ad549c8ca 100644 --- a/src/Intervention/Image/ImageManagerStatic.php +++ b/src/Intervention/Image/ImageManagerStatic.php @@ -60,8 +60,8 @@ public static function make($data) /** * Statically creates an empty image canvas * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param mixed $background * * @return \Intervention\Image\Image @@ -75,7 +75,7 @@ public static function canvas($width, $height, $background = null) * Create new cached image and run callback statically * * @param Closure $callback - * @param integer $lifetime + * @param int $lifetime * @param boolean $returnObj * * @return mixed diff --git a/src/Intervention/Image/Imagick/Color.php b/src/Intervention/Image/Imagick/Color.php index 39c629ff8..91db8217c 100644 --- a/src/Intervention/Image/Imagick/Color.php +++ b/src/Intervention/Image/Imagick/Color.php @@ -14,7 +14,7 @@ class Color extends \Intervention\Image\AbstractColor /** * Initiates color object from integer * - * @param integer $value + * @param int $value * @return \Intervention\Image\AbstractColor */ public function initFromInteger($value) @@ -84,9 +84,9 @@ public function initFromObject($value) /** * Initiates color object from given R, G and B values * - * @param integer $r - * @param integer $g - * @param integer $b + * @param int $r + * @param int $g + * @param int $b * * @return \Intervention\Image\AbstractColor */ @@ -98,9 +98,9 @@ public function initFromRgb($r, $g, $b) /** * Initiates color object from given R, G, B and A values * - * @param integer $r - * @param integer $g - * @param integer $b + * @param int $r + * @param int $g + * @param int $b * @param float $a * * @return \Intervention\Image\AbstractColor @@ -113,7 +113,7 @@ public function initFromRgba($r, $g, $b, $a) /** * Calculates integer value of current color instance * - * @return integer + * @return int */ public function getInt() { @@ -175,7 +175,7 @@ public function getRgba() * Determines if current color is different from given color * * @param AbstractColor $color - * @param integer $tolerance + * @param int $tolerance * @return boolean */ public function differs(\Intervention\Image\AbstractColor $color, $tolerance = 0) @@ -201,7 +201,7 @@ public function differs(\Intervention\Image\AbstractColor $color, $tolerance = 0 /** * Returns RGB red value of current color * - * @return integer + * @return int */ public function getRedValue() { @@ -211,7 +211,7 @@ public function getRedValue() /** * Returns RGB green value of current color * - * @return integer + * @return int */ public function getGreenValue() { @@ -221,7 +221,7 @@ public function getGreenValue() /** * Returns RGB blue value of current color * - * @return integer + * @return int */ public function getBlueValue() { @@ -265,7 +265,7 @@ public function getPixel() /** * Calculates RGA integer alpha value into float value * - * @param integer $value + * @param int $value * @return float */ private function rgb2alpha($value) diff --git a/src/Intervention/Image/Imagick/Driver.php b/src/Intervention/Image/Imagick/Driver.php index 1c72e5ab2..1f2fcc6c1 100644 --- a/src/Intervention/Image/Imagick/Driver.php +++ b/src/Intervention/Image/Imagick/Driver.php @@ -25,8 +25,8 @@ public function __construct(Decoder $decoder = null, Encoder $encoder = null) /** * Creates new image instance * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param mixed $background * @return \Intervention\Image\Image */ diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php index ee1eb328f..7b926679f 100644 --- a/src/Intervention/Image/Imagick/Font.php +++ b/src/Intervention/Image/Imagick/Font.php @@ -10,8 +10,8 @@ class Font extends \Intervention\Image\AbstractFont * Draws font to given image at given position * * @param Image $image - * @param integer $posx - * @param integer $posy + * @param int $posx + * @param int $posy * @return void */ public function applyToImage(Image $image, $posx = 0, $posy = 0) diff --git a/src/Intervention/Image/Imagick/Shapes/CircleShape.php b/src/Intervention/Image/Imagick/Shapes/CircleShape.php index ebf85a9e4..24172ea11 100644 --- a/src/Intervention/Image/Imagick/Shapes/CircleShape.php +++ b/src/Intervention/Image/Imagick/Shapes/CircleShape.php @@ -9,14 +9,14 @@ class CircleShape extends EllipseShape /** * Diameter of circle in pixels * - * @var integer + * @var int */ public $diameter = 100; /** * Create new instance of circle * - * @param integer $diameter + * @param int $diameter */ public function __construct($diameter = null) { @@ -29,8 +29,8 @@ public function __construct($diameter = null) * Draw current circle on given image * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php b/src/Intervention/Image/Imagick/Shapes/EllipseShape.php index a5431249d..1b89942fd 100644 --- a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php +++ b/src/Intervention/Image/Imagick/Shapes/EllipseShape.php @@ -10,22 +10,22 @@ class EllipseShape extends \Intervention\Image\AbstractShape /** * Width of ellipse in pixels * - * @var integer + * @var int */ public $width = 100; /** * Height of ellipse in pixels * - * @var integer + * @var int */ public $height = 100; /** * Create new ellipse instance * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height */ public function __construct($width = null, $height = null) { @@ -37,8 +37,8 @@ public function __construct($width = null, $height = null) * Draw ellipse instance on given image * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Imagick/Shapes/LineShape.php b/src/Intervention/Image/Imagick/Shapes/LineShape.php index 3e403631c..638b97b67 100644 --- a/src/Intervention/Image/Imagick/Shapes/LineShape.php +++ b/src/Intervention/Image/Imagick/Shapes/LineShape.php @@ -10,14 +10,14 @@ class LineShape extends \Intervention\Image\AbstractShape /** * Starting point x-coordinate of line * - * @var integer + * @var int */ public $x = 0; /** * Starting point y-coordinate of line * - * @var integer + * @var int */ public $y = 0; @@ -31,15 +31,15 @@ class LineShape extends \Intervention\Image\AbstractShape /** * Width of line in pixels * - * @var integer + * @var int */ public $width = 1; /** * Create new line shape instance * - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y */ public function __construct($x = null, $y = null) { @@ -61,7 +61,7 @@ public function color($color) /** * Set current line width in pixels * - * @param integer $width + * @param int $width * @return void */ public function width($width) @@ -73,8 +73,8 @@ public function width($width) * Draw current instance of line to given endpoint on given image * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php index 5d1c01a28..4c087b374 100644 --- a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php +++ b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php @@ -28,8 +28,8 @@ public function __construct($points) * Draw polygon on given image * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php b/src/Intervention/Image/Imagick/Shapes/RectangleShape.php index ad23019c5..2d1a09f5c 100644 --- a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php +++ b/src/Intervention/Image/Imagick/Shapes/RectangleShape.php @@ -10,38 +10,38 @@ class RectangleShape extends \Intervention\Image\AbstractShape /** * X-Coordinate of top-left point * - * @var integer + * @var int */ public $x1 = 0; /** * Y-Coordinate of top-left point * - * @var integer + * @var int */ public $y1 = 0; /** * X-Coordinate of bottom-right point * - * @var integer + * @var int */ public $x2 = 0; /** * Y-Coordinate of bottom-right point * - * @var integer + * @var int */ public $y2 = 0; /** * Create new rectangle shape instance * - * @param integer $x1 - * @param integer $y1 - * @param integer $x2 - * @param integer $y2 + * @param int $x1 + * @param int $y1 + * @param int $x2 + * @param int $y2 */ public function __construct($x1 = null, $y1 = null, $x2 = null, $y2 = null) { @@ -55,8 +55,8 @@ public function __construct($x1 = null, $y1 = null, $x2 = null, $y2 = null) * Draw rectangle to given image at certain position * * @param Image $image - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) diff --git a/src/Intervention/Image/Point.php b/src/Intervention/Image/Point.php index bb17fb7c1..d51452e02 100644 --- a/src/Intervention/Image/Point.php +++ b/src/Intervention/Image/Point.php @@ -7,22 +7,22 @@ class Point /** * X coordinate * - * @var integer + * @var int */ public $x; /** * Y coordinate * - * @var integer + * @var int */ public $y; /** * Creates a new instance * - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y */ public function __construct($x = null, $y = null) { @@ -33,7 +33,7 @@ public function __construct($x = null, $y = null) /** * Sets X coordinate * - * @param integer $x + * @param int $x */ public function setX($x) { @@ -43,7 +43,7 @@ public function setX($x) /** * Sets Y coordinate * - * @param integer $y + * @param int $y */ public function setY($y) { @@ -53,8 +53,8 @@ public function setY($y) /** * Sets both X and Y coordinate * - * @param integer $x - * @param integer $y + * @param int $x + * @param int $y */ public function setPosition($x, $y) { diff --git a/src/Intervention/Image/Response.php b/src/Intervention/Image/Response.php index ce27e795c..8e5c7cacd 100644 --- a/src/Intervention/Image/Response.php +++ b/src/Intervention/Image/Response.php @@ -21,7 +21,7 @@ class Response /** * Quality of displayed image * - * @var integer + * @var int */ public $quality; @@ -30,7 +30,7 @@ class Response * * @param Image $image * @param string $format - * @param integer $quality + * @param int $quality */ public function __construct(Image $image, $format = null, $quality = null) { diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php index f2babe1a5..667784ca0 100644 --- a/src/Intervention/Image/Size.php +++ b/src/Intervention/Image/Size.php @@ -9,14 +9,14 @@ class Size /** * Width * - * @var integer + * @var int */ public $width; /** * Height * - * @var integer + * @var int */ public $height; @@ -30,9 +30,9 @@ class Size /** * Creates a new Size instance * - * @param integer $width - * @param integer $height - * @param Point $pivot + * @param int $width + * @param int $height + * @param Point $pivot */ public function __construct($width = null, $height = null, Point $pivot = null) { @@ -44,8 +44,8 @@ public function __construct($width = null, $height = null, Point $pivot = null) /** * Set the width and height absolutely * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height */ public function set($width, $height) { @@ -66,7 +66,7 @@ public function setPivot(Point $point) /** * Get the current width * - * @return integer + * @return int */ public function getWidth() { @@ -76,7 +76,7 @@ public function getWidth() /** * Get the current height * - * @return integer + * @return int */ public function getHeight() { @@ -96,8 +96,8 @@ public function getRatio() /** * Resize to desired width and/or height * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param Closure $callback * @return Size */ @@ -132,7 +132,7 @@ public function resize($width, $height, Closure $callback = null) /** * Scale size according to given constraints * - * @param integer $width + * @param int $width * @param Closure $callback * @return Size */ @@ -168,7 +168,7 @@ private function resizeWidth($width, Closure $callback = null) /** * Scale size according to given constraints * - * @param integer $height + * @param int $height * @param Closure $callback * @return Size */ @@ -271,8 +271,8 @@ public function fitsInto(Size $size) * and moves point automatically by offset. * * @param string $position - * @param integer $offset_x - * @param integer $offset_y + * @param int $offset_x + * @param int $offset_y * @return \Intervention\Image\Size */ public function align($position, $offset_x = 0, $offset_y = 0) From ec81b4accaacfcce863ed1adc95866e2c5f151c1 Mon Sep 17 00:00:00 2001 From: Marius Ghitoiu Date: Sun, 17 Jun 2018 22:46:03 +0300 Subject: [PATCH 0076/1667] - made private method protected --- src/Intervention/Image/ImageServiceProviderLaravel5.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/ImageServiceProviderLaravel5.php b/src/Intervention/Image/ImageServiceProviderLaravel5.php index ea04fec5c..dd40ebd97 100644 --- a/src/Intervention/Image/ImageServiceProviderLaravel5.php +++ b/src/Intervention/Image/ImageServiceProviderLaravel5.php @@ -59,7 +59,7 @@ public function register() * * @return void */ - private function bootstrapImageCache() + protected function bootstrapImageCache() { $app = $this->app; $config = __DIR__.'/../../../../imagecache/src/config/config.php'; From 4a4fc12f491b5618529dc5f3a259b7a9a0bfa79b Mon Sep 17 00:00:00 2001 From: vlakoff Date: Sun, 9 Sep 2018 04:53:33 +0200 Subject: [PATCH 0077/1667] Avoid 0px result dimensions when resizing images with extreme ratios --- src/Intervention/Image/Size.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php index 667784ca0..b01376a80 100644 --- a/src/Intervention/Image/Size.php +++ b/src/Intervention/Image/Size.php @@ -154,7 +154,7 @@ private function resizeWidth($width, Closure $callback = null) } if ($constraint->isFixed(Constraint::ASPECTRATIO)) { - $h = intval(round($this->width / $constraint->getSize()->getRatio())); + $h = max(1, intval(round($this->width / $constraint->getSize()->getRatio()))); if ($constraint->isFixed(Constraint::UPSIZE)) { $this->height = ($h > $max_height) ? $max_height : $h; @@ -190,7 +190,7 @@ private function resizeHeight($height, Closure $callback = null) } if ($constraint->isFixed(Constraint::ASPECTRATIO)) { - $w = intval(round($this->height * $constraint->getSize()->getRatio())); + $w = max(1, intval(round($this->height * $constraint->getSize()->getRatio()))); if ($constraint->isFixed(Constraint::UPSIZE)) { $this->width = ($w > $max_width) ? $max_width : $w; From e661de60b15cc81460e06b4a5ba02a26806a5635 Mon Sep 17 00:00:00 2001 From: vlakoff Date: Sun, 9 Sep 2018 04:59:45 +0200 Subject: [PATCH 0078/1667] Improving the fix for support of custom commands with accentuated names --- composer.json | 1 + src/Intervention/Image/AbstractDriver.php | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index d7df4a236..ddc3bdf7f 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "require": { "php": ">=5.4.0", "ext-fileinfo": "*", + "ext-mbstring": "*", "guzzlehttp/psr7": "~1.1" }, "require-dev": { diff --git a/src/Intervention/Image/AbstractDriver.php b/src/Intervention/Image/AbstractDriver.php index 29ad9d598..5ece0a1c5 100644 --- a/src/Intervention/Image/AbstractDriver.php +++ b/src/Intervention/Image/AbstractDriver.php @@ -102,11 +102,11 @@ public function executeCommand($image, $name, $arguments) */ private function getCommandClassName($name) { - $name = mb_convert_case($name[0], MB_CASE_UPPER, 'utf-8') . mb_substr($name, 1, mb_strlen($name)); + $name = mb_strtoupper(mb_substr($name, 0, 1)) . mb_substr($name, 1); $drivername = $this->getDriverName(); - $classnameLocal = sprintf('\Intervention\Image\%s\Commands\%sCommand', $drivername, ucfirst($name)); - $classnameGlobal = sprintf('\Intervention\Image\Commands\%sCommand', ucfirst($name)); + $classnameLocal = sprintf('\Intervention\Image\%s\Commands\%sCommand', $drivername, $name); + $classnameGlobal = sprintf('\Intervention\Image\Commands\%sCommand', $name); if (class_exists($classnameLocal)) { return $classnameLocal; From 87f24c63fa9aa760d65732f862b003967bbcd4dc Mon Sep 17 00:00:00 2001 From: vlakoff Date: Sun, 9 Sep 2018 08:11:36 +0200 Subject: [PATCH 0079/1667] Use Symfony polyfill instead of requiring mbstring extension --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index ddc3bdf7f..7e8f3adc5 100644 --- a/composer.json +++ b/composer.json @@ -14,8 +14,8 @@ "require": { "php": ">=5.4.0", "ext-fileinfo": "*", - "ext-mbstring": "*", - "guzzlehttp/psr7": "~1.1" + "guzzlehttp/psr7": "~1.1", + "symfony/polyfill-mbstring": ">=1.0" }, "require-dev": { "phpunit/phpunit": "^4.8 || ^5.7", From ee33534e661d7c945c4e03e9740bbece5f3520ef Mon Sep 17 00:00:00 2001 From: Samuel NELA Date: Sun, 9 Sep 2018 22:06:46 +0200 Subject: [PATCH 0080/1667] Use PHPUnit\Framework\TestCase instead of PHPUnit_Framework_TestCase --- tests/AbstractColorTest.php | 6 ++++-- tests/AbstractCommandTest.php | 6 ++++-- tests/AbstractDecoderTest.php | 6 ++++-- tests/AbstractDriverTest.php | 6 ++++-- tests/AbstractFontTest.php | 6 ++++-- tests/AbstractShapeTest.php | 4 +++- tests/ArgumentTest.php | 3 ++- tests/BackupCommandTest.php | 5 +++-- tests/BlurCommandTest.php | 5 +++-- tests/BrightnessCommandTest.php | 5 +++-- tests/ChecksumCommandTest.php | 5 +++-- tests/CircleCommandTest.php | 5 +++-- tests/CircleShapeTest.php | 7 ++++--- tests/ColorizeCommandTest.php | 5 +++-- tests/ConstraintTest.php | 3 ++- tests/ContrastCommandTest.php | 5 +++-- tests/CropCommandTest.php | 5 +++-- tests/DestroyCommandTest.php | 5 +++-- tests/DriverTest.php | 5 +++-- tests/EllipseCommandTest.php | 5 +++-- tests/EllipseShapeTest.php | 9 +++++---- tests/EncoderTest.php | 5 +++-- tests/ExifCommandTest.php | 5 +++-- tests/FileTest.php | 3 ++- tests/FillCommandTest.php | 5 +++-- tests/FitCommandTest.php | 5 +++-- tests/FlipCommandTest.php | 5 +++-- tests/GammaCommandTest.php | 5 +++-- tests/GdColorTest.php | 3 ++- tests/GdSystemTest.php | 18 ++++++++++-------- tests/GetsizeCommandTest.php | 3 ++- tests/GreyscaleCommandTest.php | 5 +++-- tests/HeightenCommandTest.php | 5 +++-- tests/ImageManagerStaticTest.php | 3 ++- tests/ImageManagerTest.php | 3 ++- tests/ImageTest.php | 5 +++-- tests/ImagickColorTest.php | 5 +++-- tests/ImagickSystemTest.php | 12 +++++++----- tests/InsertCommandTest.php | 5 +++-- tests/InterlaceCommandTest.php | 5 +++-- tests/InvertCommandTest.php | 5 +++-- tests/IptcCommandTest.php | 5 +++-- tests/LimitColorsCommandTest.php | 5 +++-- tests/LineCommandTest.php | 5 +++-- tests/LineShapeTest.php | 5 +++-- tests/MaskCommandTest.php | 7 ++++--- tests/OpacityCommandTest.php | 5 +++-- tests/OrientateCommandTest.php | 5 +++-- tests/PickColorCommandTest.php | 5 +++-- tests/PixelCommandTest.php | 5 +++-- tests/PixelateCommandTest.php | 5 +++-- tests/PointTest.php | 3 ++- tests/PolygonCommandTest.php | 5 +++-- tests/PolygonShapeTest.php | 9 +++++---- tests/PsrResponseCommandTest.php | 5 +++-- tests/RectangleCommandTest.php | 5 +++-- tests/RectangleShapeTest.php | 5 +++-- tests/ResetCommandTest.php | 3 ++- tests/ResizeCanvasCommandTest.php | 5 +++-- tests/ResizeCommandTest.php | 5 +++-- tests/ResponseTest.php | 3 ++- tests/RotateCommandTest.php | 5 +++-- tests/SharpenCommandTest.php | 5 +++-- tests/SizeTest.php | 5 +++-- tests/StreamCommandTest.php | 5 +++-- tests/TextCommandTest.php | 5 +++-- tests/TrimCommandTest.php | 5 +++-- tests/WidenCommandTest.php | 5 +++-- 68 files changed, 216 insertions(+), 140 deletions(-) diff --git a/tests/AbstractColorTest.php b/tests/AbstractColorTest.php index 3febab79a..0baeaa3b0 100644 --- a/tests/AbstractColorTest.php +++ b/tests/AbstractColorTest.php @@ -1,12 +1,14 @@ getTestCommand(); diff --git a/tests/AbstractDecoderTest.php b/tests/AbstractDecoderTest.php index c728f39bf..04498b333 100644 --- a/tests/AbstractDecoderTest.php +++ b/tests/AbstractDecoderTest.php @@ -1,12 +1,14 @@ getTestDecoder(new \Imagick); diff --git a/tests/AbstractDriverTest.php b/tests/AbstractDriverTest.php index f218d2d0b..704c09928 100644 --- a/tests/AbstractDriverTest.php +++ b/tests/AbstractDriverTest.php @@ -1,12 +1,14 @@ getMockForAbstractClass('\Intervention\Image\AbstractFont', ['test']); $this->assertEquals('test', $font->text); } - + public function testText() { $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); diff --git a/tests/AbstractShapeTest.php b/tests/AbstractShapeTest.php index e27ca82fc..64f88adc2 100644 --- a/tests/AbstractShapeTest.php +++ b/tests/AbstractShapeTest.php @@ -1,6 +1,8 @@ execute($image); $this->assertTrue($result); } -} \ No newline at end of file +} diff --git a/tests/BlurCommandTest.php b/tests/BlurCommandTest.php index 52b935673..6267d48e4 100644 --- a/tests/BlurCommandTest.php +++ b/tests/BlurCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\BlurCommand as BlurGd; use Intervention\Image\Imagick\Commands\BlurCommand as BlurImagick; +use PHPUnit\Framework\TestCase; -class BlurCommandTest extends PHPUnit_Framework_TestCase +class BlurCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); diff --git a/tests/BrightnessCommandTest.php b/tests/BrightnessCommandTest.php index 7a7cb8a93..2c225d0bf 100644 --- a/tests/BrightnessCommandTest.php +++ b/tests/BrightnessCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\BrightnessCommand as BrightnessGd; use Intervention\Image\Imagick\Commands\BrightnessCommand as BrightnessImagick; +use PHPUnit\Framework\TestCase; -class BrightnessCommandTest extends PHPUnit_Framework_TestCase +class BrightnessCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); diff --git a/tests/ChecksumCommandTest.php b/tests/ChecksumCommandTest.php index 7f7cfbab2..5be7ca68a 100644 --- a/tests/ChecksumCommandTest.php +++ b/tests/ChecksumCommandTest.php @@ -1,14 +1,15 @@ assertInstanceOf('Intervention\Image\Gd\Shapes\CircleShape', $circle); $this->assertEquals(250, $circle->diameter); - + } public function testGdApplyToImage() diff --git a/tests/ColorizeCommandTest.php b/tests/ColorizeCommandTest.php index 6dc7c190e..0b3f9ecea 100644 --- a/tests/ColorizeCommandTest.php +++ b/tests/ColorizeCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\ColorizeCommand as ColorizeGd; use Intervention\Image\Imagick\Commands\ColorizeCommand as ColorizeImagick; +use PHPUnit\Framework\TestCase; -class ColorizeCommandTest extends PHPUnit_Framework_TestCase +class ColorizeCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); diff --git a/tests/ConstraintTest.php b/tests/ConstraintTest.php index b798757bb..0ac3375d5 100644 --- a/tests/ConstraintTest.php +++ b/tests/ConstraintTest.php @@ -1,8 +1,9 @@ assertInstanceOf('Intervention\Image\Gd\Shapes\EllipseShape', $ellipse); $this->assertEquals(250, $ellipse->width); $this->assertEquals(150, $ellipse->height); - + } public function testGdApplyToImage() @@ -36,7 +37,7 @@ public function testImagickConstructor() $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\EllipseShape', $ellipse); $this->assertEquals(250, $ellipse->width); $this->assertEquals(150, $ellipse->height); - + } public function testImagickApplyToImage() diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index 7a22f98cc..834618c3a 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Encoder as GdEncoder; use Intervention\Image\Imagick\Encoder as ImagickEncoder; +use PHPUnit\Framework\TestCase; -class EncoderTest extends PHPUnit_Framework_TestCase +class EncoderTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testProcessJpegGd() { $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php index de69f45ed..8b051c95c 100644 --- a/tests/ExifCommandTest.php +++ b/tests/ExifCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Image; use Intervention\Image\Commands\ExifCommand; +use PHPUnit\Framework\TestCase; -class ExifCommandTest extends PHPUnit_Framework_TestCase +class ExifCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testFetchAll() { $image = new Image; diff --git a/tests/FileTest.php b/tests/FileTest.php index d79d6497e..820a18af8 100644 --- a/tests/FileTest.php +++ b/tests/FileTest.php @@ -1,8 +1,9 @@ assertInstanceOf('Intervention\Image\Image', $img); $this->assertEquals('a9e2b15452b2a4637b65625188d206f6', $img->checksum()); - + $img = $this->manager()->canvas(16, 16, 'ffffff'); $img = $img->text('0', 8, 2, function($font) { $font->align('center'); @@ -1067,7 +1069,7 @@ public function testTextImage() }); $this->assertInstanceOf('Intervention\Image\Image', $img); $this->assertEquals('649f3f529d3931c56601155fd2680959', $img->checksum()); - + $img = $this->manager()->canvas(16, 16, 'ffffff'); $img = $img->text('0', 8, 8, function($font) { $font->align('right'); @@ -1188,7 +1190,7 @@ public function testLimitColorsKeepTransparency() $this->assertColorAtPosition('#0c02b4', $img, 6, 12); $this->assertColorAtPosition('#fcbe04', $img, 22, 24); } - + public function testLimitColorsKeepTransparencyWithMatte() { $img = $this->manager()->make('tests/images/star.png'); @@ -1257,7 +1259,7 @@ public function testPickColorFromPalette() { $img = $this->manager()->make('tests/images/tile.png'); $img->limitColors(200); - + $c = $img->pickColor(0, 0); $this->assertEquals(180, $c[0]); $this->assertEquals(226, $c[1]); @@ -1319,7 +1321,7 @@ public function testColorizeImage() $this->assertColorAtPosition('#66ee70', $img, 0, 0); $this->assertColorAtPosition('#ffe600', $img, 24, 24); } - + public function testTrimGradient() { $canvas = $this->manager()->make('tests/images/gradient.png'); @@ -1410,7 +1412,7 @@ public function testTrimOnlyBottom() public function testTrimWithFeather() { $canvas = $this->manager()->make('tests/images/trim.png'); - + $img = clone $canvas; $feather = 5; $img->trim(null, null, null, $feather); @@ -1581,7 +1583,7 @@ public function testSaveImageWithoutParameter() $img = $this->manager()->canvas(16, 16, '#ff0000'); $img->save($path); $img->destroy(); - + // open test image again $img = $this->manager()->make($path); $this->assertColorAtPosition('#ff0000', $img, 0, 0); diff --git a/tests/GetsizeCommandTest.php b/tests/GetsizeCommandTest.php index 2a7fde515..d67f3b7f5 100644 --- a/tests/GetsizeCommandTest.php +++ b/tests/GetsizeCommandTest.php @@ -2,8 +2,9 @@ use Intervention\Image\Gd\Commands\GetSizeCommand as GetSizeGd; use Intervention\Image\Imagick\Commands\GetSizeCommand as GetSizeImagick; +use PHPUnit\Framework\TestCase; -class GetsizeCommandTest extends PHPUnit_Framework_TestCase +class GetsizeCommandTest extends TestCase { public function tearDown() { diff --git a/tests/GreyscaleCommandTest.php b/tests/GreyscaleCommandTest.php index fbe9be09b..84298199e 100644 --- a/tests/GreyscaleCommandTest.php +++ b/tests/GreyscaleCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\GreyscaleCommand as GreyscaleGd; use Intervention\Image\Imagick\Commands\GreyscaleCommand as GreyscaleImagick; +use PHPUnit\Framework\TestCase; -class GreyscaleCommandTest extends PHPUnit_Framework_TestCase +class GreyscaleCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); diff --git a/tests/HeightenCommandTest.php b/tests/HeightenCommandTest.php index 5b1f112f8..4016ff7d5 100644 --- a/tests/HeightenCommandTest.php +++ b/tests/HeightenCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\HeightenCommand as HeightenGd; use Intervention\Image\Imagick\Commands\HeightenCommand as HeightenImagick; +use PHPUnit\Framework\TestCase; -class HeightenCommandTest extends PHPUnit_Framework_TestCase +class HeightenCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $callback = function ($constraint) { $constraint->aspectRatio(); }; diff --git a/tests/ImageManagerStaticTest.php b/tests/ImageManagerStaticTest.php index 66bc59596..b4bb3f5da 100644 --- a/tests/ImageManagerStaticTest.php +++ b/tests/ImageManagerStaticTest.php @@ -1,8 +1,9 @@ getTestImage(); $this->assertFalse($image->isEncoded()); - + $image->setEncoded('foo'); $this->assertTrue($image->isEncoded()); } diff --git a/tests/ImagickColorTest.php b/tests/ImagickColorTest.php index fccbc00cc..c51ec4daa 100644 --- a/tests/ImagickColorTest.php +++ b/tests/ImagickColorTest.php @@ -1,14 +1,15 @@ assertColorAtPosition('#680098', $img, 6, 12); $this->assertColorAtPosition('#c2596a', $img, 22, 24); } - + public function testLimitColorsKeepTransparencyWithMatte() { $img = $this->manager()->make('tests/images/star.png'); @@ -1203,7 +1205,7 @@ public function testPickColorFromPalette() { $img = $this->manager()->make('tests/images/tile.png'); $img->getCore()->quantizeImage(200, \Imagick::COLORSPACE_RGB, 0, false, false); - + $c = $img->pickColor(0, 0); $this->assertEquals(180, $c[0]); $this->assertEquals(224, $c[1]); @@ -1265,7 +1267,7 @@ public function testColorizeImage() $this->assertColorAtPosition('#00ece2', $img, 0, 0); $this->assertColorAtPosition('#ffea00', $img, 24, 24); } - + public function testTrimGradient() { $canvas = $this->manager()->make('tests/images/gradient.png'); @@ -1527,7 +1529,7 @@ public function testSaveImageWithoutParameter() $img = $this->manager()->canvas(16, 16, '#ff0000'); $img->save($path); $img->destroy(); - + // open test image again $img = $this->manager()->make($path); $this->assertColorAtPosition('#ff0000', $img, 0, 0); diff --git a/tests/InsertCommandTest.php b/tests/InsertCommandTest.php index db70af090..fa0b521c4 100644 --- a/tests/InsertCommandTest.php +++ b/tests/InsertCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\InsertCommand as InsertGd; use Intervention\Image\Imagick\Commands\InsertCommand as InsertImagick; +use PHPUnit\Framework\TestCase; -class InsertCommandTest extends PHPUnit_Framework_TestCase +class InsertCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $position = Mockery::mock('\Intervention\Image\Point', [0, 0]); diff --git a/tests/InterlaceCommandTest.php b/tests/InterlaceCommandTest.php index ab34c6c59..907bc2cbe 100644 --- a/tests/InterlaceCommandTest.php +++ b/tests/InterlaceCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\InterlaceCommand as InterlaceGd; use Intervention\Image\Imagick\Commands\InterlaceCommand as InterlaceImagick; +use PHPUnit\Framework\TestCase; -class InterlaceCommandTest extends PHPUnit_Framework_TestCase +class InterlaceCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); diff --git a/tests/InvertCommandTest.php b/tests/InvertCommandTest.php index 84cebd07a..3cad4ddaa 100644 --- a/tests/InvertCommandTest.php +++ b/tests/InvertCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\InvertCommand as InvertGd; use Intervention\Image\Imagick\Commands\InvertCommand as InvertImagick; +use PHPUnit\Framework\TestCase; -class InvertCommandTest extends PHPUnit_Framework_TestCase +class InvertCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); diff --git a/tests/IptcCommandTest.php b/tests/IptcCommandTest.php index 50cc670e4..27c1ef34d 100644 --- a/tests/IptcCommandTest.php +++ b/tests/IptcCommandTest.php @@ -1,14 +1,15 @@ shouldReceive('getSize')->once()->andReturn($image_size); $image->shouldReceive('getDriver')->once()->andReturn($driver); - + $command = new MaskImagick([$mask_path, true]); $result = $command->execute($image); $this->assertTrue($result); diff --git a/tests/OpacityCommandTest.php b/tests/OpacityCommandTest.php index d943230b9..aac049714 100644 --- a/tests/OpacityCommandTest.php +++ b/tests/OpacityCommandTest.php @@ -2,14 +2,15 @@ use Intervention\Image\Gd\Commands\OpacityCommand as OpacityGd; use Intervention\Image\Imagick\Commands\OpacityCommand as OpacityImagick; +use PHPUnit\Framework\TestCase; -class OpacityCommandTest extends PHPUnit_Framework_TestCase +class OpacityCommandTest extends TestCase { public function tearDown() { Mockery::close(); } - + public function testGd() { $mask_core = imagecreatetruecolor(32, 32); diff --git a/tests/OrientateCommandTest.php b/tests/OrientateCommandTest.php index c8d8860ad..884fcbb1c 100644 --- a/tests/OrientateCommandTest.php +++ b/tests/OrientateCommandTest.php @@ -1,14 +1,15 @@ assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); $this->assertEquals([1, 2, 3, 4, 5, 6], $polygon->points); - + } public function testGdApplyToImage() @@ -38,7 +39,7 @@ public function testImagickConstructor() ['x' => 3, 'y' => 4], ['x' => 5, 'y' => 6]], $polygon->points); - + } public function testImagickApplyToImage() diff --git a/tests/PsrResponseCommandTest.php b/tests/PsrResponseCommandTest.php index df287c081..230d266ff 100644 --- a/tests/PsrResponseCommandTest.php +++ b/tests/PsrResponseCommandTest.php @@ -1,7 +1,8 @@ getHeaderLine('Content-Length') ); } -} \ No newline at end of file +} diff --git a/tests/RectangleCommandTest.php b/tests/RectangleCommandTest.php index 2ab1681cd..3def6859c 100644 --- a/tests/RectangleCommandTest.php +++ b/tests/RectangleCommandTest.php @@ -1,14 +1,15 @@ upsize(); }; diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index f1181de20..6a10b2c63 100644 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -1,8 +1,9 @@ shouldReceive('setPosition')->with(0, $height)->once(); $pivot->shouldReceive('setPosition')->with(intval($width/2), $height)->once(); $pivot->shouldReceive('setPosition')->with($width, $height)->once(); - + $box = new Size($width, $height, $pivot); $box->align('top-left'); $box->align('top'); diff --git a/tests/StreamCommandTest.php b/tests/StreamCommandTest.php index bebe5fd25..1af12dbd4 100644 --- a/tests/StreamCommandTest.php +++ b/tests/StreamCommandTest.php @@ -1,7 +1,8 @@ assertInstanceOf('Psr\Http\Message\StreamInterface', $output); $this->assertEquals($encodedContent, (string)$output); } -} \ No newline at end of file +} diff --git a/tests/TextCommandTest.php b/tests/TextCommandTest.php index 5cbbd0694..8b19cbf0e 100644 --- a/tests/TextCommandTest.php +++ b/tests/TextCommandTest.php @@ -1,14 +1,15 @@ aspectRatio(); }; From 26946b9d1908138a6da5e962e003cc3d0ffb8ff5 Mon Sep 17 00:00:00 2001 From: Koen Hoeijmakers Date: Tue, 9 Oct 2018 12:57:16 +0200 Subject: [PATCH 0081/1667] Resize width is also nullable. --- src/Intervention/Image/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 6c8dbf07a..e577e7e83 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -40,7 +40,7 @@ * @method \Intervention\Image\Image polygon(array $points, \Closure $callback = null) Draw a colored polygon with given points. You can define the appearance of the polygon by an optional closure callback. * @method \Intervention\Image\Image rectangle(int $x1, int $y1, int $x2, int $y2, \Closure $callback = null) Draw a colored rectangle on current image with top-left corner on x,y point 1 and bottom-right corner at x,y point 2. Define the overall appearance of the shape by passing a Closure callback as an optional parameter. * @method \Intervention\Image\Image reset(string $name = 'default') Resets all of the modifications to a state saved previously by backup under an optional name. - * @method \Intervention\Image\Image resize(int $width, int $height = null, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. + * @method \Intervention\Image\Image resize(int $width = null, int $height = null, \Closure $callback = null) Resizes current image based on given width and/or height. To contraint the resize command, pass an optional Closure callback as third parameter. * @method \Intervention\Image\Image resizeCanvas(int $width, int $height, string $anchor = 'center', boolean $relative = false, mixed $bgcolor = null) Resize the boundaries of the current image to given width and height. An anchor can be defined to determine from what point of the image the resizing is going to happen. Set the mode to relative to add or subtract the given width or height to the actual image dimensions. You can also pass a background color for the emerging area of the image. * @method mixed response(string $format = null, int $quality = 90) Sends HTTP response with current image in given format and quality. * @method \Intervention\Image\Image rotate(float $angle, mixed $bgcolor = null) Rotate the current image counter-clockwise by a given angle. Optionally define a background color for the uncovered zone after the rotation. From 4cf7f906271f3f6cc0fe784bb6b5e132ae4acffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F?= Date: Sat, 10 Nov 2018 20:13:39 +0200 Subject: [PATCH 0082/1667] Update Argument.php I made the code easier. --- src/Intervention/Image/Commands/Argument.php | 50 ++++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/Intervention/Image/Commands/Argument.php b/src/Intervention/Image/Commands/Argument.php index 40a8f629d..95b4b2f99 100644 --- a/src/Intervention/Image/Commands/Argument.php +++ b/src/Intervention/Image/Commands/Argument.php @@ -81,59 +81,57 @@ public function required() */ public function type($type) { - $fail = false; - + $valid = true; $value = $this->value(); - if (is_null($value)) { + if ($value === null) { return $this; } switch (strtolower($type)) { - case 'bool': case 'boolean': - $fail = ! is_bool($value); - $message = sprintf('%s accepts only boolean values as argument %d.', $this->getCommandName(), $this->key + 1); + $valid = \is_bool($value); + $message = '%s accepts only boolean values as argument %d.'; break; - case 'int': case 'integer': - $fail = ! is_integer($value); - $message = sprintf('%s accepts only integer values as argument %d.', $this->getCommandName(), $this->key + 1); + $valid = \is_int($value); + $message = '%s accepts only integer values as argument %d.'; break; - case 'num': case 'numeric': - $fail = ! is_numeric($value); - $message = sprintf('%s accepts only numeric values as argument %d.', $this->getCommandName(), $this->key + 1); + $valid = is_numeric($value); + $message = '%s accepts only numeric values as argument %d.'; break; - case 'str': case 'string': - $fail = ! is_string($value); - $message = sprintf('%s accepts only string values as argument %d.', $this->getCommandName(), $this->key + 1); + $valid = \is_string($value); + $message = '%s accepts only string values as argument %d.'; break; - case 'array': - $fail = ! is_array($value); - $message = sprintf('%s accepts only array as argument %d.', $this->getCommandName(), $this->key + 1); + $valid = \is_array($value); + $message = '%s accepts only array as argument %d.'; break; - case 'closure': - $fail = ! is_a($value, '\Closure'); - $message = sprintf('%s accepts only Closure as argument %d.', $this->getCommandName(), $this->key + 1); + $valid = is_a($value, \Closure::class); + $message = '%s accepts only Closure as argument %d.'; break; - case 'digit': - $fail = ! $this->isDigit($value); - $message = sprintf('%s accepts only integer values as argument %d.', $this->getCommandName(), $this->key + 1); + $valid = $this->isDigit($value); + $message = '%s accepts only integer values as argument %d.'; break; } - if ($fail) { + if (! $valid) { + $commandName = $this->getCommandName(); + $argument = $this->key + 1; - $message = isset($message) ? $message : sprintf("Missing argument for %d.", $this->key); + if (isset($message)) { + $message = sprintf($message, $commandName, $argument); + } else { + $message = sprintf('Missing argument for %d.', $argument); + } throw new \Intervention\Image\Exception\InvalidArgumentException( $message From c4d92f221e2256b06028795d41e641d6de710d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F?= Date: Sat, 10 Nov 2018 20:27:26 +0200 Subject: [PATCH 0083/1667] Update Argument.php --- src/Intervention/Image/Commands/Argument.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Commands/Argument.php b/src/Intervention/Image/Commands/Argument.php index 95b4b2f99..e64e8ac54 100644 --- a/src/Intervention/Image/Commands/Argument.php +++ b/src/Intervention/Image/Commands/Argument.php @@ -114,7 +114,7 @@ public function type($type) $message = '%s accepts only array as argument %d.'; break; case 'closure': - $valid = is_a($value, \Closure::class); + $valid = is_a($value, '\Closure'); $message = '%s accepts only Closure as argument %d.'; break; case 'digit': From d5b2fcb00e23d7315b363886e3e31e1f935a999c Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 6 Dec 2018 16:06:49 +0100 Subject: [PATCH 0084/1667] Update Font.php When setting vertical position to "top" ImageMagic requires the "Vertical offset in pixels to the baseline of text" according to http://php.net/manual/en/imagick.annotateimage.php. Therefore we actually need the yOffset + the characterHeight --- src/Intervention/Image/Imagick/Font.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php index 7b926679f..332249b0d 100644 --- a/src/Intervention/Image/Imagick/Font.php +++ b/src/Intervention/Image/Imagick/Font.php @@ -67,7 +67,7 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) break; case 'top': - $posy = $posy + $dimensions['textHeight'] * 0.65; + $posy = $posy + $dimensions['characterHeight']; break; } } From 321c1b9f4f36b7cbb9222bdf3f780bb0da0f5a21 Mon Sep 17 00:00:00 2001 From: Kenneth Keegan Date: Wed, 16 Jan 2019 15:32:57 -0800 Subject: [PATCH 0085/1667] Ensure WebP image background is transparent Possible fix for #923. --- src/Intervention/Image/Imagick/Encoder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index 44452f247..6c20410b2 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -78,6 +78,8 @@ protected function processWebp() $compression = \Imagick::COMPRESSION_JPEG; $imagick = $this->image->getCore(); + $imagick->setImageBackgroundColor(new \ImagickPixel('transparent')); + $imagick = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_MERGE); $imagick->setFormat($format); $imagick->setImageFormat($format); From f936e64e2f45299bff6a885ddc91b75fb8bbf58d Mon Sep 17 00:00:00 2001 From: Igor Tarasov Date: Mon, 21 Jan 2019 21:19:13 +0300 Subject: [PATCH 0086/1667] Add support for Symfony Response creation This would make response method play nice with Symfony, Silex and any other project that uses Symfony's http stack. --- src/Intervention/Image/Response.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Intervention/Image/Response.php b/src/Intervention/Image/Response.php index 8e5c7cacd..78b953e59 100644 --- a/src/Intervention/Image/Response.php +++ b/src/Intervention/Image/Response.php @@ -57,6 +57,12 @@ public function make() $response->header('Content-Type', $mime); $response->header('Content-Length', $length); + } elseif (class_exists(\Symfony\Component\HttpFoundation\Response::class)) { + + $response = \Symfony\Component\HttpFoundation\Response::create($data); + $response->headers->set('Content-Type', $mime); + $response->headers->set('Content-Length', $length); + } else { header('Content-Type: ' . $mime); From bcf9679a73535fde658148793e19717fef21eec9 Mon Sep 17 00:00:00 2001 From: Simon Gow Date: Wed, 30 Jan 2019 17:28:28 +1300 Subject: [PATCH 0087/1667] Throw NotReadable exception if the exif_read_data fails PNGs and corrupted images will raise exceptions and need to be hanlded correctly. Stop handling and raise an appropriate exception. --- .../Image/Commands/ExifCommand.php | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Intervention/Image/Commands/ExifCommand.php b/src/Intervention/Image/Commands/ExifCommand.php index 2986cae8c..8c581b17b 100644 --- a/src/Intervention/Image/Commands/ExifCommand.php +++ b/src/Intervention/Image/Commands/ExifCommand.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Commands; +use Intervention\Image\Exception\NotReadableException; + class ExifCommand extends AbstractCommand { /** @@ -15,7 +17,7 @@ class ExifCommand extends AbstractCommand */ public function execute($image) { - if ( ! function_exists('exif_read_data')) { + if (!function_exists('exif_read_data')) { throw new \Intervention\Image\Exception\NotSupportedException( "Reading Exif data is not supported by this PHP installation." ); @@ -24,14 +26,25 @@ public function execute($image) $key = $this->argument(0)->value(); // try to read exif data from image file - $data = @exif_read_data($image->dirname .'/'. $image->basename); + try { + $data = @exif_read_data($image->dirname . '/' . $image->basename); + + if (!is_null($key) && is_array($data)) { + $data = array_key_exists($key, $data) ? $data[$key] : false; + } - if (! is_null($key) && is_array($data)) { - $data = array_key_exists($key, $data) ? $data[$key] : false; + } catch (\Exception $e) { + throw new NotReadableException( + sprintf( + "Cannot read the Exif data from the filename (%s) provided ", + $image->dirname . '/' . $image->basename + ), + $e->getCode(), + $e + ); } $this->setOutput($data); - return true; } } From 67e0dbf203cd74497990b7a9162e85c31355625a Mon Sep 17 00:00:00 2001 From: Tarasovych Date: Wed, 27 Mar 2019 10:50:25 +0200 Subject: [PATCH 0088/1667] Issue #933 --- src/Intervention/Image/ImageManagerStatic.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/ImageManagerStatic.php b/src/Intervention/Image/ImageManagerStatic.php index ad549c8ca..a1b564264 100644 --- a/src/Intervention/Image/ImageManagerStatic.php +++ b/src/Intervention/Image/ImageManagerStatic.php @@ -51,6 +51,7 @@ public static function configure(array $config = []) * @param mixed $data * * @return \Intervention\Image\Image + * @throws \Intervention\Image\Exception\NotReadableException */ public static function make($data) { From da3e032596b70f8a5da948f89ef1a3adf464bb42 Mon Sep 17 00:00:00 2001 From: TullariS Date: Wed, 1 May 2019 17:50:00 +0100 Subject: [PATCH 0089/1667] PHPDoc static methods for Facade Facilitates autocomplete when using the Facade --- src/Intervention/Image/Facades/Image.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Intervention/Image/Facades/Image.php b/src/Intervention/Image/Facades/Image.php index 44387aab5..7a460f43d 100644 --- a/src/Intervention/Image/Facades/Image.php +++ b/src/Intervention/Image/Facades/Image.php @@ -4,6 +4,12 @@ use Illuminate\Support\Facades\Facade; +/** + * @method static \Intervention\Image\Image make(mixed $data) + * @method static self configure(array $config) + * @method static \Intervention\Image\Image canvas(int $width, int $height, mixed $background = null) + * @method static \Intervention\Image\Image cache(\Closure $callback, int $lifetime = null, boolean $returnObj = false) + */ class Image extends Facade { protected static function getFacadeAccessor() From 1abd3bfdac3702bcff2a6eacd592b20d30049089 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 31 May 2019 12:05:16 +0200 Subject: [PATCH 0090/1667] Made test available again --- tests/GdSystemTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index 9a26128de..7d7d5ecdb 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -25,10 +25,10 @@ public function testMakeFromPath() /** * @expectedException \Intervention\Image\Exception\NotReadableException */ -// public function testMakeFromPathBroken() -// { -// $this->manager()->make('tests/images/broken.png'); -// } + public function testMakeFromPathBroken() + { + $this->manager()->make('tests/images/broken.png'); + } /** * @expectedException \Intervention\Image\Exception\NotReadableException From bd269593e8c13f9f7c6a434212d70b65baa3dd29 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 31 May 2019 12:25:14 +0200 Subject: [PATCH 0091/1667] Fix for PHP5.4 --- src/Intervention/Image/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Response.php b/src/Intervention/Image/Response.php index 78b953e59..e883cac45 100644 --- a/src/Intervention/Image/Response.php +++ b/src/Intervention/Image/Response.php @@ -57,7 +57,7 @@ public function make() $response->header('Content-Type', $mime); $response->header('Content-Length', $length); - } elseif (class_exists(\Symfony\Component\HttpFoundation\Response::class)) { + } elseif (class_exists('\Symfony\Component\HttpFoundation\Response')) { $response = \Symfony\Component\HttpFoundation\Response::create($data); $response->headers->set('Content-Type', $mime); From 09ad3953df7fb3d8da8a74c47e5c0a6ac5bca7d1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 31 May 2019 12:32:08 +0200 Subject: [PATCH 0092/1667] Added PHP versions to CI --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 87bbb1e67..de890615d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,8 @@ php: - 5.6 - 7.0 - 7.1 + - 7.2 + - 7.3 - nightly - hhvm From 2bd42c557cd691cc432284488b083878cc081031 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 31 May 2019 12:40:07 +0200 Subject: [PATCH 0093/1667] Added option to PsrResponseCommandTest --- tests/PsrResponseCommandTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/PsrResponseCommandTest.php b/tests/PsrResponseCommandTest.php index 230d266ff..84cbdfb5d 100644 --- a/tests/PsrResponseCommandTest.php +++ b/tests/PsrResponseCommandTest.php @@ -45,10 +45,10 @@ public function testResponseCreationAndHeaders() $this->assertTrue($output->hasHeader('Content-Type')); $this->assertTrue($output->hasHeader('Content-Length')); - $this->assertEquals( - "application/xml", - $output->getHeaderLine('Content-Type') - ); + $this->assertTrue(in_array($output->getHeaderLine('Content-Type'), [ + 'application/xml', + 'text/xml', + ])) $this->assertEquals( strlen($encodedContent), From 5d59c524da8a2d951d3b31a551ebbdb55db5326a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 31 May 2019 12:44:09 +0200 Subject: [PATCH 0094/1667] Bugfix --- tests/PsrResponseCommandTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PsrResponseCommandTest.php b/tests/PsrResponseCommandTest.php index 84cbdfb5d..f7753f04a 100644 --- a/tests/PsrResponseCommandTest.php +++ b/tests/PsrResponseCommandTest.php @@ -48,7 +48,7 @@ public function testResponseCreationAndHeaders() $this->assertTrue(in_array($output->getHeaderLine('Content-Type'), [ 'application/xml', 'text/xml', - ])) + ])); $this->assertEquals( strlen($encodedContent), From 8ee5f346ce8c6dcbdc7dec443486bd5f6ad924ff Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 31 May 2019 19:02:32 +0200 Subject: [PATCH 0095/1667] Optimized code --- src/Intervention/Image/AbstractColor.php | 9 ++++++--- src/Intervention/Image/AbstractDecoder.php | 7 ++++--- src/Intervention/Image/AbstractDriver.php | 4 +++- src/Intervention/Image/AbstractEncoder.php | 7 +++++-- .../Image/Commands/AbstractCommand.php | 4 +++- src/Intervention/Image/Commands/Argument.php | 12 +++++++----- src/Intervention/Image/Commands/CircleCommand.php | 2 +- src/Intervention/Image/Commands/EllipseCommand.php | 2 +- src/Intervention/Image/Commands/ExifCommand.php | 3 ++- src/Intervention/Image/Commands/IptcCommand.php | 4 +++- src/Intervention/Image/Commands/LineCommand.php | 2 +- src/Intervention/Image/Commands/PolygonCommand.php | 7 ++++--- .../Image/Commands/RectangleCommand.php | 2 +- src/Intervention/Image/Commands/TextCommand.php | 2 +- src/Intervention/Image/Filters/DemoFilter.php | 4 +++- src/Intervention/Image/Filters/FilterInterface.php | 4 +++- src/Intervention/Image/Gd/Color.php | 3 ++- .../Image/Gd/Commands/BackupCommand.php | 4 +++- src/Intervention/Image/Gd/Commands/BlurCommand.php | 4 +++- .../Image/Gd/Commands/BrightnessCommand.php | 4 +++- .../Image/Gd/Commands/ColorizeCommand.php | 4 +++- .../Image/Gd/Commands/ContrastCommand.php | 4 +++- .../Image/Gd/Commands/DestroyCommand.php | 4 +++- src/Intervention/Image/Gd/Commands/FillCommand.php | 5 +++-- .../Image/Gd/Commands/GammaCommand.php | 4 +++- .../Image/Gd/Commands/GetSizeCommand.php | 3 ++- .../Image/Gd/Commands/GreyscaleCommand.php | 4 +++- .../Image/Gd/Commands/InsertCommand.php | 4 +++- .../Image/Gd/Commands/InterlaceCommand.php | 4 +++- .../Image/Gd/Commands/InvertCommand.php | 4 +++- .../Image/Gd/Commands/LimitColorsCommand.php | 4 +++- src/Intervention/Image/Gd/Commands/MaskCommand.php | 4 +++- .../Image/Gd/Commands/OpacityCommand.php | 4 +++- .../Image/Gd/Commands/PickColorCommand.php | 3 ++- .../Image/Gd/Commands/PixelCommand.php | 3 ++- .../Image/Gd/Commands/PixelateCommand.php | 4 +++- .../Image/Gd/Commands/ResetCommand.php | 7 +++++-- .../Image/Gd/Commands/ResizeCanvasCommand.php | 4 +++- .../Image/Gd/Commands/ResizeCommand.php | 4 +++- .../Image/Gd/Commands/RotateCommand.php | 3 ++- .../Image/Gd/Commands/SharpenCommand.php | 4 +++- src/Intervention/Image/Gd/Decoder.php | 14 ++++++++------ src/Intervention/Image/Gd/Driver.php | 7 +++++-- src/Intervention/Image/Gd/Encoder.php | 12 +++++++----- src/Intervention/Image/Gd/Font.php | 3 ++- src/Intervention/Image/Gd/Shapes/EllipseShape.php | 5 +++-- src/Intervention/Image/Gd/Shapes/LineShape.php | 5 +++-- src/Intervention/Image/Gd/Shapes/PolygonShape.php | 5 +++-- .../Image/Gd/Shapes/RectangleShape.php | 5 +++-- src/Intervention/Image/Image.php | 8 +++++--- src/Intervention/Image/ImageManager.php | 10 ++++++---- src/Intervention/Image/ImageServiceProvider.php | 6 ++++-- src/Intervention/Image/Imagick/Color.php | 6 ++++-- .../Image/Imagick/Commands/BackupCommand.php | 4 +++- .../Image/Imagick/Commands/BlurCommand.php | 4 +++- .../Image/Imagick/Commands/BrightnessCommand.php | 4 +++- .../Image/Imagick/Commands/ColorizeCommand.php | 4 +++- .../Image/Imagick/Commands/ContrastCommand.php | 4 +++- .../Image/Imagick/Commands/CropCommand.php | 6 ++++-- .../Image/Imagick/Commands/DestroyCommand.php | 4 +++- .../Image/Imagick/Commands/ExifCommand.php | 3 ++- .../Image/Imagick/Commands/FillCommand.php | 8 +++++--- .../Image/Imagick/Commands/FitCommand.php | 3 ++- .../Image/Imagick/Commands/FlipCommand.php | 4 +++- .../Image/Imagick/Commands/GammaCommand.php | 4 +++- .../Image/Imagick/Commands/GetSizeCommand.php | 3 ++- .../Image/Imagick/Commands/GreyscaleCommand.php | 4 +++- .../Image/Imagick/Commands/InsertCommand.php | 4 +++- .../Image/Imagick/Commands/InterlaceCommand.php | 4 +++- .../Image/Imagick/Commands/InvertCommand.php | 4 +++- .../Image/Imagick/Commands/LimitColorsCommand.php | 4 +++- .../Image/Imagick/Commands/MaskCommand.php | 4 +++- .../Image/Imagick/Commands/OpacityCommand.php | 4 +++- .../Image/Imagick/Commands/PickColorCommand.php | 3 ++- .../Image/Imagick/Commands/PixelCommand.php | 3 ++- .../Image/Imagick/Commands/PixelateCommand.php | 4 +++- .../Image/Imagick/Commands/ResetCommand.php | 7 +++++-- .../Image/Imagick/Commands/ResizeCanvasCommand.php | 4 +++- .../Image/Imagick/Commands/ResizeCommand.php | 4 +++- .../Image/Imagick/Commands/RotateCommand.php | 3 ++- .../Image/Imagick/Commands/SharpenCommand.php | 4 +++- .../Image/Imagick/Commands/TrimCommand.php | 3 ++- src/Intervention/Image/Imagick/Decoder.php | 9 ++++++--- src/Intervention/Image/Imagick/Driver.php | 10 +++++++--- src/Intervention/Image/Imagick/Encoder.php | 7 +++++-- src/Intervention/Image/Imagick/Font.php | 8 +++++--- .../Image/Imagick/Shapes/EllipseShape.php | 3 ++- .../Image/Imagick/Shapes/LineShape.php | 3 ++- .../Image/Imagick/Shapes/PolygonShape.php | 3 ++- .../Image/Imagick/Shapes/RectangleShape.php | 3 ++- src/Intervention/Image/Response.php | 7 +++++-- src/Intervention/Image/Size.php | 3 ++- 92 files changed, 296 insertions(+), 138 deletions(-) diff --git a/src/Intervention/Image/AbstractColor.php b/src/Intervention/Image/AbstractColor.php index 28cbaf29d..c9846c6ae 100644 --- a/src/Intervention/Image/AbstractColor.php +++ b/src/Intervention/Image/AbstractColor.php @@ -2,6 +2,9 @@ namespace Intervention\Image; +use Intervention\Image\Exception\NotReadableException; +use Intervention\Image\Exception\NotSupportedException; + abstract class AbstractColor { /** @@ -136,7 +139,7 @@ public function parse($value) break; default: - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Color format ({$value}) cannot be read." ); } @@ -172,7 +175,7 @@ public function format($type) return $this; default: - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Color format ({$type}) is not supported." ); } @@ -216,7 +219,7 @@ protected function rgbaFromString($value) $result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0; $result[3] = ($matches[4] >= 0 && $matches[4] <= 1) ? $matches[4] : 0; } else { - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unable to read color ({$value})." ); } diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 235520e38..de770c328 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image; use GuzzleHttp\Psr7\Stream; +use Intervention\Image\Exception\NotReadableException; use Psr\Http\Message\StreamInterface; abstract class AbstractDecoder @@ -80,7 +81,7 @@ public function initFromUrl($url) return $this->initFromBinary($data); } - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unable to init from given url (".$url.")." ); } @@ -123,7 +124,7 @@ public function initFromStream($stream) return $this->initFromBinary($data); } - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unable to init from given stream" ); } @@ -342,7 +343,7 @@ public function init($data) return $this->initFromBinary(base64_decode($this->data)); default: - throw new Exception\NotReadableException("Image source not readable"); + throw new NotReadableException("Image source not readable"); } } diff --git a/src/Intervention/Image/AbstractDriver.php b/src/Intervention/Image/AbstractDriver.php index 29ad9d598..d2356ae80 100644 --- a/src/Intervention/Image/AbstractDriver.php +++ b/src/Intervention/Image/AbstractDriver.php @@ -2,6 +2,8 @@ namespace Intervention\Image; +use Intervention\Image\Exception\NotSupportedException; + abstract class AbstractDriver { /** @@ -114,7 +116,7 @@ private function getCommandClassName($name) return $classnameGlobal; } - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Command ({$name}) is not available for driver ({$drivername})." ); } diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index 357b8f487..d3e59eaa4 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -2,6 +2,9 @@ namespace Intervention\Image; +use Intervention\Image\Exception\InvalidArgumentException; +use Intervention\Image\Exception\NotSupportedException; + abstract class AbstractEncoder { /** @@ -167,7 +170,7 @@ public function process(Image $image, $format = null, $quality = null) break; default: - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Encoding format ({$format}) is not supported." ); } @@ -229,7 +232,7 @@ protected function setQuality($quality) $quality = $quality === 0 ? 1 : $quality; if ($quality < 0 || $quality > 100) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( 'Quality must range from 0 to 100.' ); } diff --git a/src/Intervention/Image/Commands/AbstractCommand.php b/src/Intervention/Image/Commands/AbstractCommand.php index cf9ca1083..e31078ceb 100644 --- a/src/Intervention/Image/Commands/AbstractCommand.php +++ b/src/Intervention/Image/Commands/AbstractCommand.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Commands; +use Intervention\Image\Commands\Argument; + abstract class AbstractCommand { /** @@ -44,7 +46,7 @@ public function __construct($arguments) */ public function argument($key) { - return new \Intervention\Image\Commands\Argument($this, $key); + return new Argument($this, $key); } /** diff --git a/src/Intervention/Image/Commands/Argument.php b/src/Intervention/Image/Commands/Argument.php index e64e8ac54..9538199f6 100644 --- a/src/Intervention/Image/Commands/Argument.php +++ b/src/Intervention/Image/Commands/Argument.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Commands; +use Intervention\Image\Exception\InvalidArgumentException; + class Argument { /** @@ -66,7 +68,7 @@ public function value($default = null) public function required() { if ( ! array_key_exists($this->key, $this->command->arguments)) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( sprintf("Missing argument %d for %s", $this->key + 1, $this->getCommandName()) ); } @@ -133,7 +135,7 @@ public function type($type) $message = sprintf('Missing argument for %d.', $argument); } - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( $message ); } @@ -158,7 +160,7 @@ public function between($x, $y) $omega = max($x, $y); if ($value < $alpha || $value > $omega) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( sprintf('Argument %d must be between %s and %s.', $this->key, $x, $y) ); } @@ -180,7 +182,7 @@ public function min($value) } if ($v < $value) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( sprintf('Argument %d must be at least %s.', $this->key, $value) ); } @@ -202,7 +204,7 @@ public function max($value) } if ($v > $value) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( sprintf('Argument %d may not be greater than %s.', $this->key, $value) ); } diff --git a/src/Intervention/Image/Commands/CircleCommand.php b/src/Intervention/Image/Commands/CircleCommand.php index 2fc38ddf8..c627818e3 100644 --- a/src/Intervention/Image/Commands/CircleCommand.php +++ b/src/Intervention/Image/Commands/CircleCommand.php @@ -4,7 +4,7 @@ use Closure; -class CircleCommand extends \Intervention\Image\Commands\AbstractCommand +class CircleCommand extends AbstractCommand { /** * Draw a circle centered on given image diff --git a/src/Intervention/Image/Commands/EllipseCommand.php b/src/Intervention/Image/Commands/EllipseCommand.php index 4f364eca1..4637e0205 100644 --- a/src/Intervention/Image/Commands/EllipseCommand.php +++ b/src/Intervention/Image/Commands/EllipseCommand.php @@ -4,7 +4,7 @@ use Closure; -class EllipseCommand extends \Intervention\Image\Commands\AbstractCommand +class EllipseCommand extends AbstractCommand { /** * Draws ellipse on given image diff --git a/src/Intervention/Image/Commands/ExifCommand.php b/src/Intervention/Image/Commands/ExifCommand.php index 8c581b17b..7103f2fd3 100644 --- a/src/Intervention/Image/Commands/ExifCommand.php +++ b/src/Intervention/Image/Commands/ExifCommand.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Commands; use Intervention\Image\Exception\NotReadableException; +use Intervention\Image\Exception\NotSupportedException; class ExifCommand extends AbstractCommand { @@ -18,7 +19,7 @@ class ExifCommand extends AbstractCommand public function execute($image) { if (!function_exists('exif_read_data')) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Reading Exif data is not supported by this PHP installation." ); } diff --git a/src/Intervention/Image/Commands/IptcCommand.php b/src/Intervention/Image/Commands/IptcCommand.php index 88e8fd35b..ae953d200 100644 --- a/src/Intervention/Image/Commands/IptcCommand.php +++ b/src/Intervention/Image/Commands/IptcCommand.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Commands; +use Intervention\Image\Exception\NotSupportedException; + class IptcCommand extends AbstractCommand { /** @@ -13,7 +15,7 @@ class IptcCommand extends AbstractCommand public function execute($image) { if ( ! function_exists('iptcparse')) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Reading Iptc data is not supported by this PHP installation." ); } diff --git a/src/Intervention/Image/Commands/LineCommand.php b/src/Intervention/Image/Commands/LineCommand.php index 0089c649a..a068c662a 100644 --- a/src/Intervention/Image/Commands/LineCommand.php +++ b/src/Intervention/Image/Commands/LineCommand.php @@ -4,7 +4,7 @@ use Closure; -class LineCommand extends \Intervention\Image\Commands\AbstractCommand +class LineCommand extends AbstractCommand { /** * Draws line on given image diff --git a/src/Intervention/Image/Commands/PolygonCommand.php b/src/Intervention/Image/Commands/PolygonCommand.php index e46e3fffb..a2fa99788 100644 --- a/src/Intervention/Image/Commands/PolygonCommand.php +++ b/src/Intervention/Image/Commands/PolygonCommand.php @@ -3,8 +3,9 @@ namespace Intervention\Image\Commands; use Closure; +use Intervention\Image\Exception\InvalidArgumentException; -class PolygonCommand extends \Intervention\Image\Commands\AbstractCommand +class PolygonCommand extends AbstractCommand { /** * Draw a polygon on given image @@ -21,13 +22,13 @@ public function execute($image) // check if number if coordinates is even if ($vertices_count % 2 !== 0) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( "The number of given polygon vertices must be even." ); } if ($vertices_count < 6) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( "You must have at least 3 points in your array." ); } diff --git a/src/Intervention/Image/Commands/RectangleCommand.php b/src/Intervention/Image/Commands/RectangleCommand.php index 3a2074c57..24378b386 100644 --- a/src/Intervention/Image/Commands/RectangleCommand.php +++ b/src/Intervention/Image/Commands/RectangleCommand.php @@ -4,7 +4,7 @@ use Closure; -class RectangleCommand extends \Intervention\Image\Commands\AbstractCommand +class RectangleCommand extends AbstractCommand { /** * Draws rectangle on given image diff --git a/src/Intervention/Image/Commands/TextCommand.php b/src/Intervention/Image/Commands/TextCommand.php index 4aebd8e89..3c60b4e77 100644 --- a/src/Intervention/Image/Commands/TextCommand.php +++ b/src/Intervention/Image/Commands/TextCommand.php @@ -4,7 +4,7 @@ use Closure; -class TextCommand extends \Intervention\Image\Commands\AbstractCommand +class TextCommand extends AbstractCommand { /** * Write text on given image diff --git a/src/Intervention/Image/Filters/DemoFilter.php b/src/Intervention/Image/Filters/DemoFilter.php index f5aac05bf..4e8f92b82 100644 --- a/src/Intervention/Image/Filters/DemoFilter.php +++ b/src/Intervention/Image/Filters/DemoFilter.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Filters; +use Intervention\Image\Image; + class DemoFilter implements FilterInterface { /** @@ -32,7 +34,7 @@ public function __construct($size = null) * @param \Intervention\Image\Image $image * @return \Intervention\Image\Image */ - public function applyFilter(\Intervention\Image\Image $image) + public function applyFilter(Image $image) { $image->pixelate($this->size); $image->greyscale(); diff --git a/src/Intervention/Image/Filters/FilterInterface.php b/src/Intervention/Image/Filters/FilterInterface.php index 27c0beef4..88f6cbfe0 100644 --- a/src/Intervention/Image/Filters/FilterInterface.php +++ b/src/Intervention/Image/Filters/FilterInterface.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Filters; +use Intervention\Image\Image; + interface FilterInterface { /** @@ -10,5 +12,5 @@ interface FilterInterface * @param \Intervention\Image\Image $image * @return \Intervention\Image\Image */ - public function applyFilter(\Intervention\Image\Image $image); + public function applyFilter(Image $image); } diff --git a/src/Intervention/Image/Gd/Color.php b/src/Intervention/Image/Gd/Color.php index 71a889f1c..44370ba09 100644 --- a/src/Intervention/Image/Gd/Color.php +++ b/src/Intervention/Image/Gd/Color.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Gd; use Intervention\Image\AbstractColor; +use Intervention\Image\Exception\NotSupportedException; class Color extends AbstractColor { @@ -134,7 +135,7 @@ public function initFromRgba($r, $g, $b, $a = 1) */ public function initFromObject($value) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "GD colors cannot init from ImagickPixel objects." ); } diff --git a/src/Intervention/Image/Gd/Commands/BackupCommand.php b/src/Intervention/Image/Gd/Commands/BackupCommand.php index 98b3c7250..310dc03a9 100644 --- a/src/Intervention/Image/Gd/Commands/BackupCommand.php +++ b/src/Intervention/Image/Gd/Commands/BackupCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class BackupCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class BackupCommand extends AbstractCommand { /** * Saves a backups of current state of image core diff --git a/src/Intervention/Image/Gd/Commands/BlurCommand.php b/src/Intervention/Image/Gd/Commands/BlurCommand.php index d53f59d7c..c4fca0eae 100644 --- a/src/Intervention/Image/Gd/Commands/BlurCommand.php +++ b/src/Intervention/Image/Gd/Commands/BlurCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class BlurCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class BlurCommand extends AbstractCommand { /** * Applies blur effect on image diff --git a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php b/src/Intervention/Image/Gd/Commands/BrightnessCommand.php index de4263f74..4e48464b1 100644 --- a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php +++ b/src/Intervention/Image/Gd/Commands/BrightnessCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class BrightnessCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class BrightnessCommand extends AbstractCommand { /** * Changes image brightness diff --git a/src/Intervention/Image/Gd/Commands/ColorizeCommand.php b/src/Intervention/Image/Gd/Commands/ColorizeCommand.php index 8f539638b..410917b33 100644 --- a/src/Intervention/Image/Gd/Commands/ColorizeCommand.php +++ b/src/Intervention/Image/Gd/Commands/ColorizeCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class ColorizeCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class ColorizeCommand extends AbstractCommand { /** * Changes balance of different RGB color channels diff --git a/src/Intervention/Image/Gd/Commands/ContrastCommand.php b/src/Intervention/Image/Gd/Commands/ContrastCommand.php index e43b761af..916c41f82 100644 --- a/src/Intervention/Image/Gd/Commands/ContrastCommand.php +++ b/src/Intervention/Image/Gd/Commands/ContrastCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class ContrastCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class ContrastCommand extends AbstractCommand { /** * Changes contrast of image diff --git a/src/Intervention/Image/Gd/Commands/DestroyCommand.php b/src/Intervention/Image/Gd/Commands/DestroyCommand.php index 18383307a..4503d10fe 100644 --- a/src/Intervention/Image/Gd/Commands/DestroyCommand.php +++ b/src/Intervention/Image/Gd/Commands/DestroyCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class DestroyCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class DestroyCommand extends AbstractCommand { /** * Destroys current image core and frees up memory diff --git a/src/Intervention/Image/Gd/Commands/FillCommand.php b/src/Intervention/Image/Gd/Commands/FillCommand.php index aaecb7fb9..cf1908210 100644 --- a/src/Intervention/Image/Gd/Commands/FillCommand.php +++ b/src/Intervention/Image/Gd/Commands/FillCommand.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Gd\Commands; -use Intervention\Image\Gd\Decoder; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Gd\Color; +use Intervention\Image\Gd\Decoder; -class FillCommand extends \Intervention\Image\Commands\AbstractCommand +class FillCommand extends AbstractCommand { /** * Fills image with color or pattern diff --git a/src/Intervention/Image/Gd/Commands/GammaCommand.php b/src/Intervention/Image/Gd/Commands/GammaCommand.php index 366f11808..7de0fb8a1 100644 --- a/src/Intervention/Image/Gd/Commands/GammaCommand.php +++ b/src/Intervention/Image/Gd/Commands/GammaCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class GammaCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class GammaCommand extends AbstractCommand { /** * Applies gamma correction to a given image diff --git a/src/Intervention/Image/Gd/Commands/GetSizeCommand.php b/src/Intervention/Image/Gd/Commands/GetSizeCommand.php index 89ee2848f..9eb7e2092 100644 --- a/src/Intervention/Image/Gd/Commands/GetSizeCommand.php +++ b/src/Intervention/Image/Gd/Commands/GetSizeCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Gd\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Size; -class GetSizeCommand extends \Intervention\Image\Commands\AbstractCommand +class GetSizeCommand extends AbstractCommand { /** * Reads size of given image instance in pixels diff --git a/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php b/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php index ded8e0d8f..12921e79e 100644 --- a/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php +++ b/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class GreyscaleCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class GreyscaleCommand extends AbstractCommand { /** * Turns an image into a greyscale version diff --git a/src/Intervention/Image/Gd/Commands/InsertCommand.php b/src/Intervention/Image/Gd/Commands/InsertCommand.php index eba75f012..47c770398 100644 --- a/src/Intervention/Image/Gd/Commands/InsertCommand.php +++ b/src/Intervention/Image/Gd/Commands/InsertCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class InsertCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class InsertCommand extends AbstractCommand { /** * Insert another image into given image diff --git a/src/Intervention/Image/Gd/Commands/InterlaceCommand.php b/src/Intervention/Image/Gd/Commands/InterlaceCommand.php index e8f4b184c..e3461cb07 100644 --- a/src/Intervention/Image/Gd/Commands/InterlaceCommand.php +++ b/src/Intervention/Image/Gd/Commands/InterlaceCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class InterlaceCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class InterlaceCommand extends AbstractCommand { /** * Toggles interlaced encoding mode diff --git a/src/Intervention/Image/Gd/Commands/InvertCommand.php b/src/Intervention/Image/Gd/Commands/InvertCommand.php index f72e7e305..1a514f1d4 100644 --- a/src/Intervention/Image/Gd/Commands/InvertCommand.php +++ b/src/Intervention/Image/Gd/Commands/InvertCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class InvertCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class InvertCommand extends AbstractCommand { /** * Inverts colors of an image diff --git a/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php b/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php index 27955e79a..0baed7e91 100644 --- a/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php +++ b/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php @@ -2,8 +2,10 @@ namespace Intervention\Image\Gd\Commands; +use Intervention\Image\Commands\AbstractCommand; -class LimitColorsCommand extends \Intervention\Image\Commands\AbstractCommand + +class LimitColorsCommand extends AbstractCommand { /** * Reduces colors of a given image diff --git a/src/Intervention/Image/Gd/Commands/MaskCommand.php b/src/Intervention/Image/Gd/Commands/MaskCommand.php index ef88d4dba..4c61429b2 100644 --- a/src/Intervention/Image/Gd/Commands/MaskCommand.php +++ b/src/Intervention/Image/Gd/Commands/MaskCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class MaskCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class MaskCommand extends AbstractCommand { /** * Applies an alpha mask to an image diff --git a/src/Intervention/Image/Gd/Commands/OpacityCommand.php b/src/Intervention/Image/Gd/Commands/OpacityCommand.php index 081e68a4a..48492d24f 100644 --- a/src/Intervention/Image/Gd/Commands/OpacityCommand.php +++ b/src/Intervention/Image/Gd/Commands/OpacityCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class OpacityCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class OpacityCommand extends AbstractCommand { /** * Defines opacity of an image diff --git a/src/Intervention/Image/Gd/Commands/PickColorCommand.php b/src/Intervention/Image/Gd/Commands/PickColorCommand.php index 9fb4bb422..bad96f498 100644 --- a/src/Intervention/Image/Gd/Commands/PickColorCommand.php +++ b/src/Intervention/Image/Gd/Commands/PickColorCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Gd\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Gd\Color; -class PickColorCommand extends \Intervention\Image\Commands\AbstractCommand +class PickColorCommand extends AbstractCommand { /** * Read color information from a certain position diff --git a/src/Intervention/Image/Gd/Commands/PixelCommand.php b/src/Intervention/Image/Gd/Commands/PixelCommand.php index 67f3e3b95..2a90ce34f 100644 --- a/src/Intervention/Image/Gd/Commands/PixelCommand.php +++ b/src/Intervention/Image/Gd/Commands/PixelCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Gd\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Gd\Color; -class PixelCommand extends \Intervention\Image\Commands\AbstractCommand +class PixelCommand extends AbstractCommand { /** * Draws one pixel to a given image diff --git a/src/Intervention/Image/Gd/Commands/PixelateCommand.php b/src/Intervention/Image/Gd/Commands/PixelateCommand.php index 2e2093d7d..0934797a8 100644 --- a/src/Intervention/Image/Gd/Commands/PixelateCommand.php +++ b/src/Intervention/Image/Gd/Commands/PixelateCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class PixelateCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class PixelateCommand extends AbstractCommand { /** * Applies a pixelation effect to a given image diff --git a/src/Intervention/Image/Gd/Commands/ResetCommand.php b/src/Intervention/Image/Gd/Commands/ResetCommand.php index c8d2e4a12..12c41bf6e 100644 --- a/src/Intervention/Image/Gd/Commands/ResetCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResetCommand.php @@ -2,7 +2,10 @@ namespace Intervention\Image\Gd\Commands; -class ResetCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; +use Intervention\Image\Exception\RuntimeException; + +class ResetCommand extends AbstractCommand { /** * Resets given image to its backup state @@ -28,7 +31,7 @@ public function execute($image) return true; } - throw new \Intervention\Image\Exception\RuntimeException( + throw new RuntimeException( "Backup not available. Call backup() before reset()." ); } diff --git a/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php index 70739fff9..73f3df308 100644 --- a/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class ResizeCanvasCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class ResizeCanvasCommand extends AbstractCommand { /** * Resizes image boundaries diff --git a/src/Intervention/Image/Gd/Commands/ResizeCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCommand.php index b76df3a9f..382d9709e 100644 --- a/src/Intervention/Image/Gd/Commands/ResizeCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResizeCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class ResizeCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class ResizeCommand extends AbstractCommand { /** * Resizes image dimensions diff --git a/src/Intervention/Image/Gd/Commands/RotateCommand.php b/src/Intervention/Image/Gd/Commands/RotateCommand.php index 698f514a3..e02b91760 100644 --- a/src/Intervention/Image/Gd/Commands/RotateCommand.php +++ b/src/Intervention/Image/Gd/Commands/RotateCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Gd\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Gd\Color; -class RotateCommand extends \Intervention\Image\Commands\AbstractCommand +class RotateCommand extends AbstractCommand { /** * Rotates image counter clockwise diff --git a/src/Intervention/Image/Gd/Commands/SharpenCommand.php b/src/Intervention/Image/Gd/Commands/SharpenCommand.php index 4c0cc50f5..782e56502 100644 --- a/src/Intervention/Image/Gd/Commands/SharpenCommand.php +++ b/src/Intervention/Image/Gd/Commands/SharpenCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd\Commands; -class SharpenCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class SharpenCommand extends AbstractCommand { /** * Sharpen image diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 4fe821e5e..6d2d7b4d3 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Gd; +use Intervention\Image\Exception\NotReadableException; +use Intervention\Image\Exception\NotSupportedException; use Intervention\Image\Image; class Decoder extends \Intervention\Image\AbstractDecoder @@ -15,7 +17,7 @@ class Decoder extends \Intervention\Image\AbstractDecoder public function initFromPath($path) { if ( ! file_exists($path)) { - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unable to find file ({$path})." ); } @@ -46,7 +48,7 @@ public function initFromPath($path) case 'image/webp': case 'image/x-webp': if ( ! function_exists('imagecreatefromwebp')) { - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unsupported image type. GD/PHP installation does not support WebP format." ); } @@ -54,13 +56,13 @@ public function initFromPath($path) break; default: - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unsupported image type. GD driver is only able to decode JPG, PNG, GIF or WebP files." ); } if (empty($core)) { - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unable to decode image from file ({$path})." ); } @@ -94,7 +96,7 @@ public function initFromGdResource($resource) */ public function initFromImagick(\Imagick $object) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Gd driver is unable to init from Imagick object." ); } @@ -110,7 +112,7 @@ public function initFromBinary($binary) $resource = @imagecreatefromstring($binary); if ($resource === false) { - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unable to init from given binary data." ); } diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index b5a2d72ee..5f2f23ea3 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -2,6 +2,9 @@ namespace Intervention\Image\Gd; +use Intervention\Image\Exception\NotSupportedException; +use Intervention\Image\Image; + class Driver extends \Intervention\Image\AbstractDriver { /** @@ -13,7 +16,7 @@ class Driver extends \Intervention\Image\AbstractDriver public function __construct(Decoder $decoder = null, Encoder $encoder = null) { if ( ! $this->coreAvailable()) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "GD Library extension not available with this PHP installation." ); } @@ -34,7 +37,7 @@ public function newImage($width, $height, $background = null) { // create empty resource $core = imagecreatetruecolor($width, $height); - $image = new \Intervention\Image\Image(new static, $core); + $image = new Image(new static, $core); // set background color $background = new Color($background); diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index a3fd2f127..d8cc02178 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Gd; +use Intervention\Image\Exception\NotSupportedException; + class Encoder extends \Intervention\Image\AbstractEncoder { /** @@ -58,7 +60,7 @@ protected function processGif() protected function processWebp() { if ( ! function_exists('imagewebp')) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Webp format is not supported by PHP installation." ); } @@ -79,7 +81,7 @@ protected function processWebp() */ protected function processTiff() { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "TIFF format is not supported by Gd Driver." ); } @@ -91,7 +93,7 @@ protected function processTiff() */ protected function processBmp() { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "BMP format is not supported by Gd Driver." ); } @@ -103,7 +105,7 @@ protected function processBmp() */ protected function processIco() { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "ICO format is not supported by Gd Driver." ); } @@ -115,7 +117,7 @@ protected function processIco() */ protected function processPsd() { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "PSD format is not supported by Gd Driver." ); } diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 828c7af70..47a7b15bd 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Gd; +use Intervention\Image\Exception\NotSupportedException; use Intervention\Image\Image; class Font extends \Intervention\Image\AbstractFont @@ -27,7 +28,7 @@ private function getInternalFont() $internalfont = is_numeric($internalfont) ? $internalfont : false; if ( ! in_array($internalfont, [1, 2, 3, 4, 5])) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( sprintf('Internal GD font (%s) not available. Use only 1-5.', $internalfont) ); } diff --git a/src/Intervention/Image/Gd/Shapes/EllipseShape.php b/src/Intervention/Image/Gd/Shapes/EllipseShape.php index c2e81c04e..78e5e4a59 100644 --- a/src/Intervention/Image/Gd/Shapes/EllipseShape.php +++ b/src/Intervention/Image/Gd/Shapes/EllipseShape.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Gd\Shapes; -use Intervention\Image\Image; +use Intervention\Image\AbstractShape; use Intervention\Image\Gd\Color; +use Intervention\Image\Image; -class EllipseShape extends \Intervention\Image\AbstractShape +class EllipseShape extends AbstractShape { /** * Width of ellipse in pixels diff --git a/src/Intervention/Image/Gd/Shapes/LineShape.php b/src/Intervention/Image/Gd/Shapes/LineShape.php index c1eedd688..ea38b513e 100644 --- a/src/Intervention/Image/Gd/Shapes/LineShape.php +++ b/src/Intervention/Image/Gd/Shapes/LineShape.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Gd\Shapes; -use Intervention\Image\Image; +use Intervention\Image\AbstractShape; use Intervention\Image\Gd\Color; +use Intervention\Image\Image; -class LineShape extends \Intervention\Image\AbstractShape +class LineShape extends AbstractShape { /** * Starting point x-coordinate of line diff --git a/src/Intervention/Image/Gd/Shapes/PolygonShape.php b/src/Intervention/Image/Gd/Shapes/PolygonShape.php index b11c6a11b..5e14df40e 100644 --- a/src/Intervention/Image/Gd/Shapes/PolygonShape.php +++ b/src/Intervention/Image/Gd/Shapes/PolygonShape.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Gd\Shapes; -use Intervention\Image\Image; +use Intervention\Image\AbstractShape; use Intervention\Image\Gd\Color; +use Intervention\Image\Image; -class PolygonShape extends \Intervention\Image\AbstractShape +class PolygonShape extends AbstractShape { /** * Array of points of polygon diff --git a/src/Intervention/Image/Gd/Shapes/RectangleShape.php b/src/Intervention/Image/Gd/Shapes/RectangleShape.php index 724494963..5f69a7f92 100644 --- a/src/Intervention/Image/Gd/Shapes/RectangleShape.php +++ b/src/Intervention/Image/Gd/Shapes/RectangleShape.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Gd\Shapes; -use Intervention\Image\Image; +use Intervention\Image\AbstractShape; use Intervention\Image\Gd\Color; +use Intervention\Image\Image; -class RectangleShape extends \Intervention\Image\AbstractShape +class RectangleShape extends AbstractShape { /** * X-Coordinate of top-left point diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index e577e7e83..2c4d3140a 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -2,6 +2,8 @@ namespace Intervention\Image; +use Intervention\Image\Exception\NotWritableException; +use Intervention\Image\Exception\RuntimeException; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -131,7 +133,7 @@ public function save($path = null, $quality = null) $path = is_null($path) ? $this->basePath() : $path; if (is_null($path)) { - throw new Exception\NotWritableException( + throw new NotWritableException( "Can't write to undefined path." ); } @@ -140,7 +142,7 @@ public function save($path = null, $quality = null) $saved = @file_put_contents($path, $data); if ($saved === false) { - throw new Exception\NotWritableException( + throw new NotWritableException( "Can't write image data to path ({$path})" ); } @@ -216,7 +218,7 @@ public function getBackup($name = null) $name = is_null($name) ? 'default' : $name; if ( ! $this->backupExists($name)) { - throw new \Intervention\Image\Exception\RuntimeException( + throw new RuntimeException( "Backup with name ({$name}) not available. Call backup() before reset()." ); } diff --git a/src/Intervention/Image/ImageManager.php b/src/Intervention/Image/ImageManager.php index 7c9f1c91f..324fe7fa3 100644 --- a/src/Intervention/Image/ImageManager.php +++ b/src/Intervention/Image/ImageManager.php @@ -3,6 +3,8 @@ namespace Intervention\Image; use Closure; +use Intervention\Image\Exception\MissingDependencyException; +use Intervention\Image\Exception\NotSupportedException; class ImageManager { @@ -90,7 +92,7 @@ public function cache(Closure $callback, $lifetime = null, $returnObj = false) return $imagecache->get($lifetime, $returnObj); } - throw new \Intervention\Image\Exception\MissingDependencyException( + throw new MissingDependencyException( "Please install package intervention/imagecache before running this function." ); } @@ -110,7 +112,7 @@ private function createDriver() return new $driverclass; } - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Driver ({$drivername}) could not be instantiated." ); } @@ -119,7 +121,7 @@ private function createDriver() return $this->config['driver']; } - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Unknown driver type." ); } @@ -132,7 +134,7 @@ private function createDriver() private function checkRequirements() { if ( ! function_exists('finfo_buffer')) { - throw new \Intervention\Image\Exception\MissingDependencyException( + throw new MissingDependencyException( "PHP Fileinfo extension must be installed/enabled to use Intervention Image." ); } diff --git a/src/Intervention/Image/ImageServiceProvider.php b/src/Intervention/Image/ImageServiceProvider.php index 1e6351df7..e106d06cb 100644 --- a/src/Intervention/Image/ImageServiceProvider.php +++ b/src/Intervention/Image/ImageServiceProvider.php @@ -3,6 +3,8 @@ namespace Intervention\Image; use Illuminate\Support\ServiceProvider; +use Laravel\Lumen\Application as LumenApplication; +use Illuminate\Foundation\Application as IlluminateApplication; class ImageServiceProvider extends ServiceProvider { @@ -62,9 +64,9 @@ public function register() */ private function getProvider() { - if ($this->app instanceof \Laravel\Lumen\Application) { + if ($this->app instanceof LumenApplication) { $provider = '\Intervention\Image\ImageServiceProviderLumen'; - } elseif (version_compare(\Illuminate\Foundation\Application::VERSION, '5.0', '<')) { + } elseif (version_compare(IlluminateApplication::VERSION, '5.0', '<')) { $provider = '\Intervention\Image\ImageServiceProviderLaravel4'; } else { $provider = '\Intervention\Image\ImageServiceProviderLaravel5'; diff --git a/src/Intervention/Image/Imagick/Color.php b/src/Intervention/Image/Imagick/Color.php index 91db8217c..a41ff3a6e 100644 --- a/src/Intervention/Image/Imagick/Color.php +++ b/src/Intervention/Image/Imagick/Color.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick; -class Color extends \Intervention\Image\AbstractColor +use Intervention\Image\AbstractColor; + +class Color extends AbstractColor { /** * ImagickPixel containing current color information @@ -178,7 +180,7 @@ public function getRgba() * @param int $tolerance * @return boolean */ - public function differs(\Intervention\Image\AbstractColor $color, $tolerance = 0) + public function differs(AbstractColor $color, $tolerance = 0) { $color_tolerance = round($tolerance * 2.55); $alpha_tolerance = round($tolerance); diff --git a/src/Intervention/Image/Imagick/Commands/BackupCommand.php b/src/Intervention/Image/Imagick/Commands/BackupCommand.php index 60dedb2b7..76b4f72bb 100644 --- a/src/Intervention/Image/Imagick/Commands/BackupCommand.php +++ b/src/Intervention/Image/Imagick/Commands/BackupCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class BackupCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class BackupCommand extends AbstractCommand { /** * Saves a backups of current state of image core diff --git a/src/Intervention/Image/Imagick/Commands/BlurCommand.php b/src/Intervention/Image/Imagick/Commands/BlurCommand.php index b037c1516..d2533e0ea 100644 --- a/src/Intervention/Image/Imagick/Commands/BlurCommand.php +++ b/src/Intervention/Image/Imagick/Commands/BlurCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class BlurCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class BlurCommand extends AbstractCommand { /** * Applies blur effect on image diff --git a/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php b/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php index eefb1802c..03ac8478d 100644 --- a/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php +++ b/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class BrightnessCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class BrightnessCommand extends AbstractCommand { /** * Changes image brightness diff --git a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php b/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php index 51142be27..3a6506f6d 100644 --- a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class ColorizeCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class ColorizeCommand extends AbstractCommand { /** * Changes balance of different RGB color channels diff --git a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php b/src/Intervention/Image/Imagick/Commands/ContrastCommand.php index 113a2186c..c4847c61d 100644 --- a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ContrastCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class ContrastCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class ContrastCommand extends AbstractCommand { /** * Changes contrast of image diff --git a/src/Intervention/Image/Imagick/Commands/CropCommand.php b/src/Intervention/Image/Imagick/Commands/CropCommand.php index 21c7184b7..618edea7d 100644 --- a/src/Intervention/Image/Imagick/Commands/CropCommand.php +++ b/src/Intervention/Image/Imagick/Commands/CropCommand.php @@ -2,10 +2,12 @@ namespace Intervention\Image\Imagick\Commands; +use Intervention\Image\Commands\AbstractCommand; +use Intervention\Image\Exception\InvalidArgumentException; use Intervention\Image\Point; use Intervention\Image\Size; -class CropCommand extends \Intervention\Image\Commands\AbstractCommand +class CropCommand extends AbstractCommand { /** * Crop an image instance @@ -21,7 +23,7 @@ public function execute($image) $y = $this->argument(3)->type('digit')->value(); if (is_null($width) || is_null($height)) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( "Width and height of cutout needs to be defined." ); } diff --git a/src/Intervention/Image/Imagick/Commands/DestroyCommand.php b/src/Intervention/Image/Imagick/Commands/DestroyCommand.php index d98062d8a..58c9556e4 100644 --- a/src/Intervention/Image/Imagick/Commands/DestroyCommand.php +++ b/src/Intervention/Image/Imagick/Commands/DestroyCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class DestroyCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class DestroyCommand extends AbstractCommand { /** * Destroys current image core and frees up memory diff --git a/src/Intervention/Image/Imagick/Commands/ExifCommand.php b/src/Intervention/Image/Imagick/Commands/ExifCommand.php index 924522caf..521b38b09 100644 --- a/src/Intervention/Image/Imagick/Commands/ExifCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ExifCommand.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Imagick\Commands; use Intervention\Image\Commands\ExifCommand as BaseCommand; +use Intervention\Image\Exception\NotSupportedException; class ExifCommand extends BaseCommand { @@ -35,7 +36,7 @@ public function execute($image) $core = $image->getCore(); if ( ! method_exists($core, 'getImageProperties')) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Reading Exif data is not supported by this PHP installation." ); } diff --git a/src/Intervention/Image/Imagick/Commands/FillCommand.php b/src/Intervention/Image/Imagick/Commands/FillCommand.php index bfac75fbf..82baac532 100644 --- a/src/Intervention/Image/Imagick/Commands/FillCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FillCommand.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Imagick\Commands; +use Intervention\Image\Commands\AbstractCommand; +use Intervention\Image\Exception\NotReadableException; use Intervention\Image\Image; -use Intervention\Image\Imagick\Decoder; use Intervention\Image\Imagick\Color; +use Intervention\Image\Imagick\Decoder; -class FillCommand extends \Intervention\Image\Commands\AbstractCommand +class FillCommand extends AbstractCommand { /** * Fills image with color or pattern @@ -27,7 +29,7 @@ public function execute($image) $source = new Decoder; $filling = $source->init($filling); - } catch (\Intervention\Image\Exception\NotReadableException $e) { + } catch (NotReadableException $e) { // set solid color filling $filling = new Color($filling); diff --git a/src/Intervention/Image/Imagick/Commands/FitCommand.php b/src/Intervention/Image/Imagick/Commands/FitCommand.php index f2c60d219..6d62ba677 100644 --- a/src/Intervention/Image/Imagick/Commands/FitCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FitCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Imagick\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Size; -class FitCommand extends \Intervention\Image\Commands\AbstractCommand +class FitCommand extends AbstractCommand { /** * Crops and resized an image at the same time diff --git a/src/Intervention/Image/Imagick/Commands/FlipCommand.php b/src/Intervention/Image/Imagick/Commands/FlipCommand.php index cdb03c52d..abae16ad1 100644 --- a/src/Intervention/Image/Imagick/Commands/FlipCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FlipCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class FlipCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class FlipCommand extends AbstractCommand { /** * Mirrors an image diff --git a/src/Intervention/Image/Imagick/Commands/GammaCommand.php b/src/Intervention/Image/Imagick/Commands/GammaCommand.php index e70cbdd36..200515f3b 100644 --- a/src/Intervention/Image/Imagick/Commands/GammaCommand.php +++ b/src/Intervention/Image/Imagick/Commands/GammaCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class GammaCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class GammaCommand extends AbstractCommand { /** * Applies gamma correction to a given image diff --git a/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php b/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php index 65b1078d1..ccccedb03 100644 --- a/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php +++ b/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Imagick\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Size; -class GetSizeCommand extends \Intervention\Image\Commands\AbstractCommand +class GetSizeCommand extends AbstractCommand { /** * Reads size of given image instance in pixels diff --git a/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php b/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php index bb3f47260..df0ff5b57 100644 --- a/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php +++ b/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class GreyscaleCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class GreyscaleCommand extends AbstractCommand { /** * Turns an image into a greyscale version diff --git a/src/Intervention/Image/Imagick/Commands/InsertCommand.php b/src/Intervention/Image/Imagick/Commands/InsertCommand.php index 542feb2ae..2a9974367 100644 --- a/src/Intervention/Image/Imagick/Commands/InsertCommand.php +++ b/src/Intervention/Image/Imagick/Commands/InsertCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class InsertCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class InsertCommand extends AbstractCommand { /** * Insert another image into given image diff --git a/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php b/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php index 82cddd4c6..913cab7e0 100644 --- a/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php +++ b/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class InterlaceCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class InterlaceCommand extends AbstractCommand { /** * Toggles interlaced encoding mode diff --git a/src/Intervention/Image/Imagick/Commands/InvertCommand.php b/src/Intervention/Image/Imagick/Commands/InvertCommand.php index 125fbddee..1d134301b 100644 --- a/src/Intervention/Image/Imagick/Commands/InvertCommand.php +++ b/src/Intervention/Image/Imagick/Commands/InvertCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class InvertCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class InvertCommand extends AbstractCommand { /** * Inverts colors of an image diff --git a/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php b/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php index 7308180f6..16f9b82e9 100644 --- a/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php +++ b/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class LimitColorsCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class LimitColorsCommand extends AbstractCommand { /** * Reduces colors of a given image diff --git a/src/Intervention/Image/Imagick/Commands/MaskCommand.php b/src/Intervention/Image/Imagick/Commands/MaskCommand.php index 2dfc697b0..af9d6b2f5 100644 --- a/src/Intervention/Image/Imagick/Commands/MaskCommand.php +++ b/src/Intervention/Image/Imagick/Commands/MaskCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class MaskCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class MaskCommand extends AbstractCommand { /** * Applies an alpha mask to an image diff --git a/src/Intervention/Image/Imagick/Commands/OpacityCommand.php b/src/Intervention/Image/Imagick/Commands/OpacityCommand.php index 57ed006b8..b4708d899 100644 --- a/src/Intervention/Image/Imagick/Commands/OpacityCommand.php +++ b/src/Intervention/Image/Imagick/Commands/OpacityCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class OpacityCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class OpacityCommand extends AbstractCommand { /** * Defines opacity of an image diff --git a/src/Intervention/Image/Imagick/Commands/PickColorCommand.php b/src/Intervention/Image/Imagick/Commands/PickColorCommand.php index 8daa0f95a..978a1284e 100644 --- a/src/Intervention/Image/Imagick/Commands/PickColorCommand.php +++ b/src/Intervention/Image/Imagick/Commands/PickColorCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Imagick\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Imagick\Color; -class PickColorCommand extends \Intervention\Image\Commands\AbstractCommand +class PickColorCommand extends AbstractCommand { /** * Read color information from a certain position diff --git a/src/Intervention/Image/Imagick/Commands/PixelCommand.php b/src/Intervention/Image/Imagick/Commands/PixelCommand.php index b9e6d395d..6eb6ae715 100644 --- a/src/Intervention/Image/Imagick/Commands/PixelCommand.php +++ b/src/Intervention/Image/Imagick/Commands/PixelCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Imagick\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Imagick\Color; -class PixelCommand extends \Intervention\Image\Commands\AbstractCommand +class PixelCommand extends AbstractCommand { /** * Draws one pixel to a given image diff --git a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php b/src/Intervention/Image/Imagick/Commands/PixelateCommand.php index 75f2218f5..6fe794913 100644 --- a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php +++ b/src/Intervention/Image/Imagick/Commands/PixelateCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class PixelateCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class PixelateCommand extends AbstractCommand { /** * Applies a pixelation effect to a given image diff --git a/src/Intervention/Image/Imagick/Commands/ResetCommand.php b/src/Intervention/Image/Imagick/Commands/ResetCommand.php index ee5a2cdf3..77b7f3366 100644 --- a/src/Intervention/Image/Imagick/Commands/ResetCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ResetCommand.php @@ -2,7 +2,10 @@ namespace Intervention\Image\Imagick\Commands; -class ResetCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; +use Intervention\Image\Exception\RuntimeException; + +class ResetCommand extends AbstractCommand { /** * Resets given image to its backup state @@ -30,7 +33,7 @@ public function execute($image) return true; } - throw new \Intervention\Image\Exception\RuntimeException( + throw new RuntimeException( "Backup not available. Call backup({$backupName}) before reset()." ); } diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php index f394c15dc..ecf076106 100644 --- a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class ResizeCanvasCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class ResizeCanvasCommand extends AbstractCommand { /** * Resizes image boundaries diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCommand.php index 9ccc202c2..3d4dc5bed 100644 --- a/src/Intervention/Image/Imagick/Commands/ResizeCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ResizeCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class ResizeCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class ResizeCommand extends AbstractCommand { /** * Resizes image dimensions diff --git a/src/Intervention/Image/Imagick/Commands/RotateCommand.php b/src/Intervention/Image/Imagick/Commands/RotateCommand.php index c4503a6ca..edaef0d3f 100644 --- a/src/Intervention/Image/Imagick/Commands/RotateCommand.php +++ b/src/Intervention/Image/Imagick/Commands/RotateCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Imagick\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Imagick\Color; -class RotateCommand extends \Intervention\Image\Commands\AbstractCommand +class RotateCommand extends AbstractCommand { /** * Rotates image counter clockwise diff --git a/src/Intervention/Image/Imagick/Commands/SharpenCommand.php b/src/Intervention/Image/Imagick/Commands/SharpenCommand.php index 4f2fc8c29..bc5e393f2 100644 --- a/src/Intervention/Image/Imagick/Commands/SharpenCommand.php +++ b/src/Intervention/Image/Imagick/Commands/SharpenCommand.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Imagick\Commands; -class SharpenCommand extends \Intervention\Image\Commands\AbstractCommand +use Intervention\Image\Commands\AbstractCommand; + +class SharpenCommand extends AbstractCommand { /** * Sharpen image diff --git a/src/Intervention/Image/Imagick/Commands/TrimCommand.php b/src/Intervention/Image/Imagick/Commands/TrimCommand.php index f09590320..bdc897be7 100644 --- a/src/Intervention/Image/Imagick/Commands/TrimCommand.php +++ b/src/Intervention/Image/Imagick/Commands/TrimCommand.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Imagick\Commands; +use Intervention\Image\Commands\AbstractCommand; use Intervention\Image\Imagick\Color; -class TrimCommand extends \Intervention\Image\Commands\AbstractCommand +class TrimCommand extends AbstractCommand { /** * Trims away parts of an image diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php index 8cf2420ae..f4dde9a84 100644 --- a/src/Intervention/Image/Imagick/Decoder.php +++ b/src/Intervention/Image/Imagick/Decoder.php @@ -2,9 +2,12 @@ namespace Intervention\Image\Imagick; +use Intervention\Image\AbstractDecoder; +use Intervention\Image\Exception\NotReadableException; +use Intervention\Image\Exception\NotSupportedException; use Intervention\Image\Image; -class Decoder extends \Intervention\Image\AbstractDecoder +class Decoder extends AbstractDecoder { /** * Initiates new image from path in filesystem @@ -45,7 +48,7 @@ public function initFromPath($path) */ public function initFromGdResource($resource) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( 'Imagick driver is unable to init from GD resource.' ); } @@ -84,7 +87,7 @@ public function initFromBinary($binary) $core->readImageBlob($binary); } catch (\ImagickException $e) { - throw new \Intervention\Image\Exception\NotReadableException( + throw new NotReadableException( "Unable to read image from binary data.", 0, $e diff --git a/src/Intervention/Image/Imagick/Driver.php b/src/Intervention/Image/Imagick/Driver.php index 1f2fcc6c1..bb26ca0fe 100644 --- a/src/Intervention/Image/Imagick/Driver.php +++ b/src/Intervention/Image/Imagick/Driver.php @@ -2,7 +2,11 @@ namespace Intervention\Image\Imagick; -class Driver extends \Intervention\Image\AbstractDriver +use Intervention\Image\AbstractDriver; +use Intervention\Image\Exception\NotSupportedException; +use Intervention\Image\Image; + +class Driver extends AbstractDriver { /** * Creates new instance of driver @@ -13,7 +17,7 @@ class Driver extends \Intervention\Image\AbstractDriver public function __construct(Decoder $decoder = null, Encoder $encoder = null) { if ( ! $this->coreAvailable()) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "ImageMagick module not available with this PHP installation." ); } @@ -42,7 +46,7 @@ public function newImage($width, $height, $background = null) $core->setColorspace(\Imagick::COLORSPACE_UNDEFINED); // build image - $image = new \Intervention\Image\Image(new static, $core); + $image = new Image(new static, $core); return $image; } diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index 6c20410b2..ac7345e4a 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -2,7 +2,10 @@ namespace Intervention\Image\Imagick; -class Encoder extends \Intervention\Image\AbstractEncoder +use Intervention\Image\AbstractEncoder; +use Intervention\Image\Exception\NotSupportedException; + +class Encoder extends AbstractEncoder { /** * Processes and returns encoded image as JPEG string @@ -69,7 +72,7 @@ protected function processGif() protected function processWebp() { if ( ! \Imagick::queryFormats('WEBP')) { - throw new \Intervention\Image\Exception\NotSupportedException( + throw new NotSupportedException( "Webp format is not supported by Imagick installation." ); } diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php index 62d2ef243..0c5002a47 100644 --- a/src/Intervention/Image/Imagick/Font.php +++ b/src/Intervention/Image/Imagick/Font.php @@ -2,9 +2,11 @@ namespace Intervention\Image\Imagick; +use Intervention\Image\AbstractFont; +use Intervention\Image\Exception\RuntimeException; use Intervention\Image\Image; -class Font extends \Intervention\Image\AbstractFont +class Font extends AbstractFont { /** * Draws font to given image at given position @@ -25,7 +27,7 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) if ($this->hasApplicableFontFile()) { $draw->setFont($this->file); } else { - throw new \Intervention\Image\Exception\RuntimeException( + throw new RuntimeException( "Font file must be provided to apply text to image." ); } @@ -95,7 +97,7 @@ public function getBoxSize() if ($this->hasApplicableFontFile()) { $draw->setFont($this->file); } else { - throw new \Intervention\Image\Exception\RuntimeException( + throw new RuntimeException( "Font file must be provided to apply text to image." ); } diff --git a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php b/src/Intervention/Image/Imagick/Shapes/EllipseShape.php index 1b89942fd..99694b97b 100644 --- a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php +++ b/src/Intervention/Image/Imagick/Shapes/EllipseShape.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Imagick\Shapes; +use Intervention\Image\AbstractShape; use Intervention\Image\Image; use Intervention\Image\Imagick\Color; -class EllipseShape extends \Intervention\Image\AbstractShape +class EllipseShape extends AbstractShape { /** * Width of ellipse in pixels diff --git a/src/Intervention/Image/Imagick/Shapes/LineShape.php b/src/Intervention/Image/Imagick/Shapes/LineShape.php index 638b97b67..0d6b3297d 100644 --- a/src/Intervention/Image/Imagick/Shapes/LineShape.php +++ b/src/Intervention/Image/Imagick/Shapes/LineShape.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Imagick\Shapes; +use Intervention\Image\AbstractShape; use Intervention\Image\Image; use Intervention\Image\Imagick\Color; -class LineShape extends \Intervention\Image\AbstractShape +class LineShape extends AbstractShape { /** * Starting point x-coordinate of line diff --git a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php index 4c087b374..45f44ad88 100644 --- a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php +++ b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Imagick\Shapes; +use Intervention\Image\AbstractShape; use Intervention\Image\Image; use Intervention\Image\Imagick\Color; -class PolygonShape extends \Intervention\Image\AbstractShape +class PolygonShape extends AbstractShape { /** * Array of points of polygon diff --git a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php b/src/Intervention/Image/Imagick/Shapes/RectangleShape.php index 2d1a09f5c..1166d77b7 100644 --- a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php +++ b/src/Intervention/Image/Imagick/Shapes/RectangleShape.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Imagick\Shapes; +use Intervention\Image\AbstractShape; use Intervention\Image\Image; use Intervention\Image\Imagick\Color; -class RectangleShape extends \Intervention\Image\AbstractShape +class RectangleShape extends AbstractShape { /** * X-Coordinate of top-left point diff --git a/src/Intervention/Image/Response.php b/src/Intervention/Image/Response.php index e883cac45..8e7940811 100644 --- a/src/Intervention/Image/Response.php +++ b/src/Intervention/Image/Response.php @@ -2,6 +2,9 @@ namespace Intervention\Image; +use Illuminate\Support\Facades\Response as IlluminateResponse; +use Symfony\Component\HttpFoundation\Response as SymfonyResponse; + class Response { /** @@ -53,13 +56,13 @@ public function make() if (function_exists('app') && is_a($app = app(), 'Illuminate\Foundation\Application')) { - $response = \Illuminate\Support\Facades\Response::make($data); + $response = IlluminateResponse::make($data); $response->header('Content-Type', $mime); $response->header('Content-Length', $length); } elseif (class_exists('\Symfony\Component\HttpFoundation\Response')) { - $response = \Symfony\Component\HttpFoundation\Response::create($data); + $response = SymfonyResponse::create($data); $response->headers->set('Content-Type', $mime); $response->headers->set('Content-Length', $length); diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php index b01376a80..46ae3135e 100644 --- a/src/Intervention/Image/Size.php +++ b/src/Intervention/Image/Size.php @@ -3,6 +3,7 @@ namespace Intervention\Image; use Closure; +use Intervention\Image\Exception\InvalidArgumentException; class Size { @@ -104,7 +105,7 @@ public function getRatio() public function resize($width, $height, Closure $callback = null) { if (is_null($width) && is_null($height)) { - throw new \Intervention\Image\Exception\InvalidArgumentException( + throw new InvalidArgumentException( "Width or height needs to be defined." ); } From ab55fbb04013b88886311330e70cacddea758b1c Mon Sep 17 00:00:00 2001 From: Babichev Maxim Date: Fri, 7 Jun 2019 18:04:55 +0300 Subject: [PATCH 0096/1667] added the $format parameter to the save method --- src/Intervention/Image/Image.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 2c4d3140a..7d5a59b7d 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -126,9 +126,10 @@ public function encode($format = null, $quality = 90) * * @param string $path * @param int $quality + * @param string $format * @return \Intervention\Image\Image */ - public function save($path = null, $quality = null) + public function save($path = null, $quality = null, $format = null) { $path = is_null($path) ? $this->basePath() : $path; @@ -138,7 +139,11 @@ public function save($path = null, $quality = null) ); } - $data = $this->encode(pathinfo($path, PATHINFO_EXTENSION), $quality); + if ($format === null) { + $format = pathinfo($path, PATHINFO_EXTENSION); + } + + $data = $this->encode($format, $quality); $saved = @file_put_contents($path, $data); if ($saved === false) { From dcae042e714de9827a1b977085c73c1ed0ffd5fc Mon Sep 17 00:00:00 2001 From: Babichev Maxim Date: Mon, 10 Jun 2019 11:07:42 +0300 Subject: [PATCH 0097/1667] add test unit --- tests/ImageTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/ImageTest.php b/tests/ImageTest.php index 79a387afa..870658cff 100644 --- a/tests/ImageTest.php +++ b/tests/ImageTest.php @@ -1,6 +1,7 @@ new Intervention\Image\Imagick\Driver()]; + $manager = new ImageManager($config); + + $image = $manager->make('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); + $this->assertInstanceOf('Intervention\Image\Image', $image); + $this->assertInstanceOf('Imagick', $image->getCore()); + + $gifCore = $image->getCore(); + $this->assertEquals($gifCore->getImageMimeType(), 'image/gif'); + $image->save($save_as, null, 'jpg'); + + $this->assertEquals(\mime_content_type($save_as), 'image/jpeg'); + @unlink($save_as); + } + public function testIsEncoded() { $image = $this->getTestImage(); From d5611555bf5ab988c662ac506f2a775e355445b5 Mon Sep 17 00:00:00 2001 From: Tomoyuki Tsujimoto Date: Tue, 3 Sep 2019 05:10:27 +0900 Subject: [PATCH 0098/1667] Multibyte text is not rendered In some server configration, Multibyte text is not rendered ( is not encoded properly). PDF Library DOMPDF is doing like this way. `$text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8'); ` https://github.com/dompdf/dompdf/blob/313f466e77fe63df0c53c20b54540945a0b165cd/src/Adapter/GD.php#L866 This is error log. ``` imagettfbbox(): any2eucjp(): invalid code in input string {"exception":"[object] (ErrorException(code: 0): imagettfbbox(): any2eucjp(): invalid code in input string at /usr/home/webapp/vendor/intervention/image/src/Intervention/Image/Gd/Font.php:85) [stacktrace] #0 [internal function]: Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError(2, 'imagettfbbox():...', '/usr/home/...', 85, Array) #1 /usr/home/webapp/vendor/intervention/image/src/Intervention/Image/Gd/Font.php(85): imagettfbbox(38, 0, '/usr/home/...', '\\xE3\\x82\\x8A') #2 /usr/home/webapp/vendor/intervention/image/src/Intervention/Image/Gd/Font.php(140): Intervention\\Image\\Gd\\Font->getBoxSize() #3 /usr/home/webapp/vendor/intervention/image/src/Intervention/Image/Commands/TextCommand.php(30): Intervention\\Image\\Gd\\Font->applyToImage(Object(Intervention\\Image\\Image), 4, 15) #4 /usr/home/webapp/vendor/intervention/image/src/Intervention/Image/AbstractDriver.php(92): Intervention\\Image\\Commands\\TextCommand->execute(Object(Intervention\\Image\\Image)) #5 /usr/home/webapp/vendor/intervention/image/src/Intervention/Image/Image.php(106): Intervention\\Image\\AbstractDriver->executeCommand(Object(Intervention\\Image\\Image), 'text', Array) ... ``` --- src/Intervention/Image/Gd/Font.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 47a7b15bd..440d2c524 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -82,6 +82,8 @@ public function getBoxSize() if ($this->hasApplicableFontFile()) { + $this->text = mb_encode_numericentity($this->text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8'); + // get bounding box with angle 0 $box = imagettfbbox($this->getPointSize(), 0, $this->file, $this->text); From 5f5ab3b0d10b9e2176ba3d93a798111fc65ed215 Mon Sep 17 00:00:00 2001 From: Tomoyuki Tsujimoto Date: Tue, 3 Sep 2019 05:17:03 +0900 Subject: [PATCH 0099/1667] Update Font.php --- src/Intervention/Image/Gd/Font.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 440d2c524..142b79440 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -82,6 +82,11 @@ public function getBoxSize() if ($this->hasApplicableFontFile()) { + // imagettfbbox() converts numeric entities to their respective + // character. Preserve any originally double encoded entities to be + // represented as is. + // eg: &#160; will render   rather than its character. + $this->text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $this->text); $this->text = mb_encode_numericentity($this->text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8'); // get bounding box with angle 0 From 9cac11949106d8e0b8564c3d42416891092aa89f Mon Sep 17 00:00:00 2001 From: Pierre Grimaud Date: Fri, 4 Oct 2019 20:05:18 +0200 Subject: [PATCH 0100/1667] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06b84e01e..350f471cf 100755 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Refer to the [official documentation](http://image.intervention.io/) to learn mo ## Contributing -Contributions to the Intervention Image library are welcome. Please note the following guidelines before submiting your pull request. +Contributions to the Intervention Image library are welcome. Please note the following guidelines before submitting your pull request. - Follow [PSR-2](http://www.php-fig.org/psr/psr-2/) coding standards. - Write tests for new functions and added features From abbf18d5ab8367f96b3205ca3c89fb2fa598c69e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 2 Nov 2019 10:15:47 +0100 Subject: [PATCH 0101/1667] Laravel 6 / PHP7.3 compatibility --- .../Image/ImageServiceProvider.php | 2 +- ... => ImageServiceProviderLaravelRecent.php} | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) rename src/Intervention/Image/{ImageServiceProviderLaravel5.php => ImageServiceProviderLaravelRecent.php} (82%) diff --git a/src/Intervention/Image/ImageServiceProvider.php b/src/Intervention/Image/ImageServiceProvider.php index e106d06cb..f99fe4a37 100644 --- a/src/Intervention/Image/ImageServiceProvider.php +++ b/src/Intervention/Image/ImageServiceProvider.php @@ -69,7 +69,7 @@ private function getProvider() } elseif (version_compare(IlluminateApplication::VERSION, '5.0', '<')) { $provider = '\Intervention\Image\ImageServiceProviderLaravel4'; } else { - $provider = '\Intervention\Image\ImageServiceProviderLaravel5'; + $provider = '\Intervention\Image\ImageServiceProviderLaravelRecent'; } return new $provider($this->app); diff --git a/src/Intervention/Image/ImageServiceProviderLaravel5.php b/src/Intervention/Image/ImageServiceProviderLaravelRecent.php similarity index 82% rename from src/Intervention/Image/ImageServiceProviderLaravel5.php rename to src/Intervention/Image/ImageServiceProviderLaravelRecent.php index dd40ebd97..8a9bc232e 100644 --- a/src/Intervention/Image/ImageServiceProviderLaravel5.php +++ b/src/Intervention/Image/ImageServiceProviderLaravelRecent.php @@ -4,7 +4,7 @@ use Illuminate\Support\ServiceProvider; -class ImageServiceProviderLaravel5 extends ServiceProvider +class ImageServiceProviderLaravelRecent extends ServiceProvider { /** * Determines if Intervention Imagecache is installed @@ -48,7 +48,7 @@ public function register() // create image $app->singleton('image', function ($app) { - return new ImageManager($app['config']->get('image')); + return new ImageManager($this->getImageConfig($app)); }); $app->alias('image', 'Intervention\Image\ImageManager'); @@ -86,4 +86,21 @@ protected function bootstrapImageCache() ])->where(['filename' => $filename_pattern]); } } + + /** + * Return image configuration as array + * + * @param Application $app + * @return array + */ + private function getImageConfig($app) + { + $config = $app['config']->get('image'); + + if (is_null($config)) { + return []; + } + + return $config; + } } From e2d032dc15c6f0b926173cc32b38e449426b93a6 Mon Sep 17 00:00:00 2001 From: Damien Grandi Date: Wed, 6 Nov 2019 12:09:51 +0100 Subject: [PATCH 0102/1667] Bugfix for DataUrl containing newlines inside data Remove the newlines before doing a Regex check inside the decodeDataUrl method in the same way that is the isBase64 method doing. --- src/Intervention/Image/AbstractDecoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index de770c328..397c206ff 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -290,7 +290,7 @@ private function decodeDataUrl($data_url) } $pattern = "/^data:(?:image\/[a-zA-Z\-\.]+)(?:charset=\".+\")?;base64,(?P.+)$/"; - preg_match($pattern, $data_url, $matches); + preg_match($pattern, str_replace(["\n", "\r"], '', $data_url), $matches); if (is_array($matches) && array_key_exists('data', $matches)) { return base64_decode($matches['data']); From 5e84b29174eb1744add1fd86d4c4df14ee791971 Mon Sep 17 00:00:00 2001 From: Koen Date: Thu, 7 Nov 2019 11:49:47 +0100 Subject: [PATCH 0103/1667] add support for bmp --- src/Intervention/Image/Gd/Decoder.php | 18 +++++++++++++++++- src/Intervention/Image/Gd/Encoder.php | 16 +++++++++++++--- tests/EncoderTest.php | 18 ++++++++++-------- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 6d2d7b4d3..82521ab45 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -55,9 +55,25 @@ public function initFromPath($path) $core = @imagecreatefromwebp($path); break; + case 'image/bmp': + case 'image/ms-bmp': + case 'image/x-bitmap': + case 'image/x-bmp': + case 'image/x-ms-bmp': + case 'image/x-win-bitmap': + case 'image/x-windows-bmp': + case 'image/x-xbitmap': + if (! function_exists('imagecreatefrombmp')) { + throw new NotReadableException( + "Unsupported image type. GD/PHP installation does not support BMP format." + ); + } + $core = @imagecreatefrombmp($path); + break; + default: throw new NotReadableException( - "Unsupported image type. GD driver is only able to decode JPG, PNG, GIF or WebP files." + "Unsupported image type. GD driver is only able to decode JPG, PNG, GIF, WebP or BMP files." ); } diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index d8cc02178..81739ef4c 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -93,9 +93,19 @@ protected function processTiff() */ protected function processBmp() { - throw new NotSupportedException( - "BMP format is not supported by Gd Driver." - ); + if ( ! function_exists('imagebmp')) { + throw new NotSupportedException( + "BMP format is not supported by PHP installation." + ); + } + + ob_start(); + imagebmp($this->image->getCore()); + $this->image->mime = defined('IMAGETYPE_BMP') ? image_type_to_mime_type(IMAGETYPE_BMP) : 'image/bmp'; + $buffer = ob_get_contents(); + ob_end_clean(); + + return $buffer; } /** diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index 834618c3a..4ba02e3f4 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -73,16 +73,18 @@ public function testProcessTiffGd() $this->assertInstanceOf('Intervention\Image\Image', $img); } - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ public function testProcessBmpGd() { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'bmp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); + if (function_exists('imagebmp')) { + $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $encoder = new GdEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $image->shouldReceive('getCore')->once()->andReturn($core); + $image->shouldReceive('setEncoded')->once()->andReturn($image); + $img = $encoder->process($image, 'bmp', 90); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertEquals('image/x-ms-bmp; charset=binary', $this->getMime($encoder->result)); + } } /** From eb16ed033c1d98e50548addb1e0972f127e0e5f3 Mon Sep 17 00:00:00 2001 From: shaneiseminger Date: Thu, 7 Nov 2019 09:38:08 -0700 Subject: [PATCH 0104/1667] Exception message doesn't always report format correctly --- src/Intervention/Image/AbstractEncoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index d3e59eaa4..f59231091 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -171,7 +171,7 @@ public function process(Image $image, $format = null, $quality = null) default: throw new NotSupportedException( - "Encoding format ({$format}) is not supported." + "Encoding format ({$this->format}) is not supported." ); } From 1110a23da524aafa120c15d83228bf537b5714e8 Mon Sep 17 00:00:00 2001 From: shaneiseminger Date: Thu, 7 Nov 2019 09:39:45 -0700 Subject: [PATCH 0105/1667] Support JP2 images --- src/Intervention/Image/AbstractEncoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index f59231091..ef133f48d 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -117,6 +117,7 @@ public function process(Image $image, $format = null, $quality = null) case 'jpg': case 'jpeg': + case 'image/jp2': case 'image/jpg': case 'image/jpeg': case 'image/pjpeg': From 509f5a30237899d04c386b9151c64298788f097c Mon Sep 17 00:00:00 2001 From: thomas4Bitcraft <44415527+thomas4Bitcraft@users.noreply.github.com> Date: Wed, 1 Apr 2020 22:10:53 +0200 Subject: [PATCH 0106/1667] Update Encoder.php --- src/Intervention/Image/Gd/Encoder.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index d8cc02178..5e2194621 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -66,6 +66,9 @@ protected function processWebp() } ob_start(); + imagepalettetotruecolor($this->image->getCore()); + imagealphablending($this->image->getCore(), true); + imagesavealpha($this->image->getCore(), true); imagewebp($this->image->getCore(), null, $this->quality); $this->image->mime = defined('IMAGETYPE_WEBP') ? image_type_to_mime_type(IMAGETYPE_WEBP) : 'image/webp'; $buffer = ob_get_contents(); From 9355b63bcffd079fc5d37fe86f89801d4ae24f7b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 6 Jul 2020 21:01:01 +0200 Subject: [PATCH 0107/1667] Added funding info --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..5dc8cee3f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [Intervention] From 0e5bd8c4a5f9134a7934ab2a0917c181a46e33e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C5=A1a=20Stamenkovi=C4=87?= Date: Thu, 9 Jul 2020 11:21:49 +0200 Subject: [PATCH 0108/1667] Better error message in Decoder --- src/Intervention/Image/Gd/Decoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 6d2d7b4d3..11a0bc0b5 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -57,7 +57,7 @@ public function initFromPath($path) default: throw new NotReadableException( - "Unsupported image type. GD driver is only able to decode JPG, PNG, GIF or WebP files." + sprintf("Unsupported image type %s. GD driver is only able to decode JPG, PNG, GIF or WebP files.", strtolower($mime)) ); } From a79916a009120b5aea9379c203b18960eba13045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Garc=C3=ADa?= Date: Fri, 4 Sep 2020 10:46:51 +0200 Subject: [PATCH 0109/1667] Allow rotation angle with decimals --- src/Intervention/Image/Gd/Commands/RotateCommand.php | 2 +- src/Intervention/Image/Imagick/Commands/RotateCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/RotateCommand.php b/src/Intervention/Image/Gd/Commands/RotateCommand.php index e02b91760..682ec0d4a 100644 --- a/src/Intervention/Image/Gd/Commands/RotateCommand.php +++ b/src/Intervention/Image/Gd/Commands/RotateCommand.php @@ -20,7 +20,7 @@ public function execute($image) $color = new Color($color); // restrict rotations beyond 360 degrees, since the end result is the same - $angle %= 360; + $angle = fmod($angle, 360); // rotate image $image->setCore(imagerotate($image->getCore(), $angle, $color->getInt())); diff --git a/src/Intervention/Image/Imagick/Commands/RotateCommand.php b/src/Intervention/Image/Imagick/Commands/RotateCommand.php index edaef0d3f..b3e12a535 100644 --- a/src/Intervention/Image/Imagick/Commands/RotateCommand.php +++ b/src/Intervention/Image/Imagick/Commands/RotateCommand.php @@ -20,7 +20,7 @@ public function execute($image) $color = new Color($color); // restrict rotations beyond 360 degrees, since the end result is the same - $angle %= 360; + $angle = fmod($angle, 360); // rotate image $image->getCore()->rotateImage($color->getPixel(), ($angle * -1)); From 032263954ca2a5258fb5fe7492962c1a16ca9f3c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 6 Nov 2020 16:12:52 +0100 Subject: [PATCH 0110/1667] Added Paypal info --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 5dc8cee3f..612406cbc 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ github: [Intervention] +custom: https://www.paypal.me/interventionphp From 3ab514c80461929e9f7010f1dfbf69287beb6fc8 Mon Sep 17 00:00:00 2001 From: Edgard Lorraine Messias Date: Wed, 11 Nov 2020 11:01:28 -0300 Subject: [PATCH 0111/1667] Fixed exif data from non image path (close #343, close #745, close #764) --- src/Intervention/Image/Commands/ExifCommand.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/Commands/ExifCommand.php b/src/Intervention/Image/Commands/ExifCommand.php index 7103f2fd3..063df6ef6 100644 --- a/src/Intervention/Image/Commands/ExifCommand.php +++ b/src/Intervention/Image/Commands/ExifCommand.php @@ -28,7 +28,17 @@ public function execute($image) // try to read exif data from image file try { - $data = @exif_read_data($image->dirname . '/' . $image->basename); + if ($image->dirname && $image->basename) { + $stream = $image->dirname . '/' . $image->basename; + } elseif (version_compare(PHP_VERSION, '7.2.0', '>=')) { + // https://www.php.net/manual/en/function.exif-read-data.php#refsect1-function.exif-read-data-changelog + $stream = $image->stream()->detach(); + } else { + // https://bugs.php.net/bug.php?id=65187 + $stream = $image->encode('data-url')->encoded; + } + + $data = @exif_read_data($stream); if (!is_null($key) && is_array($data)) { $data = array_key_exists($key, $data) ? $data[$key] : false; From 9fb8364727db74ac354b7e6169fb8dcdc232e219 Mon Sep 17 00:00:00 2001 From: freshleafmedia <10062339+freshleafmedia@users.noreply.github.com> Date: Thu, 12 Nov 2020 08:51:24 +0000 Subject: [PATCH 0112/1667] Adds support for the AVIF format --- src/Intervention/Image/AbstractEncoder.php | 12 ++++++++++++ src/Intervention/Image/Gd/Encoder.php | 12 ++++++++++++ src/Intervention/Image/Imagick/Encoder.php | 22 ++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index d3e59eaa4..f88c55e78 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -84,6 +84,13 @@ abstract protected function processIco(); */ abstract protected function processWebp(); + /** + * Processes and returns image as Avif encoded string + * + * @return string + */ + abstract protected function processAvif(); + /** * Process a given image * @@ -168,6 +175,11 @@ public function process(Image $image, $format = null, $quality = null) case 'image/x-webp': $this->result = $this->processWebp(); break; + + case 'avif': + case 'image/avif': + $this->result = $this->processAvif(); + break; default: throw new NotSupportedException( diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index d8cc02178..f289a6467 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -121,4 +121,16 @@ protected function processPsd() "PSD format is not supported by Gd Driver." ); } + + /** + * Processes and returns encoded image as AVIF string + * + * @return string + */ + protected function processAvif() + { + throw new NotSupportedException( + "AVIF format is not supported by Gd Driver." + ); + } } diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index ac7345e4a..956e5fdd6 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -170,4 +170,26 @@ protected function processPsd() return $imagick->getImagesBlob(); } + + protected function processAvif() + { + if ( ! \Imagick::queryFormats('AVIF')) { + throw new NotSupportedException( + "AVIF format is not supported by Imagick installation." + ); + } + + $format = 'avif'; + $compression = \Imagick::COMPRESSION_UNDEFINED; + + $imagick = $this->image->getCore(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick->setCompressionQuality($this->quality); + $imagick->setImageCompressionQuality($this->quality); + + return $imagick->getImagesBlob(); + } } From 4af40e23bb39f4008f3cfe66fd7a9baa48e808d8 Mon Sep 17 00:00:00 2001 From: freshleafmedia <10062339+freshleafmedia@users.noreply.github.com> Date: Thu, 12 Nov 2020 17:22:52 +0000 Subject: [PATCH 0113/1667] Adds PHP 7.4 to the Travis build --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index de890615d..9fc3fdeef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ php: - 7.1 - 7.2 - 7.3 + - 7.4 - nightly - hhvm From 9c1188b4d8e554256b97729170f7a753a2b7af88 Mon Sep 17 00:00:00 2001 From: freshleafmedia <10062339+freshleafmedia@users.noreply.github.com> Date: Thu, 12 Nov 2020 17:28:51 +0000 Subject: [PATCH 0114/1667] Adds tests --- tests/EncoderTest.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index 834618c3a..3a18e14a8 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -61,6 +61,18 @@ public function testProcessWebpGd() } } + /** + * @expectedException \Intervention\Image\Exception\NotSupportedException + */ + public function testProcessAvifGd() + { + $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $encoder = new GdEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $img = $encoder->process($image, 'avif', 90); + $this->assertInstanceOf('Intervention\Image\Image', $img); + } + /** * @expectedException \Intervention\Image\Exception\NotSupportedException */ @@ -180,6 +192,16 @@ public function testProcessWebpImagick() $img = $encoder->process($image, 'webp', 90); } + /** + * @expectedException \Intervention\Image\Exception\NotSupportedException + */ + public function testProcessAvifImagick() + { + $encoder = new ImagickEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $img = $encoder->process($image, 'avif', 90); + } + public function testProcessTiffImagick() { $core = $this->getImagickMock('tiff'); From 8bc2946a7d01a131bdaec20670d01212d9981484 Mon Sep 17 00:00:00 2001 From: freshleafmedia <10062339+freshleafmedia@users.noreply.github.com> Date: Mon, 16 Nov 2020 09:59:56 +0000 Subject: [PATCH 0115/1667] Updates the distribution to Xenial to get PHP7.4 GD extension working --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9fc3fdeef..05ead6713 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: php sudo: false -dist: trusty +dist: xenial php: - 5.4 From 8283c144a65be60330494f82cba83c680d716c72 Mon Sep 17 00:00:00 2001 From: freshleafmedia <10062339+freshleafmedia@users.noreply.github.com> Date: Mon, 16 Nov 2020 10:23:01 +0000 Subject: [PATCH 0116/1667] Updates Travis to use compatible OSes --- .travis.yml | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05ead6713..5e792b9a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,19 +2,22 @@ language: php sudo: false -dist: xenial - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - 7.4 - - nightly - - hhvm +jobs: + include: + - dist: trusty + php: + - 5.4 + - 5.5 + - dist: xenial + php: + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - 7.3 + - 7.4 + - nightly + - hhvm matrix: allow_failures: From f10307f99f8250b9bf10dffd3bd9cff753932076 Mon Sep 17 00:00:00 2001 From: freshleafmedia <10062339+freshleafmedia@users.noreply.github.com> Date: Mon, 16 Nov 2020 13:41:31 +0000 Subject: [PATCH 0117/1667] Fixes matrix key --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5e792b9a4..e7bee7e1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,11 +18,9 @@ jobs: - 7.4 - nightly - hhvm - -matrix: - allow_failures: - - php: nightly - - php: hhvm + allow_failures: + - php: nightly + - php: hhvm before_script: - printf "\n" | pecl install imagick From d435098e6ed251c697fa55cd6ac53aff5307dbab Mon Sep 17 00:00:00 2001 From: freshleafmedia <10062339+freshleafmedia@users.noreply.github.com> Date: Mon, 16 Nov 2020 13:43:50 +0000 Subject: [PATCH 0118/1667] Expands all jobs --- .travis.yml | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index e7bee7e1e..86ddce950 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,19 +5,25 @@ sudo: false jobs: include: - dist: trusty - php: - - 5.4 - - 5.5 - - dist: xenial - php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - 7.4 - - nightly - - hhvm + php: 5.4 + - dist: trusty + php: 5.5 + - dist: xenial + php: 5.6 + - dist: xenial + php: 7.0 + - dist: xenial + php: 7.1 + - dist: xenial + php: 7.2 + - dist: xenial + php: 7.3 + - dist: xenial + php: 7.4 + - dist: xenial + php: nightly + - dist: xenial + php: hhvm allow_failures: - php: nightly - php: hhvm From f9a78a2ea40b66321483631bd67c8e70d7a51cea Mon Sep 17 00:00:00 2001 From: freshleafmedia <10062339+freshleafmedia@users.noreply.github.com> Date: Mon, 16 Nov 2020 14:12:31 +0000 Subject: [PATCH 0119/1667] Bumps the PHPUnit version to support PHP 7.4 and fixes test --- composer.json | 2 +- tests/FileTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d7df4a236..1e2b66737 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "guzzlehttp/psr7": "~1.1" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.7", + "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15", "mockery/mockery": "~0.9.2" }, "suggest": { diff --git a/tests/FileTest.php b/tests/FileTest.php index 820a18af8..e1c01fd24 100644 --- a/tests/FileTest.php +++ b/tests/FileTest.php @@ -19,7 +19,7 @@ public function testSetFileInfoFromPath() public function testBasePath() { $file = new File; - $this->assertNull(null, $file->basePath()); + $this->assertNull(null, $file->basePath() ?: ''); $file->dirname = 'foo'; $file->basename = 'bar'; From 1ddc37817022780082799af864144c8881683084 Mon Sep 17 00:00:00 2001 From: Ahmed Hdeawy Date: Tue, 24 Nov 2020 14:57:09 +0200 Subject: [PATCH 0120/1667] FIx Issue happen in png images by different Palette --- src/Intervention/Image/Gd/Encoder.php | 1 + tests/EncoderTest.php | 15 +++++++++++++++ tests/images/black-friday.png | Bin 0 -> 4964 bytes 3 files changed, 16 insertions(+) create mode 100644 tests/images/black-friday.png diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index d8cc02178..175ddd402 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -66,6 +66,7 @@ protected function processWebp() } ob_start(); + imagepalettetotruecolor($this->image->getCore()); imagewebp($this->image->getCore(), null, $this->quality); $this->image->mime = defined('IMAGETYPE_WEBP') ? image_type_to_mime_type(IMAGETYPE_WEBP) : 'image/webp'; $buffer = ob_get_contents(); diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index 834618c3a..5445b6a3e 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -60,6 +60,21 @@ public function testProcessWebpGd() $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); } } + + public function testProcessWebpGdWithUnSupportedPalette() + { + if (function_exists('imagewebp')) { + $core = imagecreatefrompng(__DIR__.'/images/black-friday.png'); + $encoder = new GdEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $image->shouldReceive('getCore')->once()->andReturn($core); + $image->shouldReceive('setEncoded')->once()->andReturn($image); + $img = $encoder->process($image, 'webp', 90); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); + } + } + /** * @expectedException \Intervention\Image\Exception\NotSupportedException diff --git a/tests/images/black-friday.png b/tests/images/black-friday.png new file mode 100644 index 0000000000000000000000000000000000000000..129c9e9459f0dcee89f1a9f4a25e89b3e9d50a10 GIT binary patch literal 4964 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 Date: Thu, 3 Dec 2020 09:29:12 +0800 Subject: [PATCH 0121/1667] fix envoy 426 repsonse --- src/Intervention/Image/AbstractDecoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index de770c328..d3e0e74a5 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -69,6 +69,7 @@ public function initFromUrl($url) $options = [ 'http' => [ 'method'=>"GET", + 'protocol_version'=>1.1, // force use HTTP 1.1 for service mesh environment with envoy 'header'=>"Accept-language: en\r\n". "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2\r\n" ] From d0fd6e7036eafe2d1f7750ab9626bc964c6b3daf Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Sun, 22 Nov 2020 02:24:37 +0700 Subject: [PATCH 0122/1667] PHP 8: Add support for GdImage resource objects In [PHP 8.0, `gd` resources are changed to `\GdImage` class objects](https://php.watch/versions/8.0/gdimage). This changes the `\Intervention\Image\AbstractDecoder::isGdResource` method to accept `\GdImage` objects as well as `gd` resources. --- src/Intervention/Image/AbstractDecoder.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index de770c328..8e68a3d94 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -140,6 +140,10 @@ public function isGdResource() return (get_resource_type($this->data) == 'gd'); } + if ($this->data instanceof \GdImage) { + return true; + } + return false; } From 5eb91d81bedf09539261fdf8beba16416c49cb10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20QUIAIOS?= <13620819+squiaios@users.noreply.github.com> Date: Tue, 13 Apr 2021 11:01:26 +0200 Subject: [PATCH 0123/1667] Process JFIF format using JPEG process --- src/Intervention/Image/AbstractEncoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index d3e59eaa4..50729a74d 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -120,6 +120,7 @@ public function process(Image $image, $format = null, $quality = null) case 'image/jpg': case 'image/jpeg': case 'image/pjpeg': + case 'image/jfif': $this->result = $this->processJpeg(); break; From 153b05bea5aa254bcf33de647d446806b0c3a599 Mon Sep 17 00:00:00 2001 From: rjp-thijs <71867303+rjp-thijs@users.noreply.github.com> Date: Fri, 14 May 2021 16:03:15 +0200 Subject: [PATCH 0124/1667] Fix incorrect docblock in AbstractFont (Strinf -> String) --- src/Intervention/Image/AbstractFont.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 0b9b97998..516572e36 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -73,7 +73,7 @@ abstract public function getBoxSize(); /** * Create a new instance of Font * - * @param Strinf $text Text to be written + * @param String $text Text to be written */ public function __construct($text = null) { From d6ad5dbc3a4d1b8ce71ca150cab477159422a128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jair=20Cueva=20J=C3=BAnior?= Date: Mon, 7 Jun 2021 17:14:37 -0300 Subject: [PATCH 0125/1667] Add support to equal sign - '=' - on URL Updated filename regex pattern to allow the equal sign as being part of the filename portion of the route, otherwise it would fall on 404 error. --- src/Intervention/Image/ImageServiceProviderLaravelRecent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/ImageServiceProviderLaravelRecent.php b/src/Intervention/Image/ImageServiceProviderLaravelRecent.php index 8a9bc232e..17a84d0ac 100644 --- a/src/Intervention/Image/ImageServiceProviderLaravelRecent.php +++ b/src/Intervention/Image/ImageServiceProviderLaravelRecent.php @@ -77,7 +77,7 @@ protected function bootstrapImageCache() // imagecache route if (is_string(config('imagecache.route'))) { - $filename_pattern = '[ \w\\.\\/\\-\\@\(\)]+'; + $filename_pattern = '[ \w\\.\\/\\-\\@\(\)\=]+'; // route to access template applied image file $app['router']->get(config('imagecache.route').'/{template}/{filename}', [ From 8285741c768608bdda44766175cb26c165c967cf Mon Sep 17 00:00:00 2001 From: Moamen Eltouny <16774083+RaggiTech@users.noreply.github.com> Date: Fri, 2 Jul 2021 12:51:20 +0200 Subject: [PATCH 0126/1667] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d7df4a236..f6240e04f 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "require": { "php": ">=5.4.0", "ext-fileinfo": "*", - "guzzlehttp/psr7": "~1.1" + "guzzlehttp/psr7": "~1.1 || ^2.0" }, "require-dev": { "phpunit/phpunit": "^4.8 || ^5.7", From a5b24de3bf87186266061a0dedd2921da4152c09 Mon Sep 17 00:00:00 2001 From: Artem Davletshin <49448966+davlet42@users.noreply.github.com> Date: Sun, 18 Jul 2021 20:58:58 +0300 Subject: [PATCH 0127/1667] Update StreamCommand.php https://docs.guzzlephp.org/en/stable/psr7.html#streams --- src/Intervention/Image/Commands/StreamCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Commands/StreamCommand.php b/src/Intervention/Image/Commands/StreamCommand.php index 111c47569..6d659147d 100644 --- a/src/Intervention/Image/Commands/StreamCommand.php +++ b/src/Intervention/Image/Commands/StreamCommand.php @@ -16,10 +16,10 @@ public function execute($image) $format = $this->argument(0)->value(); $quality = $this->argument(1)->between(0, 100)->value(); - $this->setOutput(\GuzzleHttp\Psr7\stream_for( + $this->setOutput(\GuzzleHttp\Psr7\Utils::streamFor( $image->encode($format, $quality)->getEncoded() )); return true; } -} \ No newline at end of file +} From e2b8aebe3e5baf1e3db61a0f5227502313d74b29 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 22 Jul 2021 16:31:32 +0200 Subject: [PATCH 0128/1667] Backward compatibility for StreamCommand --- .../Image/Commands/StreamCommand.php | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/Commands/StreamCommand.php b/src/Intervention/Image/Commands/StreamCommand.php index 6d659147d..bc2ff9efb 100644 --- a/src/Intervention/Image/Commands/StreamCommand.php +++ b/src/Intervention/Image/Commands/StreamCommand.php @@ -15,11 +15,25 @@ public function execute($image) { $format = $this->argument(0)->value(); $quality = $this->argument(1)->between(0, 100)->value(); + $data = $image->encode($format, $quality)->getEncoded(); - $this->setOutput(\GuzzleHttp\Psr7\Utils::streamFor( - $image->encode($format, $quality)->getEncoded() - )); + $this->setOutput($this->getStream($data)); return true; } + + /** + * Create stream from given data + * + * @param string $data + * @return \Psr\Http\Message\StreamInterface + */ + protected function getStream($data) + { + if (class_exists(\GuzzleHttp\Psr7\Utils::class)) { + return \GuzzleHttp\Psr7\Utils::streamFor($data); // guzzlehttp/psr7 >= 2.0 + } + + return \GuzzleHttp\Psr7\stream_for($data); // guzzlehttp/psr7 < 2.0 + } } From 3ddcc2d55dbe4b1a4cb93a5cefc62582eb1665a8 Mon Sep 17 00:00:00 2001 From: Michiel Vugteveen Date: Fri, 23 Jul 2021 20:45:18 +0200 Subject: [PATCH 0129/1667] Add HEIC support --- src/Intervention/Image/AbstractEncoder.php | 7 +++++++ src/Intervention/Image/Gd/Encoder.php | 11 +++++++++++ src/Intervention/Image/Imagick/Encoder.php | 22 ++++++++++++++++++++++ tests/EncoderTest.php | 22 ++++++++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index 7c2e35905..dab783ad2 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -91,6 +91,13 @@ abstract protected function processWebp(); */ abstract protected function processAvif(); + /** + * Processes and returns image as Heic encoded string + * + * @return string + */ + abstract protected function processHeic(); + /** * Process a given image * diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index ab6d1f05f..b196de169 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -144,6 +144,17 @@ protected function processAvif() { throw new NotSupportedException( "AVIF format is not supported by Gd Driver." + + + /** + * Processes and returns encoded image as HEIC string + * + * @return string + */ + protected function processHeic() + { + throw new NotSupportedException( + "HEIC format is not supported by Gd Driver." ); } } diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index df7b5b82f..922fb2656 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -204,4 +204,26 @@ protected function processAvif() return $imagick->getImagesBlob(); } + + protected function processHeic() + { + if ( ! \Imagick::queryFormats('HEIC')) { + throw new NotSupportedException( + "HEIC format is not supported by Imagick installation." + ); + } + + $format = 'heic'; + $compression = \Imagick::COMPRESSION_UNDEFINED; + + $imagick = $this->image->getCore(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick->setCompressionQuality($this->quality); + $imagick->setImageCompressionQuality($this->quality); + + return $imagick->getImagesBlob(); + } } diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index a85732732..f04b50a42 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -88,6 +88,18 @@ public function testProcessAvifGd() $this->assertInstanceOf('Intervention\Image\Image', $img); } + /** + * @expectedException \Intervention\Image\Exception\NotSupportedException + */ + public function testProcessHeicGd() + { + $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $encoder = new GdEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $img = $encoder->process($image, 'heic', 90); + $this->assertInstanceOf('Intervention\Image\Image', $img); + } + /** * @expectedException \Intervention\Image\Exception\NotSupportedException */ @@ -219,6 +231,16 @@ public function testProcessAvifImagick() $img = $encoder->process($image, 'avif', 90); } + /** + * @expectedException \Intervention\Image\Exception\NotSupportedException + */ + public function testProcessHeicImagick() + { + $encoder = new ImagickEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $img = $encoder->process($image, 'heic', 90); + } + public function testProcessTiffImagick() { $core = $this->getImagickMock('tiff'); From e7baf44b6f408bd8d04c94b4d50ce683738d0808 Mon Sep 17 00:00:00 2001 From: Michiel Vugteveen Date: Fri, 23 Jul 2021 20:46:42 +0200 Subject: [PATCH 0130/1667] fix typo --- src/Intervention/Image/Gd/Encoder.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index b196de169..87b916255 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -57,6 +57,11 @@ protected function processGif() return $buffer; } + /** + * Processes and returns encoded image as WEBP string + * + * @return string + */ protected function processWebp() { if ( ! function_exists('imagewebp')) { @@ -146,7 +151,7 @@ protected function processAvif() "AVIF format is not supported by Gd Driver." - /** + /** * Processes and returns encoded image as HEIC string * * @return string From 19a9eb912389e122a5cd2588c9689488434ad028 Mon Sep 17 00:00:00 2001 From: Michiel Vugteveen Date: Fri, 23 Jul 2021 20:47:23 +0200 Subject: [PATCH 0131/1667] fix typo --- src/Intervention/Image/Imagick/Encoder.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index 922fb2656..feb8a9d53 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -183,6 +183,11 @@ protected function processPsd() return $imagick->getImagesBlob(); } + /** + * Processes and returns encoded image as AVIF string + * + * @return string + */ protected function processAvif() { if ( ! \Imagick::queryFormats('AVIF')) { @@ -205,6 +210,11 @@ protected function processAvif() return $imagick->getImagesBlob(); } + /** + * Processes and returns encoded image as HEIC string + * + * @return string + */ protected function processHeic() { if ( ! \Imagick::queryFormats('HEIC')) { From 84708d2dac8740b20af00b8c32a1436aeb06cdb3 Mon Sep 17 00:00:00 2001 From: Michiel Vugteveen Date: Fri, 23 Jul 2021 20:49:30 +0200 Subject: [PATCH 0132/1667] fix typo --- src/Intervention/Image/Gd/Encoder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index 87b916255..12cb0f60d 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -149,6 +149,8 @@ protected function processAvif() { throw new NotSupportedException( "AVIF format is not supported by Gd Driver." + ); + } /** From 96a53ff57741c93d585d69027a1def4e2b8aa4a2 Mon Sep 17 00:00:00 2001 From: Michiel Vugteveen Date: Fri, 23 Jul 2021 20:51:51 +0200 Subject: [PATCH 0133/1667] fix typo --- src/Intervention/Image/Gd/Encoder.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index 12cb0f60d..00c4b69a8 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -152,7 +152,6 @@ protected function processAvif() ); } - /** * Processes and returns encoded image as HEIC string * From 4013c59d018c339a97d66226027de4a50903cf6b Mon Sep 17 00:00:00 2001 From: Michiel Vugteveen Date: Fri, 23 Jul 2021 22:10:51 +0200 Subject: [PATCH 0134/1667] add heic --- src/Intervention/Image/AbstractEncoder.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index dab783ad2..77544653f 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -189,6 +189,11 @@ public function process(Image $image, $format = null, $quality = null) case 'image/avif': $this->result = $this->processAvif(); break; + + case 'heic': + case 'image/heic': + $this->result = $this->processHeic(); + break; default: throw new NotSupportedException( From b34ec45d8bee10f3bb05d30df1e539b2df71328c Mon Sep 17 00:00:00 2001 From: jackie111222 <62723172+jackie111222@users.noreply.github.com> Date: Sun, 25 Jul 2021 12:22:11 +0200 Subject: [PATCH 0135/1667] Update IptcCommand.php Extending IPTC of ReleaseDate and ReleaseTime attributes --- src/Intervention/Image/Commands/IptcCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Intervention/Image/Commands/IptcCommand.php b/src/Intervention/Image/Commands/IptcCommand.php index ae953d200..b78a2a8e4 100644 --- a/src/Intervention/Image/Commands/IptcCommand.php +++ b/src/Intervention/Image/Commands/IptcCommand.php @@ -36,6 +36,8 @@ public function execute($image) $data['Category'] = isset($iptc["2#015"][0]) ? $iptc["2#015"][0] : null; $data['Subcategories'] = isset($iptc["2#020"][0]) ? $iptc["2#020"][0] : null; $data['Keywords'] = isset($iptc["2#025"][0]) ? $iptc["2#025"] : null; + $data['ReleaseDate'] = isset($iptc["2#030"][0]) ? $iptc["2#030"][0] : null; + $data['ReleaseTime'] = isset($iptc["2#035"][0]) ? $iptc["2#035"][0] : null; $data['SpecialInstructions'] = isset($iptc["2#040"][0]) ? $iptc["2#040"][0] : null; $data['CreationDate'] = isset($iptc["2#055"][0]) ? $iptc["2#055"][0] : null; $data['CreationTime'] = isset($iptc["2#060"][0]) ? $iptc["2#060"][0] : null; From c5f8f45391b182232067a9759f696b407266c609 Mon Sep 17 00:00:00 2001 From: Daniel Hudson Date: Thu, 29 Jul 2021 13:23:17 +0100 Subject: [PATCH 0136/1667] Add PHP8 GDImage object support --- src/Intervention/Image/Gd/Commands/ResetCommand.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/ResetCommand.php b/src/Intervention/Image/Gd/Commands/ResetCommand.php index 12c41bf6e..16f1aef08 100644 --- a/src/Intervention/Image/Gd/Commands/ResetCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResetCommand.php @@ -16,8 +16,9 @@ class ResetCommand extends AbstractCommand public function execute($image) { $backupName = $this->argument(0)->value(); - - if (is_resource($backup = $image->getBackup($backupName))) { + $backup = $image->getBackup($backupName) + + if (is_resource($backup) || $backup instanceof \GdImage) { // destroy current resource imagedestroy($image->getCore()); From aeda35ffb92e8433a0f0c878c947f87e14b09bb9 Mon Sep 17 00:00:00 2001 From: Daniel Hudson Date: Thu, 29 Jul 2021 13:37:48 +0100 Subject: [PATCH 0137/1667] Missing semi-colon. --- src/Intervention/Image/Gd/Commands/ResetCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Commands/ResetCommand.php b/src/Intervention/Image/Gd/Commands/ResetCommand.php index 16f1aef08..a68b75eaf 100644 --- a/src/Intervention/Image/Gd/Commands/ResetCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResetCommand.php @@ -16,7 +16,7 @@ class ResetCommand extends AbstractCommand public function execute($image) { $backupName = $this->argument(0)->value(); - $backup = $image->getBackup($backupName) + $backup = $image->getBackup($backupName); if (is_resource($backup) || $backup instanceof \GdImage) { From 9fed536930ecc6a24233bc1499f8b71c50dc86fa Mon Sep 17 00:00:00 2001 From: "M. Vugteveen" Date: Fri, 13 Aug 2021 11:59:36 +0200 Subject: [PATCH 0138/1667] remove duplicate and add image/heif --- src/Intervention/Image/AbstractEncoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index 77544653f..160208052 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -148,7 +148,6 @@ public function process(Image $image, $format = null, $quality = null) $this->result = $this->processTiff(); break; - case 'bmp': case 'bmp': case 'ms-bmp': case 'x-bitmap': @@ -192,6 +191,7 @@ public function process(Image $image, $format = null, $quality = null) case 'heic': case 'image/heic': + case 'image/heif': $this->result = $this->processHeic(); break; From f15efc8ccfc77cc3f3e524b6f989adb40a151734 Mon Sep 17 00:00:00 2001 From: Jordan Hoff Date: Mon, 30 Aug 2021 09:35:36 -0500 Subject: [PATCH 0139/1667] Use correct docbock returns in AbstractFont.php --- src/Intervention/Image/AbstractFont.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index ed2ea0478..35c1825a6 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -69,7 +69,7 @@ abstract class AbstractFont * @return boolean */ abstract public function applyToImage(Image $image, $posx = 0, $posy = 0); - + /** * Calculates bounding box of current font setting * @@ -91,7 +91,7 @@ public function __construct($text = null) * Set text to be written * * @param String $text - * @return void + * @return self */ public function text($text) { @@ -114,7 +114,7 @@ public function getText() * Set font size in pixels * * @param int $size - * @return void + * @return self */ public function size($size) { @@ -137,7 +137,7 @@ public function getSize() * Set color of text to be written * * @param mixed $color - * @return void + * @return self */ public function color($color) { @@ -160,7 +160,7 @@ public function getColor() * Set rotation angle of text * * @param int $angle - * @return void + * @return self */ public function angle($angle) { @@ -183,7 +183,7 @@ public function getAngle() * Set horizontal text alignment * * @param string $align - * @return void + * @return self */ public function align($align) { @@ -206,7 +206,7 @@ public function getAlign() * Set vertical text alignment * * @param string $valign - * @return void + * @return self */ public function valign($valign) { @@ -250,7 +250,7 @@ public function getKerning() * Set path to font file * * @param string $file - * @return void + * @return self */ public function file($file) { From 9a823065a1d8b48f4c95034f5fcce536d010cbe3 Mon Sep 17 00:00:00 2001 From: Gijsdev <38570931+gijsdev@users.noreply.github.com> Date: Thu, 16 Sep 2021 00:15:38 +0200 Subject: [PATCH 0140/1667] Add AVIF support to GD Encoder --- src/Intervention/Image/Gd/Encoder.php | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index 00c4b69a8..559d60dcc 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -147,9 +147,23 @@ protected function processPsd() */ protected function processAvif() { - throw new NotSupportedException( - "AVIF format is not supported by Gd Driver." - ); + if ( ! function_exists('imageavif')) { + throw new NotSupportedException( + "AVIF format is not supported by PHP installation." + ); + } + + ob_start(); + $resource = $this->image->getCore(); + imagepalettetotruecolor($resource); + imagealphablending($resource, true); + imagesavealpha($resource, true); + imageavif($resource, null, $this->quality); + $this->image->mime = defined('IMAGETYPE_AVIF') ? image_type_to_mime_type(IMAGETYPE_AVIF) : 'image/avif'; + $buffer = ob_get_contents(); + ob_end_clean(); + + return $buffer; } /** From a823bdc7c44bc63559240b05890e30483857eda4 Mon Sep 17 00:00:00 2001 From: Gijsdev <38570931+gijsdev@users.noreply.github.com> Date: Thu, 16 Sep 2021 00:16:05 +0200 Subject: [PATCH 0141/1667] Add GD AVIF test --- tests/EncoderTest.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index f04b50a42..f68818df7 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -60,7 +60,7 @@ public function testProcessWebpGd() $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); } } - + public function testProcessWebpGdWithUnSupportedPalette() { if (function_exists('imagewebp')) { @@ -75,17 +75,18 @@ public function testProcessWebpGdWithUnSupportedPalette() } } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ public function testProcessAvifGd() { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'avif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); + if (function_exists('imageavif')) { + $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $encoder = new GdEncoder; + $image = Mockery::mock('\Intervention\Image\Image'); + $image->shouldReceive('getCore')->once()->andReturn($core); + $image->shouldReceive('setEncoded')->once()->andReturn($image); + $img = $encoder->process($image, 'avif', 90); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertEquals('image/avif; charset=binary', $this->getMime($encoder->result)); + } } /** From 8294abad6909c16311cc2c294487a3febd59e5f2 Mon Sep 17 00:00:00 2001 From: Muhammad Faiz Halim Date: Mon, 4 Oct 2021 12:28:19 +0800 Subject: [PATCH 0142/1667] Missing 'jfif' format encoder. Only have 'image/jfif' format. --- src/Intervention/Image/AbstractEncoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php index 160208052..c25f8bffd 100644 --- a/src/Intervention/Image/AbstractEncoder.php +++ b/src/Intervention/Image/AbstractEncoder.php @@ -131,6 +131,7 @@ public function process(Image $image, $format = null, $quality = null) case 'jpg': case 'jpeg': + case 'jfif': case 'image/jp2': case 'image/jpg': case 'image/jpeg': From 95e72b877b38f96e52996a0ab367ebc64a249395 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 14:32:05 +0200 Subject: [PATCH 0143/1667] Version 3 prototype --- .travis.yml | 36 - LICENSE | 9 - README.md | 55 - composer.json | 37 +- phpunit.xml | 1 - provides.json | 11 - src/Collection.php | 137 ++ src/Drivers/Abstract/AbstractColor.php | 17 + src/Drivers/Abstract/AbstractFrame.php | 8 + src/Drivers/Abstract/AbstractImage.php | 81 + src/Drivers/Abstract/AbstractInputHandler.php | 17 + .../Abstract/Decoders/AbstractDecoder.php | 47 + .../Abstract/Encoders/AbstractEncoder.php | 41 + src/Drivers/Gd/Color.php | 57 + src/Drivers/Gd/Decoders/ArrayColorDecoder.php | 33 + .../Gd/Decoders/Base64ImageDecoder.php | 22 + .../Gd/Decoders/BinaryImageDecoder.php | 55 + .../Gd/Decoders/DataUriImageDecoder.php | 33 + .../Gd/Decoders/FilePathImageDecoder.php | 28 + src/Drivers/Gd/Encoders/GifEncoder.php | 33 + src/Drivers/Gd/Encoders/JpegEncoder.php | 17 + src/Drivers/Gd/Frame.php | 111 ++ src/Drivers/Gd/Image.php | 36 + src/Drivers/Gd/ImageFactory.php | 19 + src/Drivers/Gd/InputHandler.php | 22 + .../Gd/Modifiers/GreyscaleModifier.php | 18 + src/Drivers/Imagick/Color.php | 53 + .../Imagick/Decoders/ArrayColorDecoder.php | 31 + .../Imagick/Decoders/Base64ImageDecoder.php | 22 + .../Imagick/Decoders/BinaryImageDecoder.php | 34 + .../Imagick/Decoders/DataUriImageDecoder.php | 33 + .../Imagick/Decoders/FilePathImageDecoder.php | 28 + src/Drivers/Imagick/Encoders/GifEncoder.php | 29 + src/Drivers/Imagick/Encoders/JpegEncoder.php | 29 + src/Drivers/Imagick/Frame.php | 83 + src/Drivers/Imagick/Image.php | 35 + src/Drivers/Imagick/InputHandler.php | 22 + .../Imagick/Modifiers/GreyscaleModifier.php | 18 + src/Exceptions/DecoderException.php | 8 + src/Exceptions/NotWritableException.php | 8 + src/Exceptions/RuntimeException.php | 8 + src/Geometry/Point.php | 135 ++ src/Geometry/Size.php | 92 + src/ImageManager.php | 96 + src/Interfaces/CollectionInterface.php | 12 + src/Interfaces/ColorInterface.php | 13 + src/Interfaces/DecoderInterface.php | 8 + src/Interfaces/EncoderInterface.php | 8 + src/Interfaces/FactoryInterface.php | 8 + src/Interfaces/FrameInterface.php | 18 + src/Interfaces/ImageInterface.php | 12 + src/Interfaces/ModifierInterface.php | 8 + src/Interfaces/PointInterface.php | 9 + src/Interfaces/SizeInterface.php | 16 + src/Intervention/Image/AbstractColor.php | 229 --- src/Intervention/Image/AbstractDecoder.php | 364 ---- src/Intervention/Image/AbstractDriver.php | 140 -- src/Intervention/Image/AbstractEncoder.php | 271 --- src/Intervention/Image/AbstractFont.php | 295 --- src/Intervention/Image/AbstractShape.php | 71 - .../Image/Commands/AbstractCommand.php | 81 - src/Intervention/Image/Commands/Argument.php | 225 --- .../Image/Commands/ChecksumCommand.php | 29 - .../Image/Commands/CircleCommand.php | 35 - .../Image/Commands/EllipseCommand.php | 36 - .../Image/Commands/ExifCommand.php | 61 - .../Image/Commands/IptcCommand.php | 68 - .../Image/Commands/LineCommand.php | 36 - .../Image/Commands/OrientateCommand.php | 48 - .../Image/Commands/PolygonCommand.php | 49 - .../Image/Commands/PsrResponseCommand.php | 45 - .../Image/Commands/RectangleCommand.php | 36 - .../Image/Commands/ResponseCommand.php | 26 - .../Image/Commands/StreamCommand.php | 39 - .../Image/Commands/TextCommand.php | 34 - src/Intervention/Image/Constraint.php | 92 - .../Image/Exception/ImageException.php | 8 - .../Exception/InvalidArgumentException.php | 8 - .../Exception/MissingDependencyException.php | 8 - .../Image/Exception/NotFoundException.php | 8 - .../Image/Exception/NotReadableException.php | 8 - .../Image/Exception/NotSupportedException.php | 8 - .../Image/Exception/NotWritableException.php | 8 - .../Image/Exception/RuntimeException.php | 8 - src/Intervention/Image/Facades/Image.php | 19 - src/Intervention/Image/File.php | 92 - src/Intervention/Image/Filters/DemoFilter.php | 44 - .../Image/Filters/FilterInterface.php | 16 - src/Intervention/Image/Gd/Color.php | 227 --- .../Image/Gd/Commands/BackupCommand.php | 25 - .../Image/Gd/Commands/BlurCommand.php | 25 - .../Image/Gd/Commands/BrightnessCommand.php | 21 - .../Image/Gd/Commands/ColorizeCommand.php | 29 - .../Image/Gd/Commands/ContrastCommand.php | 21 - .../Image/Gd/Commands/CropCommand.php | 40 - .../Image/Gd/Commands/DestroyCommand.php | 27 - .../Image/Gd/Commands/FillCommand.php | 69 - .../Image/Gd/Commands/FitCommand.php | 32 - .../Image/Gd/Commands/FlipCommand.php | 37 - .../Image/Gd/Commands/GammaCommand.php | 21 - .../Image/Gd/Commands/GetSizeCommand.php | 25 - .../Image/Gd/Commands/GreyscaleCommand.php | 19 - .../Image/Gd/Commands/HeightenCommand.php | 28 - .../Image/Gd/Commands/InsertCommand.php | 34 - .../Image/Gd/Commands/InterlaceCommand.php | 23 - .../Image/Gd/Commands/InvertCommand.php | 19 - .../Image/Gd/Commands/LimitColorsCommand.php | 53 - .../Image/Gd/Commands/MaskCommand.php | 83 - .../Image/Gd/Commands/OpacityCommand.php | 31 - .../Image/Gd/Commands/PickColorCommand.php | 37 - .../Image/Gd/Commands/PixelCommand.php | 25 - .../Image/Gd/Commands/PixelateCommand.php | 21 - .../Image/Gd/Commands/ResetCommand.php | 39 - .../Image/Gd/Commands/ResizeCanvasCommand.php | 83 - .../Image/Gd/Commands/ResizeCommand.php | 84 - .../Image/Gd/Commands/RotateCommand.php | 30 - .../Image/Gd/Commands/SharpenCommand.php | 34 - .../Image/Gd/Commands/TrimCommand.php | 176 -- .../Image/Gd/Commands/WidenCommand.php | 28 - src/Intervention/Image/Gd/Decoder.php | 171 -- src/Intervention/Image/Gd/Driver.php | 89 - src/Intervention/Image/Gd/Encoder.php | 180 -- src/Intervention/Image/Gd/Font.php | 277 --- .../Image/Gd/Shapes/CircleShape.php | 40 - .../Image/Gd/Shapes/EllipseShape.php | 65 - .../Image/Gd/Shapes/LineShape.php | 90 - .../Image/Gd/Shapes/PolygonShape.php | 49 - .../Image/Gd/Shapes/RectangleShape.php | 76 - src/Intervention/Image/Image.php | 370 ---- src/Intervention/Image/ImageManager.php | 142 -- src/Intervention/Image/ImageManagerStatic.php | 88 - .../Image/ImageServiceProvider.php | 87 - .../Image/ImageServiceProviderLaravel4.php | 112 -- .../ImageServiceProviderLaravelRecent.php | 106 - .../Image/ImageServiceProviderLeague.php | 42 - .../Image/ImageServiceProviderLumen.php | 34 - src/Intervention/Image/Imagick/Color.php | 279 --- .../Image/Imagick/Commands/BackupCommand.php | 25 - .../Image/Imagick/Commands/BlurCommand.php | 21 - .../Imagick/Commands/BrightnessCommand.php | 21 - .../Imagick/Commands/ColorizeCommand.php | 44 - .../Imagick/Commands/ContrastCommand.php | 21 - .../Image/Imagick/Commands/CropCommand.php | 45 - .../Image/Imagick/Commands/DestroyCommand.php | 27 - .../Image/Imagick/Commands/ExifCommand.php | 63 - .../Image/Imagick/Commands/FillCommand.php | 105 - .../Image/Imagick/Commands/FitCommand.php | 42 - .../Image/Imagick/Commands/FlipCommand.php | 27 - .../Image/Imagick/Commands/GammaCommand.php | 21 - .../Image/Imagick/Commands/GetSizeCommand.php | 28 - .../Imagick/Commands/GreyscaleCommand.php | 19 - .../Imagick/Commands/HeightenCommand.php | 28 - .../Image/Imagick/Commands/InsertCommand.php | 33 - .../Imagick/Commands/InterlaceCommand.php | 29 - .../Image/Imagick/Commands/InvertCommand.php | 19 - .../Imagick/Commands/LimitColorsCommand.php | 59 - .../Image/Imagick/Commands/MaskCommand.php | 60 - .../Image/Imagick/Commands/OpacityCommand.php | 23 - .../Imagick/Commands/PickColorCommand.php | 30 - .../Image/Imagick/Commands/PixelCommand.php | 31 - .../Imagick/Commands/PixelateCommand.php | 27 - .../Image/Imagick/Commands/ResetCommand.php | 40 - .../Imagick/Commands/ResizeCanvasCommand.php | 91 - .../Image/Imagick/Commands/ResizeCommand.php | 29 - .../Image/Imagick/Commands/RotateCommand.php | 30 - .../Image/Imagick/Commands/SharpenCommand.php | 21 - .../Image/Imagick/Commands/TrimCommand.php | 121 -- .../Image/Imagick/Commands/WidenCommand.php | 28 - src/Intervention/Image/Imagick/Decoder.php | 124 -- src/Intervention/Image/Imagick/Driver.php | 74 - src/Intervention/Image/Imagick/Encoder.php | 239 --- src/Intervention/Image/Imagick/Font.php | 122 -- .../Image/Imagick/Shapes/CircleShape.php | 40 - .../Image/Imagick/Shapes/EllipseShape.php | 66 - .../Image/Imagick/Shapes/LineShape.php | 94 - .../Image/Imagick/Shapes/PolygonShape.php | 81 - .../Image/Imagick/Shapes/RectangleShape.php | 84 - src/Intervention/Image/Point.php | 64 - src/Intervention/Image/Response.php | 78 - src/Intervention/Image/Size.php | 374 ---- src/Traits/CanDecodeDataUri.php | 75 + src/Traits/CanResolveDriverClass.php | 48 + src/Traits/CanValidateBase64.php | 15 + src/Traits/CanValidateColorArray.php | 31 + src/config/config.php | 20 - tests/AbstractColorTest.php | 20 - tests/AbstractCommandTest.php | 48 - tests/AbstractDecoderTest.php | 160 -- tests/AbstractDriverTest.php | 21 - tests/AbstractFontTest.php | 86 - tests/AbstractShapeTest.php | 43 - tests/ArgumentTest.php | 422 ---- tests/BackupCommandTest.php | 65 - tests/BlurCommandTest.php | 34 - tests/BrightnessCommandTest.php | 34 - tests/ChecksumCommandTest.php | 27 - tests/CircleCommandTest.php | 43 - tests/CircleShapeTest.php | 52 - tests/CollectionTest.php | 102 + tests/ColorizeCommandTest.php | 37 - tests/ConstraintTest.php | 58 - tests/ContrastCommandTest.php | 34 - tests/CropCommandTest.php | 36 - tests/DestroyCommandTest.php | 46 - tests/DriverTest.php | 61 - tests/Drivers/Gd/ColorTest.php | 75 + .../Gd/Decoders/ArrayColorDecoderTest.php | 21 + .../Gd/Decoders/BinaryImageDecoderTest.php | 40 + tests/Drivers/Gd/Encoders/GifEncoderTest.php | 40 + tests/Drivers/Gd/Encoders/JpegEncoderTest.php | 29 + tests/Drivers/Gd/FrameTest.php | 79 + tests/Drivers/Gd/ImageFactoryTest.php | 17 + tests/Drivers/Gd/ImageTest.php | 46 + tests/Drivers/Gd/InputHandlerTest.php | 62 + tests/Drivers/Imagick/ColorTest.php | 87 + .../Decoders/ArrayColorDecoderTest.php | 21 + .../Imagick/Encoders/GifEncoderTest.php | 42 + .../Imagick/Encoders/JpegEncoderTest.php | 33 + tests/Drivers/Imagick/FrameTest.php | 87 + tests/Drivers/Imagick/ImageTest.php | 51 + tests/Drivers/Imagick/InputHandlerTest.php | 62 + tests/EllipseCommandTest.php | 43 - tests/EllipseShapeTest.php | 55 - tests/EncoderTest.php | 339 ---- tests/ExifCommandTest.php | 112 -- tests/FileTest.php | 28 - tests/FillCommandTest.php | 93 - tests/FitCommandTest.php | 93 - tests/FlipCommandTest.php | 45 - tests/GammaCommandTest.php | 34 - tests/GdColorTest.php | 296 --- tests/GdSystemTest.php | 1703 ----------------- tests/Geometry/PointTest.php | 82 + tests/Geometry/SizeTest.php | 101 + tests/GetsizeCommandTest.php | 39 - tests/GreyscaleCommandTest.php | 34 - tests/HeightenCommandTest.php | 49 - tests/ImageManagerStaticTest.php | 36 - tests/ImageManagerTest.php | 48 +- tests/ImageTest.php | 143 -- tests/ImagickColorTest.php | 338 ---- tests/ImagickSystemTest.php | 1650 ---------------- tests/InsertCommandTest.php | 67 - tests/InterlaceCommandTest.php | 34 - tests/InvertCommandTest.php | 34 - tests/IptcCommandTest.php | 72 - tests/LimitColorsCommandTest.php | 43 - tests/LineCommandTest.php | 43 - tests/LineShapeTest.php | 50 - tests/MaskCommandTest.php | 68 - tests/OpacityCommandTest.php | 44 - tests/OrientateCommandTest.php | 94 - tests/PickColorCommandTest.php | 67 - tests/PixelCommandTest.php | 34 - tests/PixelateCommandTest.php | 37 - tests/PointTest.php | 47 - tests/PolygonCommandTest.php | 45 - tests/PolygonShapeTest.php | 57 - tests/PsrResponseCommandTest.php | 58 - tests/RectangleCommandTest.php | 43 - tests/RectangleShapeTest.php | 54 - tests/ResetCommandTest.php | 71 - tests/ResizeCanvasCommandTest.php | 80 - tests/ResizeCommandTest.php | 49 - tests/ResponseTest.php | 30 - tests/RotateCommandTest.php | 48 - tests/SharpenCommandTest.php | 34 - tests/SizeTest.php | 436 ----- tests/StreamCommandTest.php | 36 - tests/TestCase.php | 17 + tests/TextCommandTest.php | 32 - tests/TrimCommandTest.php | 54 - tests/WidenCommandTest.php | 49 - tests/images/animation.gif | Bin 0 -> 592 bytes tests/images/black-friday.png | Bin 4964 -> 0 bytes tests/images/blue.gif | Bin 0 -> 47 bytes tests/images/broken.png | Bin 42 -> 0 bytes tests/images/cats.gif | Bin 0 -> 1721 bytes tests/images/circle.png | Bin 383 -> 0 bytes tests/images/exif.jpg | Bin 7791 -> 0 bytes tests/images/gradient.png | Bin 531 -> 0 bytes tests/images/green.gif | Bin 0 -> 47 bytes tests/images/iptc.jpg | Bin 10526 -> 0 bytes tests/images/red.gif | Bin 0 -> 47 bytes tests/images/star.png | Bin 463 -> 0 bytes tests/images/test.jpg | Bin 9183 -> 0 bytes tests/images/test.webp | Bin 82 -> 0 bytes tests/images/trim.png | Bin 258 -> 0 bytes tests/tmp/.gitkeep | 0 289 files changed, 3009 insertions(+), 17482 deletions(-) delete mode 100644 .travis.yml delete mode 100644 LICENSE delete mode 100755 README.md delete mode 100644 provides.json create mode 100644 src/Collection.php create mode 100644 src/Drivers/Abstract/AbstractColor.php create mode 100644 src/Drivers/Abstract/AbstractFrame.php create mode 100644 src/Drivers/Abstract/AbstractImage.php create mode 100644 src/Drivers/Abstract/AbstractInputHandler.php create mode 100644 src/Drivers/Abstract/Decoders/AbstractDecoder.php create mode 100644 src/Drivers/Abstract/Encoders/AbstractEncoder.php create mode 100644 src/Drivers/Gd/Color.php create mode 100644 src/Drivers/Gd/Decoders/ArrayColorDecoder.php create mode 100644 src/Drivers/Gd/Decoders/Base64ImageDecoder.php create mode 100644 src/Drivers/Gd/Decoders/BinaryImageDecoder.php create mode 100644 src/Drivers/Gd/Decoders/DataUriImageDecoder.php create mode 100644 src/Drivers/Gd/Decoders/FilePathImageDecoder.php create mode 100644 src/Drivers/Gd/Encoders/GifEncoder.php create mode 100644 src/Drivers/Gd/Encoders/JpegEncoder.php create mode 100644 src/Drivers/Gd/Frame.php create mode 100644 src/Drivers/Gd/Image.php create mode 100644 src/Drivers/Gd/ImageFactory.php create mode 100644 src/Drivers/Gd/InputHandler.php create mode 100644 src/Drivers/Gd/Modifiers/GreyscaleModifier.php create mode 100644 src/Drivers/Imagick/Color.php create mode 100644 src/Drivers/Imagick/Decoders/ArrayColorDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/Base64ImageDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/BinaryImageDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/DataUriImageDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/FilePathImageDecoder.php create mode 100644 src/Drivers/Imagick/Encoders/GifEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/JpegEncoder.php create mode 100644 src/Drivers/Imagick/Frame.php create mode 100644 src/Drivers/Imagick/Image.php create mode 100644 src/Drivers/Imagick/InputHandler.php create mode 100644 src/Drivers/Imagick/Modifiers/GreyscaleModifier.php create mode 100644 src/Exceptions/DecoderException.php create mode 100644 src/Exceptions/NotWritableException.php create mode 100644 src/Exceptions/RuntimeException.php create mode 100644 src/Geometry/Point.php create mode 100644 src/Geometry/Size.php create mode 100644 src/ImageManager.php create mode 100644 src/Interfaces/CollectionInterface.php create mode 100644 src/Interfaces/ColorInterface.php create mode 100644 src/Interfaces/DecoderInterface.php create mode 100644 src/Interfaces/EncoderInterface.php create mode 100644 src/Interfaces/FactoryInterface.php create mode 100644 src/Interfaces/FrameInterface.php create mode 100644 src/Interfaces/ImageInterface.php create mode 100644 src/Interfaces/ModifierInterface.php create mode 100644 src/Interfaces/PointInterface.php create mode 100644 src/Interfaces/SizeInterface.php delete mode 100644 src/Intervention/Image/AbstractColor.php delete mode 100644 src/Intervention/Image/AbstractDecoder.php delete mode 100644 src/Intervention/Image/AbstractDriver.php delete mode 100644 src/Intervention/Image/AbstractEncoder.php delete mode 100644 src/Intervention/Image/AbstractFont.php delete mode 100644 src/Intervention/Image/AbstractShape.php delete mode 100644 src/Intervention/Image/Commands/AbstractCommand.php delete mode 100644 src/Intervention/Image/Commands/Argument.php delete mode 100644 src/Intervention/Image/Commands/ChecksumCommand.php delete mode 100644 src/Intervention/Image/Commands/CircleCommand.php delete mode 100644 src/Intervention/Image/Commands/EllipseCommand.php delete mode 100644 src/Intervention/Image/Commands/ExifCommand.php delete mode 100644 src/Intervention/Image/Commands/IptcCommand.php delete mode 100644 src/Intervention/Image/Commands/LineCommand.php delete mode 100644 src/Intervention/Image/Commands/OrientateCommand.php delete mode 100644 src/Intervention/Image/Commands/PolygonCommand.php delete mode 100644 src/Intervention/Image/Commands/PsrResponseCommand.php delete mode 100644 src/Intervention/Image/Commands/RectangleCommand.php delete mode 100644 src/Intervention/Image/Commands/ResponseCommand.php delete mode 100644 src/Intervention/Image/Commands/StreamCommand.php delete mode 100644 src/Intervention/Image/Commands/TextCommand.php delete mode 100644 src/Intervention/Image/Constraint.php delete mode 100644 src/Intervention/Image/Exception/ImageException.php delete mode 100644 src/Intervention/Image/Exception/InvalidArgumentException.php delete mode 100644 src/Intervention/Image/Exception/MissingDependencyException.php delete mode 100644 src/Intervention/Image/Exception/NotFoundException.php delete mode 100644 src/Intervention/Image/Exception/NotReadableException.php delete mode 100644 src/Intervention/Image/Exception/NotSupportedException.php delete mode 100644 src/Intervention/Image/Exception/NotWritableException.php delete mode 100644 src/Intervention/Image/Exception/RuntimeException.php delete mode 100644 src/Intervention/Image/Facades/Image.php delete mode 100644 src/Intervention/Image/File.php delete mode 100644 src/Intervention/Image/Filters/DemoFilter.php delete mode 100644 src/Intervention/Image/Filters/FilterInterface.php delete mode 100644 src/Intervention/Image/Gd/Color.php delete mode 100644 src/Intervention/Image/Gd/Commands/BackupCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/BlurCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/BrightnessCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ColorizeCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ContrastCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/CropCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/DestroyCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/FillCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/FitCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/FlipCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/GammaCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/GetSizeCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/GreyscaleCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/HeightenCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/InsertCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/InterlaceCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/InvertCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/LimitColorsCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/MaskCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/OpacityCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/PickColorCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/PixelCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/PixelateCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ResetCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/ResizeCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/RotateCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/SharpenCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/TrimCommand.php delete mode 100644 src/Intervention/Image/Gd/Commands/WidenCommand.php delete mode 100644 src/Intervention/Image/Gd/Decoder.php delete mode 100644 src/Intervention/Image/Gd/Driver.php delete mode 100644 src/Intervention/Image/Gd/Encoder.php delete mode 100644 src/Intervention/Image/Gd/Font.php delete mode 100644 src/Intervention/Image/Gd/Shapes/CircleShape.php delete mode 100644 src/Intervention/Image/Gd/Shapes/EllipseShape.php delete mode 100644 src/Intervention/Image/Gd/Shapes/LineShape.php delete mode 100644 src/Intervention/Image/Gd/Shapes/PolygonShape.php delete mode 100644 src/Intervention/Image/Gd/Shapes/RectangleShape.php delete mode 100644 src/Intervention/Image/Image.php delete mode 100644 src/Intervention/Image/ImageManager.php delete mode 100644 src/Intervention/Image/ImageManagerStatic.php delete mode 100644 src/Intervention/Image/ImageServiceProvider.php delete mode 100755 src/Intervention/Image/ImageServiceProviderLaravel4.php delete mode 100644 src/Intervention/Image/ImageServiceProviderLaravelRecent.php delete mode 100644 src/Intervention/Image/ImageServiceProviderLeague.php delete mode 100644 src/Intervention/Image/ImageServiceProviderLumen.php delete mode 100644 src/Intervention/Image/Imagick/Color.php delete mode 100644 src/Intervention/Image/Imagick/Commands/BackupCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/BlurCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/BrightnessCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ColorizeCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ContrastCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/CropCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/DestroyCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ExifCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/FillCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/FitCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/FlipCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/GammaCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/GetSizeCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/HeightenCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/InsertCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/InterlaceCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/InvertCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/MaskCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/OpacityCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/PickColorCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/PixelCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/PixelateCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ResetCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/ResizeCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/RotateCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/SharpenCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/TrimCommand.php delete mode 100644 src/Intervention/Image/Imagick/Commands/WidenCommand.php delete mode 100644 src/Intervention/Image/Imagick/Decoder.php delete mode 100644 src/Intervention/Image/Imagick/Driver.php delete mode 100644 src/Intervention/Image/Imagick/Encoder.php delete mode 100644 src/Intervention/Image/Imagick/Font.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/CircleShape.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/EllipseShape.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/LineShape.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/PolygonShape.php delete mode 100644 src/Intervention/Image/Imagick/Shapes/RectangleShape.php delete mode 100644 src/Intervention/Image/Point.php delete mode 100644 src/Intervention/Image/Response.php delete mode 100644 src/Intervention/Image/Size.php create mode 100644 src/Traits/CanDecodeDataUri.php create mode 100644 src/Traits/CanResolveDriverClass.php create mode 100644 src/Traits/CanValidateBase64.php create mode 100644 src/Traits/CanValidateColorArray.php delete mode 100644 src/config/config.php delete mode 100644 tests/AbstractColorTest.php delete mode 100644 tests/AbstractCommandTest.php delete mode 100644 tests/AbstractDecoderTest.php delete mode 100644 tests/AbstractDriverTest.php delete mode 100644 tests/AbstractFontTest.php delete mode 100644 tests/AbstractShapeTest.php delete mode 100644 tests/ArgumentTest.php delete mode 100644 tests/BackupCommandTest.php delete mode 100644 tests/BlurCommandTest.php delete mode 100644 tests/BrightnessCommandTest.php delete mode 100644 tests/ChecksumCommandTest.php delete mode 100644 tests/CircleCommandTest.php delete mode 100644 tests/CircleShapeTest.php create mode 100644 tests/CollectionTest.php delete mode 100644 tests/ColorizeCommandTest.php delete mode 100644 tests/ConstraintTest.php delete mode 100644 tests/ContrastCommandTest.php delete mode 100644 tests/CropCommandTest.php delete mode 100644 tests/DestroyCommandTest.php delete mode 100644 tests/DriverTest.php create mode 100644 tests/Drivers/Gd/ColorTest.php create mode 100644 tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php create mode 100644 tests/Drivers/Gd/Encoders/GifEncoderTest.php create mode 100644 tests/Drivers/Gd/Encoders/JpegEncoderTest.php create mode 100644 tests/Drivers/Gd/FrameTest.php create mode 100644 tests/Drivers/Gd/ImageFactoryTest.php create mode 100644 tests/Drivers/Gd/ImageTest.php create mode 100644 tests/Drivers/Gd/InputHandlerTest.php create mode 100644 tests/Drivers/Imagick/ColorTest.php create mode 100644 tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/GifEncoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/JpegEncoderTest.php create mode 100644 tests/Drivers/Imagick/FrameTest.php create mode 100644 tests/Drivers/Imagick/ImageTest.php create mode 100644 tests/Drivers/Imagick/InputHandlerTest.php delete mode 100644 tests/EllipseCommandTest.php delete mode 100644 tests/EllipseShapeTest.php delete mode 100644 tests/EncoderTest.php delete mode 100644 tests/ExifCommandTest.php delete mode 100644 tests/FileTest.php delete mode 100644 tests/FillCommandTest.php delete mode 100644 tests/FitCommandTest.php delete mode 100644 tests/FlipCommandTest.php delete mode 100644 tests/GammaCommandTest.php delete mode 100644 tests/GdColorTest.php delete mode 100644 tests/GdSystemTest.php create mode 100644 tests/Geometry/PointTest.php create mode 100644 tests/Geometry/SizeTest.php delete mode 100644 tests/GetsizeCommandTest.php delete mode 100644 tests/GreyscaleCommandTest.php delete mode 100644 tests/HeightenCommandTest.php delete mode 100644 tests/ImageManagerStaticTest.php delete mode 100644 tests/ImageTest.php delete mode 100644 tests/ImagickColorTest.php delete mode 100644 tests/ImagickSystemTest.php delete mode 100644 tests/InsertCommandTest.php delete mode 100644 tests/InterlaceCommandTest.php delete mode 100644 tests/InvertCommandTest.php delete mode 100644 tests/IptcCommandTest.php delete mode 100644 tests/LimitColorsCommandTest.php delete mode 100644 tests/LineCommandTest.php delete mode 100644 tests/LineShapeTest.php delete mode 100644 tests/MaskCommandTest.php delete mode 100644 tests/OpacityCommandTest.php delete mode 100644 tests/OrientateCommandTest.php delete mode 100644 tests/PickColorCommandTest.php delete mode 100644 tests/PixelCommandTest.php delete mode 100644 tests/PixelateCommandTest.php delete mode 100644 tests/PointTest.php delete mode 100644 tests/PolygonCommandTest.php delete mode 100644 tests/PolygonShapeTest.php delete mode 100644 tests/PsrResponseCommandTest.php delete mode 100644 tests/RectangleCommandTest.php delete mode 100644 tests/RectangleShapeTest.php delete mode 100644 tests/ResetCommandTest.php delete mode 100644 tests/ResizeCanvasCommandTest.php delete mode 100644 tests/ResizeCommandTest.php delete mode 100644 tests/ResponseTest.php delete mode 100644 tests/RotateCommandTest.php delete mode 100644 tests/SharpenCommandTest.php delete mode 100644 tests/SizeTest.php delete mode 100644 tests/StreamCommandTest.php create mode 100644 tests/TestCase.php delete mode 100644 tests/TextCommandTest.php delete mode 100644 tests/TrimCommandTest.php delete mode 100644 tests/WidenCommandTest.php create mode 100644 tests/images/animation.gif delete mode 100644 tests/images/black-friday.png create mode 100644 tests/images/blue.gif delete mode 100644 tests/images/broken.png create mode 100644 tests/images/cats.gif delete mode 100644 tests/images/circle.png delete mode 100644 tests/images/exif.jpg delete mode 100644 tests/images/gradient.png create mode 100644 tests/images/green.gif delete mode 100644 tests/images/iptc.jpg create mode 100644 tests/images/red.gif delete mode 100644 tests/images/star.png delete mode 100644 tests/images/test.jpg delete mode 100755 tests/images/test.webp delete mode 100644 tests/images/trim.png delete mode 100644 tests/tmp/.gitkeep diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 86ddce950..000000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: php - -sudo: false - -jobs: - include: - - dist: trusty - php: 5.4 - - dist: trusty - php: 5.5 - - dist: xenial - php: 5.6 - - dist: xenial - php: 7.0 - - dist: xenial - php: 7.1 - - dist: xenial - php: 7.2 - - dist: xenial - php: 7.3 - - dist: xenial - php: 7.4 - - dist: xenial - php: nightly - - dist: xenial - php: hhvm - allow_failures: - - php: nightly - - php: hhvm - -before_script: - - printf "\n" | pecl install imagick - - composer self-update || true - - composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader - -script: vendor/bin/phpunit diff --git a/LICENSE b/LICENSE deleted file mode 100644 index bc444ba22..000000000 --- a/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Oliver Vogel - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100755 index 350f471cf..000000000 --- a/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Intervention Image - -Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. The package includes ServiceProviders and Facades for easy **Laravel** integration. - -[![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) -[![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) -[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) - -## Requirements - -- PHP >=5.4 -- Fileinfo Extension - -## Supported Image Libraries - -- GD Library (>=2.0) -- Imagick PHP extension (>=6.5.7) - -## Getting started - -- [Installation](http://image.intervention.io/getting_started/installation) -- [Laravel Framework Integration](http://image.intervention.io/getting_started/installation#laravel) -- [Basic Usage](http://image.intervention.io/use/basics) - -## Code Examples - -```php -// open an image file -$img = Image::make('public/foo.jpg'); - -// resize image instance -$img->resize(320, 240); - -// insert a watermark -$img->insert('public/watermark.png'); - -// save image in desired format -$img->save('public/bar.jpg'); -``` - -Refer to the [official documentation](http://image.intervention.io/) to learn more about Intervention Image. - -## Contributing - -Contributions to the Intervention Image library are welcome. Please note the following guidelines before submitting your pull request. - -- Follow [PSR-2](http://www.php-fig.org/psr/psr-2/) coding standards. -- Write tests for new functions and added features -- API calls should work consistently with both GD and Imagick drivers - -## License - -Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). - -Copyright 2017 [Oliver Vogel](http://olivervogel.com/) diff --git a/composer.json b/composer.json index e750b406b..93776f08d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "intervention/image", - "description": "Image handling and manipulation library with support for Laravel integration", + "description": "PHP image manipulation", "homepage": "http://image.intervention.io/", "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail"], "license": "MIT", @@ -8,40 +8,25 @@ { "name": "Oliver Vogel", "email": "oliver@olivervogel.com", - "homepage": "http://olivervogel.com/" + "homepage": "http://intervention.io/" } ], "require": { - "php": ">=5.4.0", - "ext-fileinfo": "*", - "guzzlehttp/psr7": "~1.1 || ^2.0" + "php": "^8", + "intervention/gif": "dev-master", + "intervention/mimesniffer": "^0.4.2" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15", - "mockery/mockery": "~0.9.2" - }, - "suggest": { - "ext-gd": "to use GD library based image processing.", - "ext-imagick": "to use Imagick based image processing.", - "intervention/imagecache": "Caching extension for the Intervention Image library" + "phpunit/phpunit": "^9" }, "autoload": { "psr-4": { - "Intervention\\Image\\": "src/Intervention/Image" + "Intervention\\Image\\": "src" } }, - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - }, - "laravel": { - "providers": [ - "Intervention\\Image\\ImageServiceProvider" - ], - "aliases": { - "Image": "Intervention\\Image\\Facades\\Image" - } + "autoload-dev": { + "psr-4": { + "Intervention\\Image\\Tests\\": "tests" } - }, - "minimum-stability": "stable" + } } diff --git a/phpunit.xml b/phpunit.xml index 3347b75b7..422eeac61 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -8,7 +8,6 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" > diff --git a/provides.json b/provides.json deleted file mode 100644 index a8cd1b6a5..000000000 --- a/provides.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "providers": [ - "Intervention\\Image\\ImageServiceProvider" - ], - "aliases": [ - { - "alias": "Image", - "facade": "Intervention\\Image\\Facades\\Image" - } - ] -} diff --git a/src/Collection.php b/src/Collection.php new file mode 100644 index 000000000..2c40af196 --- /dev/null +++ b/src/Collection.php @@ -0,0 +1,137 @@ +items); + } + + public function toArray(): array + { + return $this->items; + } + + /** + * Count items in collection + * + * @return integer + */ + public function count(): int + { + return count($this->items); + } + + /** + * Append new item to collection + * + * @param mixed $item + * @return CollectionInterface + */ + public function push($item): CollectionInterface + { + $this->items[] = $item; + + return $this; + } + + /** + * Return first item in collection + * + * @return mixed + */ + public function first() + { + if ($item = reset($this->items)) { + return $item; + } + + return null; + } + + /** + * Returns last item in collection + * + * @return mixed + */ + public function last() + { + if ($item = end($this->items)) { + return $item; + } + + return null; + } + + /** + * Return item with given key + * + * @param integer $key + * @return mixed + */ + public function get(int $key = 0) + { + if (! array_key_exists($key, $this->items)) { + return null; + } + + return $this->items[$key]; + } + + public function map(callable $callback): self + { + $items = array_map(function ($item) use ($callback) { + return $callback($item); + }, $this->items); + + return new self($items); + } + + public function pushEach(array $data, ?callable $callback = null): CollectionInterface + { + if (! is_iterable($data)) { + throw new RuntimeException('Unable to iterate given data.'); + } + + foreach ($data as $item) { + $this->push(is_callable($callback) ? $callback($item) : $item); + } + + return $this; + } +} diff --git a/src/Drivers/Abstract/AbstractColor.php b/src/Drivers/Abstract/AbstractColor.php new file mode 100644 index 000000000..449e4747b --- /dev/null +++ b/src/Drivers/Abstract/AbstractColor.php @@ -0,0 +1,17 @@ +red(), + $this->green(), + $this->blue() + ); + } +} diff --git a/src/Drivers/Abstract/AbstractFrame.php b/src/Drivers/Abstract/AbstractFrame.php new file mode 100644 index 000000000..c83c8af33 --- /dev/null +++ b/src/Drivers/Abstract/AbstractFrame.php @@ -0,0 +1,8 @@ +frames; + } + + public function getFrames(): Collection + { + return $this->frames; + } + + public function addFrame(FrameInterface $frame): ImageInterface + { + $this->frames->push($frame); + + return $this; + } + + public function size(): SizeInterface + { + return new Size($this->width(), $this->height()); + } + + public function isAnimated(): bool + { + return $this->getFrames()->count() > 1; + } + + public function modify(ModifierInterface $modifier): ImageInterface + { + return $modifier->apply($this); + } + + public function encode(EncoderInterface $encoder, ?string $path = null): string + { + $encoded = $encoder->encode($this); + + if ($path) { + $saved = @file_put_contents($path, $encoded); + if ($saved === false) { + throw new NotWritableException( + "Can't write image data to path ({$path})." + ); + } + } + + return $encoded; + } + + public function toJpeg(?int $quality = null, ?string $path = null): string + { + return $this->encode( + $this->resolveDriverClass('Encoders\JpegEncoder', $quality), + $path + ); + } + + public function toGif(?string $path = null): string + { + return $this->encode( + $this->resolveDriverClass('Encoders\GifEncoder'), + $path + ); + } +} diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php new file mode 100644 index 000000000..fce61f859 --- /dev/null +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -0,0 +1,17 @@ +chain()->handle($input); + } +} diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php new file mode 100644 index 000000000..937400415 --- /dev/null +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -0,0 +1,47 @@ +decode($input); + } catch (DecoderException $e) { + if ($this->hasSuccessor()) { + return $this->successor->handle($input); + } + + $this->fail(); + } + + return $decoded; + } + + protected function hasSuccessor(): bool + { + return $this->successor !== null; + } + + protected function fail(): void + { + throw new DecoderException("Unable to decode given input."); + } + + protected function inputType($input): AbstractType + { + return MimeSniffer::createFromString($input)->getType(); + } +} diff --git a/src/Drivers/Abstract/Encoders/AbstractEncoder.php b/src/Drivers/Abstract/Encoders/AbstractEncoder.php new file mode 100644 index 000000000..31a53ac51 --- /dev/null +++ b/src/Drivers/Abstract/Encoders/AbstractEncoder.php @@ -0,0 +1,41 @@ +quality = $quality; + + return $this; + } + + public function getQuality(): int + { + return $this->quality; + } +} diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php new file mode 100644 index 000000000..351072bd2 --- /dev/null +++ b/src/Drivers/Gd/Color.php @@ -0,0 +1,57 @@ +value = $value; + } + + public function red(): int + { + return $this->toArray()[0]; + } + + public function green(): int + { + return $this->toArray()[1]; + } + + public function blue(): int + { + return $this->toArray()[2]; + } + + public function alpha(): float + { + return $this->toArray()[3]; + } + + public function toArray(): array + { + $a = ($this->value >> 24) & 0xFF; + $r = ($this->value >> 16) & 0xFF; + $g = ($this->value >> 8) & 0xFF; + $b = $this->value & 0xFF; + $a = (float) round(1 - $a / 127, 2); + + return [$r, $g, $b, $a]; + } +} diff --git a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php new file mode 100644 index 000000000..7a1b71618 --- /dev/null +++ b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php @@ -0,0 +1,33 @@ +isValidColorArray($input)) { + $this->fail(); + } + + list($r, $g, $b, $a) = $input; + + return new Color( + ($this->opacityToGdAlpha($a) << 24) + ($r << 16) + ($g << 8) + $b + ); + } + + protected function opacityToGdAlpha(float $opacity): int + { + return intval(round($opacity * 127 * -1 + 127)); + } +} diff --git a/src/Drivers/Gd/Decoders/Base64ImageDecoder.php b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php new file mode 100644 index 000000000..fb22e3bad --- /dev/null +++ b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php @@ -0,0 +1,22 @@ +isValidBase64($input)) { + $this->fail(); + } + + return parent::decode(base64_decode($input)); + } +} diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php new file mode 100644 index 000000000..a5e69c0b4 --- /dev/null +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -0,0 +1,55 @@ +inputType($input)->isBinary()) { + $this->fail(); + } + + if (is_a($this->inputType($input), ImageGif::class)) { + return $this->decodeGif($input); // decode (animated) gif + } + + $resource = @imagecreatefromstring($input); + + if ($resource === false) { + $this->fail(); + } + + return new Image(new Collection([new Frame($resource)])); + } + + protected function decodeGif($input): ImageInterface + { + $image = new Image(new Collection()); + $gif = GifDecoder::decode($input); + + if (!$gif->isAnimated()) { + return $image->addFrame(new Frame(@imagecreatefromstring($input))); + } + + $splitter = GifSplitter::create($gif)->split(); + $delays = $splitter->getDelays(); + foreach ($splitter->coalesceToResources() as $key => $gd) { + $image->addFrame((new Frame($gd))->setDelay($delays[$key])); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Decoders/DataUriImageDecoder.php b/src/Drivers/Gd/Decoders/DataUriImageDecoder.php new file mode 100644 index 000000000..b1c8f245f --- /dev/null +++ b/src/Drivers/Gd/Decoders/DataUriImageDecoder.php @@ -0,0 +1,33 @@ +fail(); + } + + $uri = $this->decodeDataUri($input); + + if (! $uri->isValid()) { + $this->fail(); + } + + if ($uri->isBase64Encoded()) { + return parent::decode(base64_decode($uri->data())); + } + + return parent::decode(urldecode($uri->data())); + } +} diff --git a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php new file mode 100644 index 000000000..6448a645a --- /dev/null +++ b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php @@ -0,0 +1,28 @@ +fail(); + } + + try { + if (! @is_file($input)) { + $this->fail(); + } + } catch (Exception $e) { + $this->fail(); + } + + return parent::decode(file_get_contents($input)); + } +} diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php new file mode 100644 index 000000000..d4a99a021 --- /dev/null +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -0,0 +1,33 @@ +isAnimated()) { + return $this->encodeAnimated($image); + } + + return $this->getBuffered(function () use ($image) { + imagegif($image->getFrames()->first()->getCore()); + }); + } + + protected function encodeAnimated($image): string + { + $builder = GifBuilder::canvas($image->width(), $image->height(), 2); + foreach ($image as $key => $frame) { + $source = $this->encode($frame->toImage()); + $builder->addFrame($source, $frame->getDelay()); + } + + return $builder->encode(); + } +} diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php new file mode 100644 index 000000000..408c7b957 --- /dev/null +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -0,0 +1,17 @@ +getBuffered(function () use ($image) { + imagejpeg($image->getFrames()->first()->getCore(), null, $this->quality); + }); + } +} diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php new file mode 100644 index 000000000..e5c9e0d15 --- /dev/null +++ b/src/Drivers/Gd/Frame.php @@ -0,0 +1,111 @@ +core; + } + + public function getDelay(): int + { + return $this->delay; + } + + public function setDelay(int $delay): FrameInterface + { + $this->delay = $delay; + + return $this; + } + + public function getDispose(): int + { + return $this->dispose; + } + + public function setDispose(int $dispose): FrameInterface + { + $this->dispose = $dispose; + + return $this; + } + + public function setOffset(int $left, int $top): FrameInterface + { + $this->offset_left = $left; + $this->offset_top = $top; + + return $this; + } + + public function getOffsetLeft(): int + { + return $this->offset_left; + } + + public function setOffsetLeft(int $offset): FrameInterface + { + $this->offset_left = $offset; + + return $this; + } + + public function getOffsetTop(): int + { + return $this->offset_top; + } + + public function setOffsetTop(int $offset): FrameInterface + { + $this->offset_top = $offset; + + return $this; + } + + public function toImage(): ImageInterface + { + return new Image(new Collection([$this])); + } +} diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php new file mode 100644 index 000000000..40742d244 --- /dev/null +++ b/src/Drivers/Gd/Image.php @@ -0,0 +1,36 @@ +frames->first()->getCore()); + } + + public function height(): int + { + return imagesy($this->frames->first()->getCore()); + } + + public function greyscale(): ImageInterface + { + return $this->modify(new Modifiers\GreyscaleModifier()); + } +} diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php new file mode 100644 index 000000000..0127f36c6 --- /dev/null +++ b/src/Drivers/Gd/ImageFactory.php @@ -0,0 +1,19 @@ +getCore(), IMG_FILTER_GRAYSCALE); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php new file mode 100644 index 000000000..f07a82ca2 --- /dev/null +++ b/src/Drivers/Imagick/Color.php @@ -0,0 +1,53 @@ +pixel = $pixel; + } + + public function red(): int + { + return round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255); + } + + public function green(): int + { + return round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255); + } + + public function blue(): int + { + return round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255); + } + + public function alpha(): float + { + return round($this->pixel->getColorValue(Imagick::COLOR_ALPHA), 2); + } + + public function toArray(): array + { + return [ + $this->red(), + $this->green(), + $this->blue(), + $this->alpha() + ]; + } +} diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php new file mode 100644 index 000000000..e21107445 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php @@ -0,0 +1,31 @@ +isValidColorArray($input)) { + $this->fail(); + } + + list($r, $g, $b, $a) = $input; + + $pixel = new ImagickPixel( + sprintf('rgba(%d, %d, %d, %.2F)', $r, $g, $b, $a) + ); + + return new Color($pixel); + } +} diff --git a/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php new file mode 100644 index 000000000..46d344303 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php @@ -0,0 +1,22 @@ +isValidBase64($input)) { + $this->fail(); + } + + return parent::decode(base64_decode($input)); + } +} diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php new file mode 100644 index 000000000..96cc9a982 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -0,0 +1,34 @@ +inputType($input)->isBinary()) { + $this->fail(); + } + + $imagick = new Imagick(); + $imagick->readImageBlob($input); + $imagick = $imagick->coalesceImages(); + + $image = new Image(new Collection()); + + foreach ($imagick as $frame_content) { + $image->addFrame(new Frame($frame_content)); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php b/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php new file mode 100644 index 000000000..9f80b117f --- /dev/null +++ b/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php @@ -0,0 +1,33 @@ +fail(); + } + + $uri = $this->decodeDataUri($input); + + if (! $uri->isValid()) { + $this->fail(); + } + + if ($uri->isBase64Encoded()) { + return parent::decode(base64_decode($uri->data())); + } + + return parent::decode(urldecode($uri->data())); + } +} diff --git a/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php new file mode 100644 index 000000000..a4fe1d306 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php @@ -0,0 +1,28 @@ +fail(); + } + + try { + if (! @is_file($input)) { + $this->fail(); + } + } catch (Exception $e) { + $this->fail(); + } + + return parent::decode(file_get_contents($input)); + } +} diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php new file mode 100644 index 000000000..4b62dae2e --- /dev/null +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -0,0 +1,29 @@ +getFrames() as $frame) { + $gif->addImage($frame->getCore()); + } + + $gif->setFormat($format); + $gif->setImageFormat($format); + $gif->setCompression($compression); + $gif->setImageCompression($compression); + + return $gif->getImagesBlob(); + } +} diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php new file mode 100644 index 000000000..179531d7d --- /dev/null +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -0,0 +1,29 @@ +getFrames()->first()->getCore(); + $imagick->setImageBackgroundColor('white'); + $imagick->setBackgroundColor('white'); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick->setCompressionQuality($this->quality); + $imagick->setImageCompressionQuality($this->quality); + + return $imagick->getImagesBlob(); + } +} diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php new file mode 100644 index 000000000..c5c5652a9 --- /dev/null +++ b/src/Drivers/Imagick/Frame.php @@ -0,0 +1,83 @@ +core; + } + + public function getDelay(): int + { + return $this->core->getImageDelay(); + } + + public function setDelay(int $delay): FrameInterface + { + $this->core->setImageDelay($delay); + + return $this; + } + + public function getDispose(): int + { + return $this->core->getImageDispose(); + } + + public function setDispose(int $dispose): FrameInterface + { + $this->core->setImageDispose($dispose); + + return $this; + } + + public function setOffset(int $left, int $top): FrameInterface + { + $this->core->setImagePage( + $this->core->getImageWidth(), + $this->core->getImageHeight(), + $left, + $top + ); + + return $this; + } + + public function getOffsetLeft(): int + { + return $this->core->getImagePage()['x']; + } + + public function setOffsetLeft(int $offset): FrameInterface + { + return $this->setOffset($offset, $this->getOffsetTop()); + } + + public function getOffsetTop(): int + { + return $this->core->getImagePage()['y']; + } + + public function setOffsetTop(int $offset): FrameInterface + { + return $this->setOffset($this->getOffsetLeft(), $offset); + } + + public function toImage(): ImageInterface + { + return new Image(new Collection([$this])); + } +} diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php new file mode 100644 index 000000000..be0a3afca --- /dev/null +++ b/src/Drivers/Imagick/Image.php @@ -0,0 +1,35 @@ +frames->first()->getCore()->getImageWidth(); + } + + public function height(): int + { + return $this->frames->first()->getCore()->getImageHeight(); + } + + public function greyscale(): ImageInterface + { + return $this->modify(new Modifiers\GreyscaleModifier()); + } +} diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php new file mode 100644 index 000000000..d1fcbde2a --- /dev/null +++ b/src/Drivers/Imagick/InputHandler.php @@ -0,0 +1,22 @@ +image as $frame) { + $frame->getCore()->modulateImage(100, 0, 100); + } + + return $this->image; + } +} diff --git a/src/Exceptions/DecoderException.php b/src/Exceptions/DecoderException.php new file mode 100644 index 000000000..9271e2027 --- /dev/null +++ b/src/Exceptions/DecoderException.php @@ -0,0 +1,8 @@ +x = $x; + $this->y = $y; + } + + /** + * Sets X coordinate + * + * @param integer $x + */ + public function setX(int $x): self + { + $this->x = $x; + + return $this; + } + + /** + * Get X coordinate + * + * @return integer + */ + public function getX(): int + { + return $this->x; + } + + /** + * Sets Y coordinate + * + * @param integer $y + */ + public function setY(int $y): self + { + $this->y = $y; + + return $this; + } + + /** + * Get Y coordinate + * + * @return integer + */ + public function getY(): int + { + return $this->y; + } + + /** + * Move X coordinate + * + * @param integer $x + */ + public function moveX(int $value): self + { + $this->x += $value; + + return $this; + } + + /** + * Move Y coordinate + * + * @param integer $y + */ + public function moveY(int $value): self + { + $this->y += $value; + + return $this; + } + + /** + * Sets both X and Y coordinate + * + * @param integer $x + * @param integer $y + * @return Point + */ + public function setPosition(int $x, int $y): self + { + $this->setX($x); + $this->setY($y); + + return $this; + } + + /** + * Rotate point ccw around pivot + * + * @param float $angle + * @param Point $pivot + * @return Point + */ + public function rotate(float $angle, Point $pivot): self + { + $sin = round(sin(deg2rad($angle)), 6); + $cos = round(cos(deg2rad($angle)), 6); + + return $this->setPosition( + $cos * ($this->x - $pivot->x) - $sin * ($this->y - $pivot->y) + $pivot->x, + $sin * ($this->x - $pivot->x) + $cos * ($this->y - $pivot->y) + $pivot->y + ); + } +} diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php new file mode 100644 index 000000000..4f19bc30e --- /dev/null +++ b/src/Geometry/Size.php @@ -0,0 +1,92 @@ +width = $width; + $this->height = $height; + $this->pivot = $pivot ? $pivot : new Point(); + } + + public function getWidth(): int + { + return $this->width; + } + + public function getHeight(): int + { + return $this->height; + } + + /** + * Get current pivot point + * + * @return Point + */ + public function getPivot(): PointInterface + { + return $this->pivot; + } + + public function setWidth(int $width): SizeInterface + { + $this->width = $width; + + return $this; + } + + public function setHeight(int $height): SizeInterface + { + $this->height = $height; + + return $this; + } + + public function getAspectRatio(): float + { + return $this->width / $this->height; + } + + public function fitsInto(SizeInterface $size): bool + { + if ($this->getWidth() > $size->getWidth()) { + return false; + } + + if ($this->getHeight() > $size->getHeight()) { + return false; + } + + return true; + } + + /** + * Determine if size is landscape format + * + * @return boolean + */ + public function isLandscape(): bool + { + return $this->getWidth() > $this->getHeight(); + } + + /** + * Determine if size is portrait format + * + * @return boolean + */ + public function isPortrait(): bool + { + return $this->getWidth() < $this->getHeight(); + } +} diff --git a/src/ImageManager.php b/src/ImageManager.php new file mode 100644 index 000000000..428df54b4 --- /dev/null +++ b/src/ImageManager.php @@ -0,0 +1,96 @@ + 'gd', + ]; + + /** + * Create new instance + * + * @param array $config + */ + public function __construct(array $config = []) + { + $this->configure($config); + } + + /** + * Override configuration settings + * + * @param array $config + */ + public function configure(array $config = []): self + { + $this->config = array_replace($this->config, $config); + + return $this; + } + + /** + * Return given value of configuration + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function getConfig($key, $default = null) + { + return array_key_exists($key, $this->config) ? $this->config[$key] : $default; + } + + /** + * Create new image instance from scratch + * + * @param int $width + * @param int $height + * @return ImageInterface + */ + public function create(int $width, int $height): ImageInterface + { + return $this->resolve('ImageFactory')->newImage($width, $height); + } + + /** + * Create new image instance from input + * + * @param mixed $input + * @return ImageInterface + */ + public function make($input): ImageInterface + { + return $this->resolve('InputHandler')->handle($input); + } + + /** + * Resolve given classname according to current configuration + * + * @param string $classname + * @param array $arguments + * @return mixed + */ + private function resolve(string $classname, ...$arguments) + { + $classname = sprintf( + "Intervention\\Image\\Drivers\\%s\\%s", + ucfirst($this->config['driver']), + $classname + ); + + $reflection = new ReflectionClass($classname); + + return $reflection->newInstanceArgs($arguments); + } +} diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php new file mode 100644 index 000000000..5c63e5a7d --- /dev/null +++ b/src/Interfaces/CollectionInterface.php @@ -0,0 +1,12 @@ +parse($value); - } - - /** - * Parses given value as color - * - * @param mixed $value - * @return \Intervention\Image\AbstractColor - */ - public function parse($value) - { - switch (true) { - - case is_string($value): - $this->initFromString($value); - break; - - case is_int($value): - $this->initFromInteger($value); - break; - - case is_array($value): - $this->initFromArray($value); - break; - - case is_object($value): - $this->initFromObject($value); - break; - - case is_null($value): - $this->initFromArray([255, 255, 255, 0]); - break; - - default: - throw new NotReadableException( - "Color format ({$value}) cannot be read." - ); - } - - return $this; - } - - /** - * Formats current color instance into given format - * - * @param string $type - * @return mixed - */ - public function format($type) - { - switch (strtolower($type)) { - - case 'rgba': - return $this->getRgba(); - - case 'hex': - return $this->getHex('#'); - - case 'int': - case 'integer': - return $this->getInt(); - - case 'array': - return $this->getArray(); - - case 'obj': - case 'object': - return $this; - - default: - throw new NotSupportedException( - "Color format ({$type}) is not supported." - ); - } - } - - /** - * Reads RGBA values from string into array - * - * @param string $value - * @return array - */ - protected function rgbaFromString($value) - { - $result = false; - - // parse color string in hexidecimal format like #cccccc or cccccc or ccc - $hexPattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; - - // parse color string in format rgb(140, 140, 140) - $rgbPattern = '/^rgb ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3})\)$/i'; - - // parse color string in format rgba(255, 0, 0, 0.5) - $rgbaPattern = '/^rgba ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9.]{1,4})\)$/i'; - - if (preg_match($hexPattern, $value, $matches)) { - $result = []; - $result[0] = strlen($matches[1]) == '1' ? hexdec($matches[1].$matches[1]) : hexdec($matches[1]); - $result[1] = strlen($matches[2]) == '1' ? hexdec($matches[2].$matches[2]) : hexdec($matches[2]); - $result[2] = strlen($matches[3]) == '1' ? hexdec($matches[3].$matches[3]) : hexdec($matches[3]); - $result[3] = 1; - } elseif (preg_match($rgbPattern, $value, $matches)) { - $result = []; - $result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0; - $result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0; - $result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0; - $result[3] = 1; - } elseif (preg_match($rgbaPattern, $value, $matches)) { - $result = []; - $result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0; - $result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0; - $result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0; - $result[3] = ($matches[4] >= 0 && $matches[4] <= 1) ? $matches[4] : 0; - } else { - throw new NotReadableException( - "Unable to read color ({$value})." - ); - } - - return $result; - } -} diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php deleted file mode 100644 index 743cc5c36..000000000 --- a/src/Intervention/Image/AbstractDecoder.php +++ /dev/null @@ -1,364 +0,0 @@ -data = $data; - } - - /** - * Init from given URL - * - * @param string $url - * @return \Intervention\Image\Image - */ - public function initFromUrl($url) - { - - $options = [ - 'http' => [ - 'method'=>"GET", - 'protocol_version'=>1.1, // force use HTTP 1.1 for service mesh environment with envoy - 'header'=>"Accept-language: en\r\n". - "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2\r\n" - ] - ]; - - $context = stream_context_create($options); - - - if ($data = @file_get_contents($url, false, $context)) { - return $this->initFromBinary($data); - } - - throw new NotReadableException( - "Unable to init from given url (".$url.")." - ); - } - - /** - * Init from given stream - * - * @param StreamInterface|resource $stream - * @return \Intervention\Image\Image - */ - public function initFromStream($stream) - { - if (!$stream instanceof StreamInterface) { - $stream = new Stream($stream); - } - - try { - $offset = $stream->tell(); - } catch (\RuntimeException $e) { - $offset = 0; - } - - $shouldAndCanSeek = $offset !== 0 && $stream->isSeekable(); - - if ($shouldAndCanSeek) { - $stream->rewind(); - } - - try { - $data = $stream->getContents(); - } catch (\RuntimeException $e) { - $data = null; - } - - if ($shouldAndCanSeek) { - $stream->seek($offset); - } - - if ($data) { - return $this->initFromBinary($data); - } - - throw new NotReadableException( - "Unable to init from given stream" - ); - } - - /** - * Determines if current source data is GD resource - * - * @return boolean - */ - public function isGdResource() - { - if (is_resource($this->data)) { - return (get_resource_type($this->data) == 'gd'); - } - - if ($this->data instanceof \GdImage) { - return true; - } - - return false; - } - - /** - * Determines if current source data is Imagick object - * - * @return boolean - */ - public function isImagick() - { - return is_a($this->data, 'Imagick'); - } - - /** - * Determines if current source data is Intervention\Image\Image object - * - * @return boolean - */ - public function isInterventionImage() - { - return is_a($this->data, '\Intervention\Image\Image'); - } - - /** - * Determines if current data is SplFileInfo object - * - * @return boolean - */ - public function isSplFileInfo() - { - return is_a($this->data, 'SplFileInfo'); - } - - /** - * Determines if current data is Symfony UploadedFile component - * - * @return boolean - */ - public function isSymfonyUpload() - { - return is_a($this->data, 'Symfony\Component\HttpFoundation\File\UploadedFile'); - } - - /** - * Determines if current source data is file path - * - * @return boolean - */ - public function isFilePath() - { - if (is_string($this->data)) { - try { - return is_file($this->data); - } catch (\Exception $e) { - return false; - } - } - - return false; - } - - /** - * Determines if current source data is url - * - * @return boolean - */ - public function isUrl() - { - return (bool) filter_var($this->data, FILTER_VALIDATE_URL); - } - - /** - * Determines if current source data is a stream resource - * - * @return boolean - */ - public function isStream() - { - if ($this->data instanceof StreamInterface) return true; - if (!is_resource($this->data)) return false; - if (get_resource_type($this->data) !== 'stream') return false; - - return true; - } - - /** - * Determines if current source data is binary data - * - * @return boolean - */ - public function isBinary() - { - if (is_string($this->data)) { - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $this->data); - return (substr($mime, 0, 4) != 'text' && $mime != 'application/x-empty'); - } - - return false; - } - - /** - * Determines if current source data is data-url - * - * @return boolean - */ - public function isDataUrl() - { - $data = $this->decodeDataUrl($this->data); - - return is_null($data) ? false : true; - } - - /** - * Determines if current source data is base64 encoded - * - * @return boolean - */ - public function isBase64() - { - if (!is_string($this->data)) { - return false; - } - - return base64_encode(base64_decode($this->data)) === str_replace(["\n", "\r"], '', $this->data); - } - - /** - * Initiates new Image from Intervention\Image\Image - * - * @param Image $object - * @return \Intervention\Image\Image - */ - public function initFromInterventionImage($object) - { - return $object; - } - - /** - * Parses and decodes binary image data from data-url - * - * @param string $data_url - * @return string - */ - private function decodeDataUrl($data_url) - { - if (!is_string($data_url)) { - return null; - } - - $pattern = "/^data:(?:image\/[a-zA-Z\-\.]+)(?:charset=\".+\")?;base64,(?P.+)$/"; - preg_match($pattern, str_replace(["\n", "\r"], '', $data_url), $matches); - - if (is_array($matches) && array_key_exists('data', $matches)) { - return base64_decode($matches['data']); - } - - return null; - } - - /** - * Initiates new image from mixed data - * - * @param mixed $data - * @return \Intervention\Image\Image - */ - public function init($data) - { - $this->data = $data; - - switch (true) { - - case $this->isGdResource(): - return $this->initFromGdResource($this->data); - - case $this->isImagick(): - return $this->initFromImagick($this->data); - - case $this->isInterventionImage(): - return $this->initFromInterventionImage($this->data); - - case $this->isSplFileInfo(): - return $this->initFromPath($this->data->getRealPath()); - - case $this->isBinary(): - return $this->initFromBinary($this->data); - - case $this->isUrl(): - return $this->initFromUrl($this->data); - - case $this->isStream(): - return $this->initFromStream($this->data); - - case $this->isDataUrl(): - return $this->initFromBinary($this->decodeDataUrl($this->data)); - - case $this->isFilePath(): - return $this->initFromPath($this->data); - - // isBase64 has to be after isFilePath to prevent false positives - case $this->isBase64(): - return $this->initFromBinary(base64_decode($this->data)); - - default: - throw new NotReadableException("Image source not readable"); - } - } - - /** - * Decoder object transforms to string source data - * - * @return string - */ - public function __toString() - { - return (string) $this->data; - } -} diff --git a/src/Intervention/Image/AbstractDriver.php b/src/Intervention/Image/AbstractDriver.php deleted file mode 100644 index 6c591a794..000000000 --- a/src/Intervention/Image/AbstractDriver.php +++ /dev/null @@ -1,140 +0,0 @@ -decoder->init($data); - } - - /** - * Encodes given image - * - * @param Image $image - * @param string $format - * @param int $quality - * @return \Intervention\Image\Image - */ - public function encode($image, $format, $quality) - { - return $this->encoder->process($image, $format, $quality); - } - - /** - * Executes named command on given image - * - * @param Image $image - * @param string $name - * @param array $arguments - * @return \Intervention\Image\Commands\AbstractCommand - */ - public function executeCommand($image, $name, $arguments) - { - $commandName = $this->getCommandClassName($name); - $command = new $commandName($arguments); - $command->execute($image); - - return $command; - } - - /** - * Returns classname of given command name - * - * @param string $name - * @return string - */ - private function getCommandClassName($name) - { - if (extension_loaded('mbstring')) { - $name = mb_strtoupper(mb_substr($name, 0, 1)) . mb_substr($name, 1); - } else { - $name = strtoupper(substr($name, 0, 1)) . substr($name, 1); - } - - $drivername = $this->getDriverName(); - $classnameLocal = sprintf('\Intervention\Image\%s\Commands\%sCommand', $drivername, ucfirst($name)); - $classnameGlobal = sprintf('\Intervention\Image\Commands\%sCommand', ucfirst($name)); - - if (class_exists($classnameLocal)) { - return $classnameLocal; - } elseif (class_exists($classnameGlobal)) { - return $classnameGlobal; - } - - throw new NotSupportedException( - "Command ({$name}) is not available for driver ({$drivername})." - ); - } - - /** - * Returns name of current driver instance - * - * @return string - */ - public function getDriverName() - { - $reflect = new \ReflectionClass($this); - $namespace = $reflect->getNamespaceName(); - - return substr(strrchr($namespace, "\\"), 1); - } -} diff --git a/src/Intervention/Image/AbstractEncoder.php b/src/Intervention/Image/AbstractEncoder.php deleted file mode 100644 index c25f8bffd..000000000 --- a/src/Intervention/Image/AbstractEncoder.php +++ /dev/null @@ -1,271 +0,0 @@ -setImage($image); - $this->setFormat($format); - $this->setQuality($quality); - - switch (strtolower($this->format)) { - - case 'data-url': - $this->result = $this->processDataUrl(); - break; - - case 'gif': - case 'image/gif': - $this->result = $this->processGif(); - break; - - case 'png': - case 'image/png': - case 'image/x-png': - $this->result = $this->processPng(); - break; - - case 'jpg': - case 'jpeg': - case 'jfif': - case 'image/jp2': - case 'image/jpg': - case 'image/jpeg': - case 'image/pjpeg': - case 'image/jfif': - $this->result = $this->processJpeg(); - break; - - case 'tif': - case 'tiff': - case 'image/tiff': - case 'image/tif': - case 'image/x-tif': - case 'image/x-tiff': - $this->result = $this->processTiff(); - break; - - case 'bmp': - case 'ms-bmp': - case 'x-bitmap': - case 'x-bmp': - case 'x-ms-bmp': - case 'x-win-bitmap': - case 'x-windows-bmp': - case 'x-xbitmap': - case 'image/ms-bmp': - case 'image/x-bitmap': - case 'image/x-bmp': - case 'image/x-ms-bmp': - case 'image/x-win-bitmap': - case 'image/x-windows-bmp': - case 'image/x-xbitmap': - $this->result = $this->processBmp(); - break; - - case 'ico': - case 'image/x-ico': - case 'image/x-icon': - case 'image/vnd.microsoft.icon': - $this->result = $this->processIco(); - break; - - case 'psd': - case 'image/vnd.adobe.photoshop': - $this->result = $this->processPsd(); - break; - - case 'webp': - case 'image/webp': - case 'image/x-webp': - $this->result = $this->processWebp(); - break; - - case 'avif': - case 'image/avif': - $this->result = $this->processAvif(); - break; - - case 'heic': - case 'image/heic': - case 'image/heif': - $this->result = $this->processHeic(); - break; - - default: - throw new NotSupportedException( - "Encoding format ({$this->format}) is not supported." - ); - } - - $this->setImage(null); - - return $image->setEncoded($this->result); - } - - /** - * Processes and returns encoded image as data-url string - * - * @return string - */ - protected function processDataUrl() - { - $mime = $this->image->mime ? $this->image->mime : 'image/png'; - - return sprintf('data:%s;base64,%s', - $mime, - base64_encode($this->process($this->image, $mime, $this->quality)) - ); - } - - /** - * Sets image to process - * - * @param Image $image - */ - protected function setImage($image) - { - $this->image = $image; - } - - /** - * Determines output format - * - * @param string $format - */ - protected function setFormat($format = null) - { - if ($format == '' && $this->image instanceof Image) { - $format = $this->image->mime; - } - - $this->format = $format ? $format : 'jpg'; - - return $this; - } - - /** - * Determines output quality - * - * @param int $quality - */ - protected function setQuality($quality) - { - $quality = is_null($quality) ? 90 : $quality; - $quality = $quality === 0 ? 1 : $quality; - - if ($quality < 0 || $quality > 100) { - throw new InvalidArgumentException( - 'Quality must range from 0 to 100.' - ); - } - - $this->quality = intval($quality); - - return $this; - } -} diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php deleted file mode 100644 index 35c1825a6..000000000 --- a/src/Intervention/Image/AbstractFont.php +++ /dev/null @@ -1,295 +0,0 @@ -text = $text; - } - - /** - * Set text to be written - * - * @param String $text - * @return self - */ - public function text($text) - { - $this->text = $text; - - return $this; - } - - /** - * Get text to be written - * - * @return String - */ - public function getText() - { - return $this->text; - } - - /** - * Set font size in pixels - * - * @param int $size - * @return self - */ - public function size($size) - { - $this->size = $size; - - return $this; - } - - /** - * Get font size in pixels - * - * @return int - */ - public function getSize() - { - return $this->size; - } - - /** - * Set color of text to be written - * - * @param mixed $color - * @return self - */ - public function color($color) - { - $this->color = $color; - - return $this; - } - - /** - * Get color of text - * - * @return mixed - */ - public function getColor() - { - return $this->color; - } - - /** - * Set rotation angle of text - * - * @param int $angle - * @return self - */ - public function angle($angle) - { - $this->angle = $angle; - - return $this; - } - - /** - * Get rotation angle of text - * - * @return int - */ - public function getAngle() - { - return $this->angle; - } - - /** - * Set horizontal text alignment - * - * @param string $align - * @return self - */ - public function align($align) - { - $this->align = $align; - - return $this; - } - - /** - * Get horizontal text alignment - * - * @return string - */ - public function getAlign() - { - return $this->align; - } - - /** - * Set vertical text alignment - * - * @param string $valign - * @return self - */ - public function valign($valign) - { - $this->valign = $valign; - - return $this; - } - - /** - * Get vertical text alignment - * - * @return string - */ - public function getValign() - { - return $this->valign; - } - - /** - * Set text kerning - * - * @param string $kerning - * @return void - */ - public function kerning($kerning) - { - $this->kerning = $kerning; - } - - /** - * Get kerning - * - * @return float - */ - public function getKerning() - { - return $this->kerning; - } - - /** - * Set path to font file - * - * @param string $file - * @return self - */ - public function file($file) - { - $this->file = $file; - - return $this; - } - - /** - * Get path to font file - * - * @return string - */ - public function getFile() - { - return $this->file; - } - - /** - * Checks if current font has access to an applicable font file - * - * @return boolean - */ - protected function hasApplicableFontFile() - { - if (is_string($this->file)) { - return file_exists($this->file); - } - - return false; - } - - /** - * Counts lines of text to be written - * - * @return int - */ - public function countLines() - { - return count(explode(PHP_EOL, $this->text)); - } -} diff --git a/src/Intervention/Image/AbstractShape.php b/src/Intervention/Image/AbstractShape.php deleted file mode 100644 index cd4a9f1c6..000000000 --- a/src/Intervention/Image/AbstractShape.php +++ /dev/null @@ -1,71 +0,0 @@ -background = $color; - } - - /** - * Set border width and color of current shape - * - * @param int $width - * @param string $color - * @return void - */ - public function border($width, $color = null) - { - $this->border_width = is_numeric($width) ? intval($width) : 0; - $this->border_color = is_null($color) ? '#000000' : $color; - } - - /** - * Determines if current shape has border - * - * @return boolean - */ - public function hasBorder() - { - return ($this->border_width >= 1); - } -} diff --git a/src/Intervention/Image/Commands/AbstractCommand.php b/src/Intervention/Image/Commands/AbstractCommand.php deleted file mode 100644 index e31078ceb..000000000 --- a/src/Intervention/Image/Commands/AbstractCommand.php +++ /dev/null @@ -1,81 +0,0 @@ -arguments = $arguments; - } - - /** - * Creates new argument instance from given argument key - * - * @param int $key - * @return \Intervention\Image\Commands\Argument - */ - public function argument($key) - { - return new Argument($this, $key); - } - - /** - * Returns output data of current command - * - * @return mixed - */ - public function getOutput() - { - return $this->output ? $this->output : null; - } - - /** - * Determines if current instance has output data - * - * @return boolean - */ - public function hasOutput() - { - return ! is_null($this->output); - } - - /** - * Sets output data of current command - * - * @param mixed $value - */ - public function setOutput($value) - { - $this->output = $value; - } -} diff --git a/src/Intervention/Image/Commands/Argument.php b/src/Intervention/Image/Commands/Argument.php deleted file mode 100644 index 9538199f6..000000000 --- a/src/Intervention/Image/Commands/Argument.php +++ /dev/null @@ -1,225 +0,0 @@ -command = $command; - $this->key = $key; - } - - /** - * Returns name of current arguments command - * - * @return string - */ - public function getCommandName() - { - preg_match("/\\\\([\w]+)Command$/", get_class($this->command), $matches); - return isset($matches[1]) ? lcfirst($matches[1]).'()' : 'Method'; - } - - /** - * Returns value of current argument - * - * @param mixed $default - * @return mixed - */ - public function value($default = null) - { - $arguments = $this->command->arguments; - - if (is_array($arguments)) { - return isset($arguments[$this->key]) ? $arguments[$this->key] : $default; - } - - return $default; - } - - /** - * Defines current argument as required - * - * @return \Intervention\Image\Commands\Argument - */ - public function required() - { - if ( ! array_key_exists($this->key, $this->command->arguments)) { - throw new InvalidArgumentException( - sprintf("Missing argument %d for %s", $this->key + 1, $this->getCommandName()) - ); - } - - return $this; - } - - /** - * Determines that current argument must be of given type - * - * @return \Intervention\Image\Commands\Argument - */ - public function type($type) - { - $valid = true; - $value = $this->value(); - - if ($value === null) { - return $this; - } - - switch (strtolower($type)) { - case 'bool': - case 'boolean': - $valid = \is_bool($value); - $message = '%s accepts only boolean values as argument %d.'; - break; - case 'int': - case 'integer': - $valid = \is_int($value); - $message = '%s accepts only integer values as argument %d.'; - break; - case 'num': - case 'numeric': - $valid = is_numeric($value); - $message = '%s accepts only numeric values as argument %d.'; - break; - case 'str': - case 'string': - $valid = \is_string($value); - $message = '%s accepts only string values as argument %d.'; - break; - case 'array': - $valid = \is_array($value); - $message = '%s accepts only array as argument %d.'; - break; - case 'closure': - $valid = is_a($value, '\Closure'); - $message = '%s accepts only Closure as argument %d.'; - break; - case 'digit': - $valid = $this->isDigit($value); - $message = '%s accepts only integer values as argument %d.'; - break; - } - - if (! $valid) { - $commandName = $this->getCommandName(); - $argument = $this->key + 1; - - if (isset($message)) { - $message = sprintf($message, $commandName, $argument); - } else { - $message = sprintf('Missing argument for %d.', $argument); - } - - throw new InvalidArgumentException( - $message - ); - } - - return $this; - } - - /** - * Determines that current argument value must be numeric between given values - * - * @return \Intervention\Image\Commands\Argument - */ - public function between($x, $y) - { - $value = $this->type('numeric')->value(); - - if (is_null($value)) { - return $this; - } - - $alpha = min($x, $y); - $omega = max($x, $y); - - if ($value < $alpha || $value > $omega) { - throw new InvalidArgumentException( - sprintf('Argument %d must be between %s and %s.', $this->key, $x, $y) - ); - } - - return $this; - } - - /** - * Determines that current argument must be over a minimum value - * - * @return \Intervention\Image\Commands\Argument - */ - public function min($value) - { - $v = $this->type('numeric')->value(); - - if (is_null($v)) { - return $this; - } - - if ($v < $value) { - throw new InvalidArgumentException( - sprintf('Argument %d must be at least %s.', $this->key, $value) - ); - } - - return $this; - } - - /** - * Determines that current argument must be under a maxiumum value - * - * @return \Intervention\Image\Commands\Argument - */ - public function max($value) - { - $v = $this->type('numeric')->value(); - - if (is_null($v)) { - return $this; - } - - if ($v > $value) { - throw new InvalidArgumentException( - sprintf('Argument %d may not be greater than %s.', $this->key, $value) - ); - } - - return $this; - } - - /** - * Checks if value is "PHP" integer (120 but also 120.0) - * - * @param mixed $value - * @return boolean - */ - private function isDigit($value) - { - return is_numeric($value) ? intval($value) == $value : false; - } -} diff --git a/src/Intervention/Image/Commands/ChecksumCommand.php b/src/Intervention/Image/Commands/ChecksumCommand.php deleted file mode 100644 index 9acc40308..000000000 --- a/src/Intervention/Image/Commands/ChecksumCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -getSize(); - - for ($x=0; $x <= ($size->width-1); $x++) { - for ($y=0; $y <= ($size->height-1); $y++) { - $colors[] = $image->pickColor($x, $y, 'array'); - } - } - - $this->setOutput(md5(serialize($colors))); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/CircleCommand.php b/src/Intervention/Image/Commands/CircleCommand.php deleted file mode 100644 index c627818e3..000000000 --- a/src/Intervention/Image/Commands/CircleCommand.php +++ /dev/null @@ -1,35 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $x = $this->argument(1)->type('numeric')->required()->value(); - $y = $this->argument(2)->type('numeric')->required()->value(); - $callback = $this->argument(3)->type('closure')->value(); - - $circle_classname = sprintf('\Intervention\Image\%s\Shapes\CircleShape', - $image->getDriver()->getDriverName()); - - $circle = new $circle_classname($diameter); - - if ($callback instanceof Closure) { - $callback($circle); - } - - $circle->applyToImage($image, $x, $y); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/EllipseCommand.php b/src/Intervention/Image/Commands/EllipseCommand.php deleted file mode 100644 index 4637e0205..000000000 --- a/src/Intervention/Image/Commands/EllipseCommand.php +++ /dev/null @@ -1,36 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $height = $this->argument(1)->type('numeric')->required()->value(); - $x = $this->argument(2)->type('numeric')->required()->value(); - $y = $this->argument(3)->type('numeric')->required()->value(); - $callback = $this->argument(4)->type('closure')->value(); - - $ellipse_classname = sprintf('\Intervention\Image\%s\Shapes\EllipseShape', - $image->getDriver()->getDriverName()); - - $ellipse = new $ellipse_classname($width, $height); - - if ($callback instanceof Closure) { - $callback($ellipse); - } - - $ellipse->applyToImage($image, $x, $y); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/ExifCommand.php b/src/Intervention/Image/Commands/ExifCommand.php deleted file mode 100644 index 063df6ef6..000000000 --- a/src/Intervention/Image/Commands/ExifCommand.php +++ /dev/null @@ -1,61 +0,0 @@ -argument(0)->value(); - - // try to read exif data from image file - try { - if ($image->dirname && $image->basename) { - $stream = $image->dirname . '/' . $image->basename; - } elseif (version_compare(PHP_VERSION, '7.2.0', '>=')) { - // https://www.php.net/manual/en/function.exif-read-data.php#refsect1-function.exif-read-data-changelog - $stream = $image->stream()->detach(); - } else { - // https://bugs.php.net/bug.php?id=65187 - $stream = $image->encode('data-url')->encoded; - } - - $data = @exif_read_data($stream); - - if (!is_null($key) && is_array($data)) { - $data = array_key_exists($key, $data) ? $data[$key] : false; - } - - } catch (\Exception $e) { - throw new NotReadableException( - sprintf( - "Cannot read the Exif data from the filename (%s) provided ", - $image->dirname . '/' . $image->basename - ), - $e->getCode(), - $e - ); - } - - $this->setOutput($data); - return true; - } -} diff --git a/src/Intervention/Image/Commands/IptcCommand.php b/src/Intervention/Image/Commands/IptcCommand.php deleted file mode 100644 index b78a2a8e4..000000000 --- a/src/Intervention/Image/Commands/IptcCommand.php +++ /dev/null @@ -1,68 +0,0 @@ -argument(0)->value(); - - $info = []; - @getimagesize($image->dirname .'/'. $image->basename, $info); - - $data = []; - - if (array_key_exists('APP13', $info)) { - $iptc = iptcparse($info['APP13']); - - if (is_array($iptc)) { - $data['DocumentTitle'] = isset($iptc["2#005"][0]) ? $iptc["2#005"][0] : null; - $data['Urgency'] = isset($iptc["2#010"][0]) ? $iptc["2#010"][0] : null; - $data['Category'] = isset($iptc["2#015"][0]) ? $iptc["2#015"][0] : null; - $data['Subcategories'] = isset($iptc["2#020"][0]) ? $iptc["2#020"][0] : null; - $data['Keywords'] = isset($iptc["2#025"][0]) ? $iptc["2#025"] : null; - $data['ReleaseDate'] = isset($iptc["2#030"][0]) ? $iptc["2#030"][0] : null; - $data['ReleaseTime'] = isset($iptc["2#035"][0]) ? $iptc["2#035"][0] : null; - $data['SpecialInstructions'] = isset($iptc["2#040"][0]) ? $iptc["2#040"][0] : null; - $data['CreationDate'] = isset($iptc["2#055"][0]) ? $iptc["2#055"][0] : null; - $data['CreationTime'] = isset($iptc["2#060"][0]) ? $iptc["2#060"][0] : null; - $data['AuthorByline'] = isset($iptc["2#080"][0]) ? $iptc["2#080"][0] : null; - $data['AuthorTitle'] = isset($iptc["2#085"][0]) ? $iptc["2#085"][0] : null; - $data['City'] = isset($iptc["2#090"][0]) ? $iptc["2#090"][0] : null; - $data['SubLocation'] = isset($iptc["2#092"][0]) ? $iptc["2#092"][0] : null; - $data['State'] = isset($iptc["2#095"][0]) ? $iptc["2#095"][0] : null; - $data['Country'] = isset($iptc["2#101"][0]) ? $iptc["2#101"][0] : null; - $data['OTR'] = isset($iptc["2#103"][0]) ? $iptc["2#103"][0] : null; - $data['Headline'] = isset($iptc["2#105"][0]) ? $iptc["2#105"][0] : null; - $data['Source'] = isset($iptc["2#110"][0]) ? $iptc["2#110"][0] : null; - $data['PhotoSource'] = isset($iptc["2#115"][0]) ? $iptc["2#115"][0] : null; - $data['Copyright'] = isset($iptc["2#116"][0]) ? $iptc["2#116"][0] : null; - $data['Caption'] = isset($iptc["2#120"][0]) ? $iptc["2#120"][0] : null; - $data['CaptionWriter'] = isset($iptc["2#122"][0]) ? $iptc["2#122"][0] : null; - } - } - - if (! is_null($key) && is_array($data)) { - $data = array_key_exists($key, $data) ? $data[$key] : false; - } - - $this->setOutput($data); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/LineCommand.php b/src/Intervention/Image/Commands/LineCommand.php deleted file mode 100644 index a068c662a..000000000 --- a/src/Intervention/Image/Commands/LineCommand.php +++ /dev/null @@ -1,36 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $y1 = $this->argument(1)->type('numeric')->required()->value(); - $x2 = $this->argument(2)->type('numeric')->required()->value(); - $y2 = $this->argument(3)->type('numeric')->required()->value(); - $callback = $this->argument(4)->type('closure')->value(); - - $line_classname = sprintf('\Intervention\Image\%s\Shapes\LineShape', - $image->getDriver()->getDriverName()); - - $line = new $line_classname($x2, $y2); - - if ($callback instanceof Closure) { - $callback($line); - } - - $line->applyToImage($image, $x1, $y1); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/OrientateCommand.php b/src/Intervention/Image/Commands/OrientateCommand.php deleted file mode 100644 index 552482cb4..000000000 --- a/src/Intervention/Image/Commands/OrientateCommand.php +++ /dev/null @@ -1,48 +0,0 @@ -exif('Orientation')) { - - case 2: - $image->flip(); - break; - - case 3: - $image->rotate(180); - break; - - case 4: - $image->rotate(180)->flip(); - break; - - case 5: - $image->rotate(270)->flip(); - break; - - case 6: - $image->rotate(270); - break; - - case 7: - $image->rotate(90)->flip(); - break; - - case 8: - $image->rotate(90); - break; - } - - return true; - } -} diff --git a/src/Intervention/Image/Commands/PolygonCommand.php b/src/Intervention/Image/Commands/PolygonCommand.php deleted file mode 100644 index a2fa99788..000000000 --- a/src/Intervention/Image/Commands/PolygonCommand.php +++ /dev/null @@ -1,49 +0,0 @@ -argument(0)->type('array')->required()->value(); - $callback = $this->argument(1)->type('closure')->value(); - - $vertices_count = count($points); - - // check if number if coordinates is even - if ($vertices_count % 2 !== 0) { - throw new InvalidArgumentException( - "The number of given polygon vertices must be even." - ); - } - - if ($vertices_count < 6) { - throw new InvalidArgumentException( - "You must have at least 3 points in your array." - ); - } - - $polygon_classname = sprintf('\Intervention\Image\%s\Shapes\PolygonShape', - $image->getDriver()->getDriverName()); - - $polygon = new $polygon_classname($points); - - if ($callback instanceof Closure) { - $callback($polygon); - } - - $polygon->applyToImage($image); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/PsrResponseCommand.php b/src/Intervention/Image/Commands/PsrResponseCommand.php deleted file mode 100644 index d75cd903b..000000000 --- a/src/Intervention/Image/Commands/PsrResponseCommand.php +++ /dev/null @@ -1,45 +0,0 @@ -argument(0)->value(); - $quality = $this->argument(1)->between(0, 100)->value(); - - //Encoded property will be populated at this moment - $stream = $image->stream($format, $quality); - - $mimetype = finfo_buffer( - finfo_open(FILEINFO_MIME_TYPE), - $image->getEncoded() - ); - - $this->setOutput(new Response( - 200, - [ - 'Content-Type' => $mimetype, - 'Content-Length' => strlen($image->getEncoded()) - ], - $stream - )); - - return true; - } -} \ No newline at end of file diff --git a/src/Intervention/Image/Commands/RectangleCommand.php b/src/Intervention/Image/Commands/RectangleCommand.php deleted file mode 100644 index 24378b386..000000000 --- a/src/Intervention/Image/Commands/RectangleCommand.php +++ /dev/null @@ -1,36 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $y1 = $this->argument(1)->type('numeric')->required()->value(); - $x2 = $this->argument(2)->type('numeric')->required()->value(); - $y2 = $this->argument(3)->type('numeric')->required()->value(); - $callback = $this->argument(4)->type('closure')->value(); - - $rectangle_classname = sprintf('\Intervention\Image\%s\Shapes\RectangleShape', - $image->getDriver()->getDriverName()); - - $rectangle = new $rectangle_classname($x1, $y1, $x2, $y2); - - if ($callback instanceof Closure) { - $callback($rectangle); - } - - $rectangle->applyToImage($image, $x1, $y1); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/ResponseCommand.php b/src/Intervention/Image/Commands/ResponseCommand.php deleted file mode 100644 index 7903b5af4..000000000 --- a/src/Intervention/Image/Commands/ResponseCommand.php +++ /dev/null @@ -1,26 +0,0 @@ -argument(0)->value(); - $quality = $this->argument(1)->between(0, 100)->value(); - - $response = new Response($image, $format, $quality); - - $this->setOutput($response->make()); - - return true; - } -} diff --git a/src/Intervention/Image/Commands/StreamCommand.php b/src/Intervention/Image/Commands/StreamCommand.php deleted file mode 100644 index bc2ff9efb..000000000 --- a/src/Intervention/Image/Commands/StreamCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -argument(0)->value(); - $quality = $this->argument(1)->between(0, 100)->value(); - $data = $image->encode($format, $quality)->getEncoded(); - - $this->setOutput($this->getStream($data)); - - return true; - } - - /** - * Create stream from given data - * - * @param string $data - * @return \Psr\Http\Message\StreamInterface - */ - protected function getStream($data) - { - if (class_exists(\GuzzleHttp\Psr7\Utils::class)) { - return \GuzzleHttp\Psr7\Utils::streamFor($data); // guzzlehttp/psr7 >= 2.0 - } - - return \GuzzleHttp\Psr7\stream_for($data); // guzzlehttp/psr7 < 2.0 - } -} diff --git a/src/Intervention/Image/Commands/TextCommand.php b/src/Intervention/Image/Commands/TextCommand.php deleted file mode 100644 index 3c60b4e77..000000000 --- a/src/Intervention/Image/Commands/TextCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -argument(0)->required()->value(); - $x = $this->argument(1)->type('numeric')->value(0); - $y = $this->argument(2)->type('numeric')->value(0); - $callback = $this->argument(3)->type('closure')->value(); - - $fontclassname = sprintf('\Intervention\Image\%s\Font', - $image->getDriver()->getDriverName()); - - $font = new $fontclassname($text); - - if ($callback instanceof Closure) { - $callback($font); - } - - $font->applyToImage($image, $x, $y); - - return true; - } -} diff --git a/src/Intervention/Image/Constraint.php b/src/Intervention/Image/Constraint.php deleted file mode 100644 index 44bdd67a6..000000000 --- a/src/Intervention/Image/Constraint.php +++ /dev/null @@ -1,92 +0,0 @@ -size = $size; - } - - /** - * Returns current size of constraint - * - * @return \Intervention\Image\Size - */ - public function getSize() - { - return $this->size; - } - - /** - * Fix the given argument in current constraint - * - * @param int $type - * @return void - */ - public function fix($type) - { - $this->fixed = ($this->fixed & ~(1 << $type)) | (1 << $type); - } - - /** - * Checks if given argument is fixed in current constraint - * - * @param int $type - * @return boolean - */ - public function isFixed($type) - { - return (bool) ($this->fixed & (1 << $type)); - } - - /** - * Fixes aspect ratio in current constraint - * - * @return void - */ - public function aspectRatio() - { - $this->fix(self::ASPECTRATIO); - } - - /** - * Fixes possibility to size up in current constraint - * - * @return void - */ - public function upsize() - { - $this->fix(self::UPSIZE); - } -} diff --git a/src/Intervention/Image/Exception/ImageException.php b/src/Intervention/Image/Exception/ImageException.php deleted file mode 100644 index 83e6b91f2..000000000 --- a/src/Intervention/Image/Exception/ImageException.php +++ /dev/null @@ -1,8 +0,0 @@ -dirname = array_key_exists('dirname', $info) ? $info['dirname'] : null; - $this->basename = array_key_exists('basename', $info) ? $info['basename'] : null; - $this->extension = array_key_exists('extension', $info) ? $info['extension'] : null; - $this->filename = array_key_exists('filename', $info) ? $info['filename'] : null; - - if (file_exists($path) && is_file($path)) { - $this->mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path); - } - - return $this; - } - - /** - * Get file size - * - * @return mixed - */ - public function filesize() - { - $path = $this->basePath(); - - if (file_exists($path) && is_file($path)) { - return filesize($path); - } - - return false; - } - - /** - * Get fully qualified path - * - * @return string - */ - public function basePath() - { - if ($this->dirname && $this->basename) { - return ($this->dirname .'/'. $this->basename); - } - - return null; - } - -} diff --git a/src/Intervention/Image/Filters/DemoFilter.php b/src/Intervention/Image/Filters/DemoFilter.php deleted file mode 100644 index 4e8f92b82..000000000 --- a/src/Intervention/Image/Filters/DemoFilter.php +++ /dev/null @@ -1,44 +0,0 @@ -size = is_numeric($size) ? intval($size) : self::DEFAULT_SIZE; - } - - /** - * Applies filter effects to given image - * - * @param \Intervention\Image\Image $image - * @return \Intervention\Image\Image - */ - public function applyFilter(Image $image) - { - $image->pixelate($this->size); - $image->greyscale(); - - return $image; - } -} diff --git a/src/Intervention/Image/Filters/FilterInterface.php b/src/Intervention/Image/Filters/FilterInterface.php deleted file mode 100644 index 88f6cbfe0..000000000 --- a/src/Intervention/Image/Filters/FilterInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -a = ($value >> 24) & 0xFF; - $this->r = ($value >> 16) & 0xFF; - $this->g = ($value >> 8) & 0xFF; - $this->b = $value & 0xFF; - } - - /** - * Initiates color object from given array - * - * @param array $value - * @return \Intervention\Image\AbstractColor - */ - public function initFromArray($array) - { - $array = array_values($array); - - if (count($array) == 4) { - - // color array with alpha value - list($r, $g, $b, $a) = $array; - $this->a = $this->alpha2gd($a); - - } elseif (count($array) == 3) { - - // color array without alpha value - list($r, $g, $b) = $array; - $this->a = 0; - - } - - $this->r = $r; - $this->g = $g; - $this->b = $b; - } - - /** - * Initiates color object from given string - * - * @param string $value - * @return \Intervention\Image\AbstractColor - */ - public function initFromString($value) - { - if ($color = $this->rgbaFromString($value)) { - $this->r = $color[0]; - $this->g = $color[1]; - $this->b = $color[2]; - $this->a = $this->alpha2gd($color[3]); - } - } - - /** - * Initiates color object from given R, G and B values - * - * @param int $r - * @param int $g - * @param int $b - * @return \Intervention\Image\AbstractColor - */ - public function initFromRgb($r, $g, $b) - { - $this->r = intval($r); - $this->g = intval($g); - $this->b = intval($b); - $this->a = 0; - } - - /** - * Initiates color object from given R, G, B and A values - * - * @param int $r - * @param int $g - * @param int $b - * @param float $a - * @return \Intervention\Image\AbstractColor - */ - public function initFromRgba($r, $g, $b, $a = 1) - { - $this->r = intval($r); - $this->g = intval($g); - $this->b = intval($b); - $this->a = $this->alpha2gd($a); - } - - /** - * Initiates color object from given ImagickPixel object - * - * @param ImagickPixel $value - * @return \Intervention\Image\AbstractColor - */ - public function initFromObject($value) - { - throw new NotSupportedException( - "GD colors cannot init from ImagickPixel objects." - ); - } - - /** - * Calculates integer value of current color instance - * - * @return int - */ - public function getInt() - { - return ($this->a << 24) + ($this->r << 16) + ($this->g << 8) + $this->b; - } - - /** - * Calculates hexadecimal value of current color instance - * - * @param string $prefix - * @return string - */ - public function getHex($prefix = '') - { - return sprintf('%s%02x%02x%02x', $prefix, $this->r, $this->g, $this->b); - } - - /** - * Calculates RGB(A) in array format of current color instance - * - * @return array - */ - public function getArray() - { - return [$this->r, $this->g, $this->b, round(1 - $this->a / 127, 2)]; - } - - /** - * Calculates RGBA in string format of current color instance - * - * @return string - */ - public function getRgba() - { - return sprintf('rgba(%d, %d, %d, %.2F)', $this->r, $this->g, $this->b, round(1 - $this->a / 127, 2)); - } - - /** - * Determines if current color is different from given color - * - * @param AbstractColor $color - * @param int $tolerance - * @return boolean - */ - public function differs(AbstractColor $color, $tolerance = 0) - { - $color_tolerance = round($tolerance * 2.55); - $alpha_tolerance = round($tolerance * 1.27); - - $delta = [ - 'r' => abs($color->r - $this->r), - 'g' => abs($color->g - $this->g), - 'b' => abs($color->b - $this->b), - 'a' => abs($color->a - $this->a) - ]; - - return ( - $delta['r'] > $color_tolerance or - $delta['g'] > $color_tolerance or - $delta['b'] > $color_tolerance or - $delta['a'] > $alpha_tolerance - ); - } - - /** - * Convert rgba alpha (0-1) value to gd value (0-127) - * - * @param float $input - * @return int - */ - private function alpha2gd($input) - { - $oldMin = 0; - $oldMax = 1; - - $newMin = 127; - $newMax = 0; - - return ceil(((($input- $oldMin) * ($newMax - $newMin)) / ($oldMax - $oldMin)) + $newMin); - } -} diff --git a/src/Intervention/Image/Gd/Commands/BackupCommand.php b/src/Intervention/Image/Gd/Commands/BackupCommand.php deleted file mode 100644 index 310dc03a9..000000000 --- a/src/Intervention/Image/Gd/Commands/BackupCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->value(); - - // clone current image resource - $clone = clone $image; - $image->setBackup($clone->getCore(), $backupName); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/BlurCommand.php b/src/Intervention/Image/Gd/Commands/BlurCommand.php deleted file mode 100644 index c4fca0eae..000000000 --- a/src/Intervention/Image/Gd/Commands/BlurCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->between(0, 100)->value(1); - - for ($i=0; $i < intval($amount); $i++) { - imagefilter($image->getCore(), IMG_FILTER_GAUSSIAN_BLUR); - } - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php b/src/Intervention/Image/Gd/Commands/BrightnessCommand.php deleted file mode 100644 index 4e48464b1..000000000 --- a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - - return imagefilter($image->getCore(), IMG_FILTER_BRIGHTNESS, ($level * 2.55)); - } -} diff --git a/src/Intervention/Image/Gd/Commands/ColorizeCommand.php b/src/Intervention/Image/Gd/Commands/ColorizeCommand.php deleted file mode 100644 index 410917b33..000000000 --- a/src/Intervention/Image/Gd/Commands/ColorizeCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - $green = $this->argument(1)->between(-100, 100)->required()->value(); - $blue = $this->argument(2)->between(-100, 100)->required()->value(); - - // normalize colorize levels - $red = round($red * 2.55); - $green = round($green * 2.55); - $blue = round($blue * 2.55); - - // apply filter - return imagefilter($image->getCore(), IMG_FILTER_COLORIZE, $red, $green, $blue); - } -} diff --git a/src/Intervention/Image/Gd/Commands/ContrastCommand.php b/src/Intervention/Image/Gd/Commands/ContrastCommand.php deleted file mode 100644 index 916c41f82..000000000 --- a/src/Intervention/Image/Gd/Commands/ContrastCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - - return imagefilter($image->getCore(), IMG_FILTER_CONTRAST, ($level * -1)); - } -} diff --git a/src/Intervention/Image/Gd/Commands/CropCommand.php b/src/Intervention/Image/Gd/Commands/CropCommand.php deleted file mode 100644 index b7f595421..000000000 --- a/src/Intervention/Image/Gd/Commands/CropCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->required()->value(); - $x = $this->argument(2)->type('digit')->value(); - $y = $this->argument(3)->type('digit')->value(); - - if (is_null($width) || is_null($height)) { - throw new \Intervention\Image\Exception\InvalidArgumentException( - "Width and height of cutout needs to be defined." - ); - } - - $cropped = new Size($width, $height); - $position = new Point($x, $y); - - // align boxes - if (is_null($x) && is_null($y)) { - $position = $image->getSize()->align('center')->relativePosition($cropped->align('center')); - } - - // crop image core - return $this->modify($image, 0, 0, $position->x, $position->y, $cropped->width, $cropped->height, $cropped->width, $cropped->height); - } -} diff --git a/src/Intervention/Image/Gd/Commands/DestroyCommand.php b/src/Intervention/Image/Gd/Commands/DestroyCommand.php deleted file mode 100644 index 4503d10fe..000000000 --- a/src/Intervention/Image/Gd/Commands/DestroyCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -getCore()); - - // destroy backups - foreach ($image->getBackups() as $backup) { - imagedestroy($backup); - } - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/FillCommand.php b/src/Intervention/Image/Gd/Commands/FillCommand.php deleted file mode 100644 index cf1908210..000000000 --- a/src/Intervention/Image/Gd/Commands/FillCommand.php +++ /dev/null @@ -1,69 +0,0 @@ -argument(0)->value(); - $x = $this->argument(1)->type('digit')->value(); - $y = $this->argument(2)->type('digit')->value(); - - $width = $image->getWidth(); - $height = $image->getHeight(); - $resource = $image->getCore(); - - try { - - // set image tile filling - $source = new Decoder; - $tile = $source->init($filling); - imagesettile($image->getCore(), $tile->getCore()); - $filling = IMG_COLOR_TILED; - - } catch (\Intervention\Image\Exception\NotReadableException $e) { - - // set solid color filling - $color = new Color($filling); - $filling = $color->getInt(); - } - - imagealphablending($resource, true); - - if (is_int($x) && is_int($y)) { - - // resource should be visible through transparency - $base = $image->getDriver()->newImage($width, $height)->getCore(); - imagecopy($base, $resource, 0, 0, 0, 0, $width, $height); - - // floodfill if exact position is defined - imagefill($resource, $x, $y, $filling); - - // copy filled original over base - imagecopy($base, $resource, 0, 0, 0, 0, $width, $height); - - // set base as new resource-core - $image->setCore($base); - imagedestroy($resource); - - } else { - // fill whole image otherwise - imagefilledrectangle($resource, 0, 0, $width - 1, $height - 1, $filling); - } - - isset($tile) ? imagedestroy($tile->getCore()) : null; - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/FitCommand.php b/src/Intervention/Image/Gd/Commands/FitCommand.php deleted file mode 100644 index d861ad94c..000000000 --- a/src/Intervention/Image/Gd/Commands/FitCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->value($width); - $constraints = $this->argument(2)->type('closure')->value(); - $position = $this->argument(3)->type('string')->value('center'); - - // calculate size - $cropped = $image->getSize()->fit(new Size($width, $height), $position); - $resized = clone $cropped; - $resized = $resized->resize($width, $height, $constraints); - - // modify image - $this->modify($image, 0, 0, $cropped->pivot->x, $cropped->pivot->y, $resized->getWidth(), $resized->getHeight(), $cropped->getWidth(), $cropped->getHeight()); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/FlipCommand.php b/src/Intervention/Image/Gd/Commands/FlipCommand.php deleted file mode 100644 index aa8f230e8..000000000 --- a/src/Intervention/Image/Gd/Commands/FlipCommand.php +++ /dev/null @@ -1,37 +0,0 @@ -argument(0)->value('h'); - - $size = $image->getSize(); - $dst = clone $size; - - switch (strtolower($mode)) { - case 2: - case 'v': - case 'vert': - case 'vertical': - $size->pivot->y = $size->height - 1; - $size->height = $size->height * (-1); - break; - - default: - $size->pivot->x = $size->width - 1; - $size->width = $size->width * (-1); - break; - } - - return $this->modify($image, 0, 0, $size->pivot->x, $size->pivot->y, $dst->width, $dst->height, $size->width, $size->height); - } -} diff --git a/src/Intervention/Image/Gd/Commands/GammaCommand.php b/src/Intervention/Image/Gd/Commands/GammaCommand.php deleted file mode 100644 index 7de0fb8a1..000000000 --- a/src/Intervention/Image/Gd/Commands/GammaCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - - return imagegammacorrect($image->getCore(), 1, $gamma); - } -} diff --git a/src/Intervention/Image/Gd/Commands/GetSizeCommand.php b/src/Intervention/Image/Gd/Commands/GetSizeCommand.php deleted file mode 100644 index 9eb7e2092..000000000 --- a/src/Intervention/Image/Gd/Commands/GetSizeCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -setOutput(new Size( - imagesx($image->getCore()), - imagesy($image->getCore()) - )); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php b/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php deleted file mode 100644 index 12921e79e..000000000 --- a/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -getCore(), IMG_FILTER_GRAYSCALE); - } -} diff --git a/src/Intervention/Image/Gd/Commands/HeightenCommand.php b/src/Intervention/Image/Gd/Commands/HeightenCommand.php deleted file mode 100644 index d31e9cde0..000000000 --- a/src/Intervention/Image/Gd/Commands/HeightenCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $additionalConstraints = $this->argument(1)->type('closure')->value(); - - $this->arguments[0] = null; - $this->arguments[1] = $height; - $this->arguments[2] = function ($constraint) use ($additionalConstraints) { - $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) - $additionalConstraints($constraint); - }; - - return parent::execute($image); - } -} diff --git a/src/Intervention/Image/Gd/Commands/InsertCommand.php b/src/Intervention/Image/Gd/Commands/InsertCommand.php deleted file mode 100644 index 47c770398..000000000 --- a/src/Intervention/Image/Gd/Commands/InsertCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -argument(0)->required()->value(); - $position = $this->argument(1)->type('string')->value(); - $x = $this->argument(2)->type('digit')->value(0); - $y = $this->argument(3)->type('digit')->value(0); - - // build watermark - $watermark = $image->getDriver()->init($source); - - // define insertion point - $image_size = $image->getSize()->align($position, $x, $y); - $watermark_size = $watermark->getSize()->align($position); - $target = $image_size->relativePosition($watermark_size); - - // insert image at position - imagealphablending($image->getCore(), true); - return imagecopy($image->getCore(), $watermark->getCore(), $target->x, $target->y, 0, 0, $watermark_size->width, $watermark_size->height); - } -} diff --git a/src/Intervention/Image/Gd/Commands/InterlaceCommand.php b/src/Intervention/Image/Gd/Commands/InterlaceCommand.php deleted file mode 100644 index e3461cb07..000000000 --- a/src/Intervention/Image/Gd/Commands/InterlaceCommand.php +++ /dev/null @@ -1,23 +0,0 @@ -argument(0)->type('bool')->value(true); - - imageinterlace($image->getCore(), $mode); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/InvertCommand.php b/src/Intervention/Image/Gd/Commands/InvertCommand.php deleted file mode 100644 index 1a514f1d4..000000000 --- a/src/Intervention/Image/Gd/Commands/InvertCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -getCore(), IMG_FILTER_NEGATE); - } -} diff --git a/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php b/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php deleted file mode 100644 index 0baed7e91..000000000 --- a/src/Intervention/Image/Gd/Commands/LimitColorsCommand.php +++ /dev/null @@ -1,53 +0,0 @@ -argument(0)->value(); - $matte = $this->argument(1)->value(); - - // get current image size - $size = $image->getSize(); - - // create empty canvas - $resource = imagecreatetruecolor($size->width, $size->height); - - // define matte - if (is_null($matte)) { - $matte = imagecolorallocatealpha($resource, 255, 255, 255, 127); - } else { - $matte = $image->getDriver()->parseColor($matte)->getInt(); - } - - // fill with matte and copy original image - imagefill($resource, 0, 0, $matte); - - // set transparency - imagecolortransparent($resource, $matte); - - // copy original image - imagecopy($resource, $image->getCore(), 0, 0, 0, 0, $size->width, $size->height); - - if (is_numeric($count) && $count <= 256) { - // decrease colors - imagetruecolortopalette($resource, true, $count); - } - - // set new resource - $image->setCore($resource); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/MaskCommand.php b/src/Intervention/Image/Gd/Commands/MaskCommand.php deleted file mode 100644 index 4c61429b2..000000000 --- a/src/Intervention/Image/Gd/Commands/MaskCommand.php +++ /dev/null @@ -1,83 +0,0 @@ -argument(0)->value(); - $mask_w_alpha = $this->argument(1)->type('bool')->value(false); - - $image_size = $image->getSize(); - - // create empty canvas - $canvas = $image->getDriver()->newImage($image_size->width, $image_size->height, [0,0,0,0]); - - // build mask image from source - $mask = $image->getDriver()->init($mask_source); - $mask_size = $mask->getSize(); - - // resize mask to size of current image (if necessary) - if ($mask_size != $image_size) { - $mask->resize($image_size->width, $image_size->height); - } - - imagealphablending($canvas->getCore(), false); - - if ( ! $mask_w_alpha) { - // mask from greyscale image - imagefilter($mask->getCore(), IMG_FILTER_GRAYSCALE); - } - - // redraw old image pixel by pixel considering alpha map - for ($x=0; $x < $image_size->width; $x++) { - for ($y=0; $y < $image_size->height; $y++) { - - $color = $image->pickColor($x, $y, 'array'); - $alpha = $mask->pickColor($x, $y, 'array'); - - if ($mask_w_alpha) { - $alpha = $alpha[3]; // use alpha channel as mask - } else { - - if ($alpha[3] == 0) { // transparent as black - $alpha = 0; - } else { - - // $alpha = floatval(round((($alpha[0] + $alpha[1] + $alpha[3]) / 3) / 255, 2)); - - // image is greyscale, so channel doesn't matter (use red channel) - $alpha = floatval(round($alpha[0] / 255, 2)); - } - - } - - // preserve alpha of original image... - if ($color[3] < $alpha) { - $alpha = $color[3]; - } - - // replace alpha value - $color[3] = $alpha; - - // redraw pixel - $canvas->pixel($color, $x, $y); - } - } - - - // replace current image with masked instance - $image->setCore($canvas->getCore()); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/OpacityCommand.php b/src/Intervention/Image/Gd/Commands/OpacityCommand.php deleted file mode 100644 index 48492d24f..000000000 --- a/src/Intervention/Image/Gd/Commands/OpacityCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -argument(0)->between(0, 100)->required()->value(); - - // get size of image - $size = $image->getSize(); - - // build temp alpha mask - $mask_color = sprintf('rgba(0, 0, 0, %.1F)', $transparency / 100); - $mask = $image->getDriver()->newImage($size->width, $size->height, $mask_color); - - // mask image - $image->mask($mask->getCore(), true); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/PickColorCommand.php b/src/Intervention/Image/Gd/Commands/PickColorCommand.php deleted file mode 100644 index bad96f498..000000000 --- a/src/Intervention/Image/Gd/Commands/PickColorCommand.php +++ /dev/null @@ -1,37 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $y = $this->argument(1)->type('digit')->required()->value(); - $format = $this->argument(2)->type('string')->value('array'); - - // pick color - $color = imagecolorat($image->getCore(), $x, $y); - - if ( ! imageistruecolor($image->getCore())) { - $color = imagecolorsforindex($image->getCore(), $color); - $color['alpha'] = round(1 - $color['alpha'] / 127, 2); - } - - $color = new Color($color); - - // format to output - $this->setOutput($color->format($format)); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/PixelCommand.php b/src/Intervention/Image/Gd/Commands/PixelCommand.php deleted file mode 100644 index 2a90ce34f..000000000 --- a/src/Intervention/Image/Gd/Commands/PixelCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->required()->value(); - $color = new Color($color); - $x = $this->argument(1)->type('digit')->required()->value(); - $y = $this->argument(2)->type('digit')->required()->value(); - - return imagesetpixel($image->getCore(), $x, $y, $color->getInt()); - } -} diff --git a/src/Intervention/Image/Gd/Commands/PixelateCommand.php b/src/Intervention/Image/Gd/Commands/PixelateCommand.php deleted file mode 100644 index 0934797a8..000000000 --- a/src/Intervention/Image/Gd/Commands/PixelateCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->type('digit')->value(10); - - return imagefilter($image->getCore(), IMG_FILTER_PIXELATE, $size, true); - } -} diff --git a/src/Intervention/Image/Gd/Commands/ResetCommand.php b/src/Intervention/Image/Gd/Commands/ResetCommand.php deleted file mode 100644 index a68b75eaf..000000000 --- a/src/Intervention/Image/Gd/Commands/ResetCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -argument(0)->value(); - $backup = $image->getBackup($backupName); - - if (is_resource($backup) || $backup instanceof \GdImage) { - - // destroy current resource - imagedestroy($image->getCore()); - - // clone backup - $backup = $image->getDriver()->cloneCore($backup); - - // reset to new resource - $image->setCore($backup); - - return true; - } - - throw new RuntimeException( - "Backup not available. Call backup() before reset()." - ); - } -} diff --git a/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php deleted file mode 100644 index 73f3df308..000000000 --- a/src/Intervention/Image/Gd/Commands/ResizeCanvasCommand.php +++ /dev/null @@ -1,83 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->required()->value(); - $anchor = $this->argument(2)->value('center'); - $relative = $this->argument(3)->type('boolean')->value(false); - $bgcolor = $this->argument(4)->value(); - - $original_width = $image->getWidth(); - $original_height = $image->getHeight(); - - // check of only width or height is set - $width = is_null($width) ? $original_width : intval($width); - $height = is_null($height) ? $original_height : intval($height); - - // check on relative width/height - if ($relative) { - $width = $original_width + $width; - $height = $original_height + $height; - } - - // check for negative width/height - $width = ($width <= 0) ? $width + $original_width : $width; - $height = ($height <= 0) ? $height + $original_height : $height; - - // create new canvas - $canvas = $image->getDriver()->newImage($width, $height, $bgcolor); - - // set copy position - $canvas_size = $canvas->getSize()->align($anchor); - $image_size = $image->getSize()->align($anchor); - $canvas_pos = $image_size->relativePosition($canvas_size); - $image_pos = $canvas_size->relativePosition($image_size); - - if ($width <= $original_width) { - $dst_x = 0; - $src_x = $canvas_pos->x; - $src_w = $canvas_size->width; - } else { - $dst_x = $image_pos->x; - $src_x = 0; - $src_w = $original_width; - } - - if ($height <= $original_height) { - $dst_y = 0; - $src_y = $canvas_pos->y; - $src_h = $canvas_size->height; - } else { - $dst_y = $image_pos->y; - $src_y = 0; - $src_h = $original_height; - } - - // make image area transparent to keep transparency - // even if background-color is set - $transparent = imagecolorallocatealpha($canvas->getCore(), 255, 255, 255, 127); - imagealphablending($canvas->getCore(), false); // do not blend / just overwrite - imagefilledrectangle($canvas->getCore(), $dst_x, $dst_y, $dst_x + $src_w - 1, $dst_y + $src_h - 1, $transparent); - - // copy image into new canvas - imagecopy($canvas->getCore(), $image->getCore(), $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h); - - // set new core to canvas - $image->setCore($canvas->getCore()); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/ResizeCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCommand.php deleted file mode 100644 index 382d9709e..000000000 --- a/src/Intervention/Image/Gd/Commands/ResizeCommand.php +++ /dev/null @@ -1,84 +0,0 @@ -argument(0)->value(); - $height = $this->argument(1)->value(); - $constraints = $this->argument(2)->type('closure')->value(); - - // resize box - $resized = $image->getSize()->resize($width, $height, $constraints); - - // modify image - $this->modify($image, 0, 0, 0, 0, $resized->getWidth(), $resized->getHeight(), $image->getWidth(), $image->getHeight()); - - return true; - } - - /** - * Wrapper function for 'imagecopyresampled' - * - * @param Image $image - * @param int $dst_x - * @param int $dst_y - * @param int $src_x - * @param int $src_y - * @param int $dst_w - * @param int $dst_h - * @param int $src_w - * @param int $src_h - * @return boolean - */ - protected function modify($image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) - { - // create new image - $modified = imagecreatetruecolor($dst_w, $dst_h); - - // get current image - $resource = $image->getCore(); - - // preserve transparency - $transIndex = imagecolortransparent($resource); - - 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); - } - - // copy content from resource - $result = imagecopyresampled( - $modified, - $resource, - $dst_x, - $dst_y, - $src_x, - $src_y, - $dst_w, - $dst_h, - $src_w, - $src_h - ); - - // set new content as recource - $image->setCore($modified); - - return $result; - } -} diff --git a/src/Intervention/Image/Gd/Commands/RotateCommand.php b/src/Intervention/Image/Gd/Commands/RotateCommand.php deleted file mode 100644 index 682ec0d4a..000000000 --- a/src/Intervention/Image/Gd/Commands/RotateCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $color = $this->argument(1)->value(); - $color = new Color($color); - - // restrict rotations beyond 360 degrees, since the end result is the same - $angle = fmod($angle, 360); - - // rotate image - $image->setCore(imagerotate($image->getCore(), $angle, $color->getInt())); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Commands/SharpenCommand.php b/src/Intervention/Image/Gd/Commands/SharpenCommand.php deleted file mode 100644 index 782e56502..000000000 --- a/src/Intervention/Image/Gd/Commands/SharpenCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -argument(0)->between(0, 100)->value(10); - - // build matrix - $min = $amount >= 10 ? $amount * -0.01 : 0; - $max = $amount * -0.025; - $abs = ((4 * $min + 4 * $max) * -1) + 1; - $div = 1; - - $matrix = [ - [$min, $max, $min], - [$max, $abs, $max], - [$min, $max, $min] - ]; - - // apply the matrix - return imageconvolution($image->getCore(), $matrix, $div, 0); - } -} diff --git a/src/Intervention/Image/Gd/Commands/TrimCommand.php b/src/Intervention/Image/Gd/Commands/TrimCommand.php deleted file mode 100644 index 2e3697527..000000000 --- a/src/Intervention/Image/Gd/Commands/TrimCommand.php +++ /dev/null @@ -1,176 +0,0 @@ -argument(0)->type('string')->value(); - $away = $this->argument(1)->value(); - $tolerance = $this->argument(2)->type('numeric')->value(0); - $feather = $this->argument(3)->type('numeric')->value(0); - - $width = $image->getWidth(); - $height = $image->getHeight(); - - // default values - $checkTransparency = false; - - // define borders to trim away - if (is_null($away)) { - $away = ['top', 'right', 'bottom', 'left']; - } elseif (is_string($away)) { - $away = [$away]; - } - - // lower border names - foreach ($away as $key => $value) { - $away[$key] = strtolower($value); - } - - // define base color position - switch (strtolower($base)) { - case 'transparent': - case 'trans': - $checkTransparency = true; - $base_x = 0; - $base_y = 0; - break; - - case 'bottom-right': - case 'right-bottom': - $base_x = $width - 1; - $base_y = $height - 1; - break; - - default: - case 'top-left': - case 'left-top': - $base_x = 0; - $base_y = 0; - break; - } - - // pick base color - if ($checkTransparency) { - $color = new Color; // color will only be used to compare alpha channel - } else { - $color = $image->pickColor($base_x, $base_y, 'object'); - } - - $top_x = 0; - $top_y = 0; - $bottom_x = $width; - $bottom_y = $height; - - // search upper part of image for colors to trim away - if (in_array('top', $away)) { - - for ($y=0; $y < ceil($height/2); $y++) { - for ($x=0; $x < $width; $x++) { - - $checkColor = $image->pickColor($x, $y, 'object'); - - if ($checkTransparency) { - $checkColor->r = $color->r; - $checkColor->g = $color->g; - $checkColor->b = $color->b; - } - - if ($color->differs($checkColor, $tolerance)) { - $top_y = max(0, $y - $feather); - break 2; - } - - } - } - - } - - // search left part of image for colors to trim away - if (in_array('left', $away)) { - - for ($x=0; $x < ceil($width/2); $x++) { - for ($y=$top_y; $y < $height; $y++) { - - $checkColor = $image->pickColor($x, $y, 'object'); - - if ($checkTransparency) { - $checkColor->r = $color->r; - $checkColor->g = $color->g; - $checkColor->b = $color->b; - } - - if ($color->differs($checkColor, $tolerance)) { - $top_x = max(0, $x - $feather); - break 2; - } - - } - } - - } - - // search lower part of image for colors to trim away - if (in_array('bottom', $away)) { - - for ($y=($height-1); $y >= floor($height/2)-1; $y--) { - for ($x=$top_x; $x < $width; $x++) { - - $checkColor = $image->pickColor($x, $y, 'object'); - - if ($checkTransparency) { - $checkColor->r = $color->r; - $checkColor->g = $color->g; - $checkColor->b = $color->b; - } - - if ($color->differs($checkColor, $tolerance)) { - $bottom_y = min($height, $y+1 + $feather); - break 2; - } - - } - } - - } - - // search right part of image for colors to trim away - if (in_array('right', $away)) { - - for ($x=($width-1); $x >= floor($width/2)-1; $x--) { - for ($y=$top_y; $y < $bottom_y; $y++) { - - $checkColor = $image->pickColor($x, $y, 'object'); - - if ($checkTransparency) { - $checkColor->r = $color->r; - $checkColor->g = $color->g; - $checkColor->b = $color->b; - } - - if ($color->differs($checkColor, $tolerance)) { - $bottom_x = min($width, $x+1 + $feather); - break 2; - } - - } - } - - } - - - // trim parts of image - return $this->modify($image, 0, 0, $top_x, $top_y, ($bottom_x-$top_x), ($bottom_y-$top_y), ($bottom_x-$top_x), ($bottom_y-$top_y)); - - } -} diff --git a/src/Intervention/Image/Gd/Commands/WidenCommand.php b/src/Intervention/Image/Gd/Commands/WidenCommand.php deleted file mode 100644 index 43000d5dc..000000000 --- a/src/Intervention/Image/Gd/Commands/WidenCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $additionalConstraints = $this->argument(1)->type('closure')->value(); - - $this->arguments[0] = $width; - $this->arguments[1] = null; - $this->arguments[2] = function ($constraint) use ($additionalConstraints) { - $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) - $additionalConstraints($constraint); - }; - - return parent::execute($image); - } -} diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php deleted file mode 100644 index f5c34aa50..000000000 --- a/src/Intervention/Image/Gd/Decoder.php +++ /dev/null @@ -1,171 +0,0 @@ -gdResourceToTruecolor($core); - - // build image - $image = $this->initFromGdResource($core); - $image->mime = $mime; - $image->setFileInfoFromPath($path); - - return $image; - } - - /** - * Initiates new image from GD resource - * - * @param Resource $resource - * @return \Intervention\Image\Image - */ - public function initFromGdResource($resource) - { - return new Image(new Driver, $resource); - } - - /** - * Initiates new image from Imagick object - * - * @param Imagick $object - * @return \Intervention\Image\Image - */ - public function initFromImagick(\Imagick $object) - { - throw new NotSupportedException( - "Gd driver is unable to init from Imagick object." - ); - } - - /** - * Initiates new image from binary data - * - * @param string $data - * @return \Intervention\Image\Image - */ - public function initFromBinary($binary) - { - $resource = @imagecreatefromstring($binary); - - if ($resource === false) { - throw new NotReadableException( - "Unable to init from given binary data." - ); - } - - $image = $this->initFromGdResource($resource); - $image->mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $binary); - - return $image; - } - - /** - * Transform GD resource into Truecolor version - * - * @param resource $resource - * @return bool - */ - public function gdResourceToTruecolor(&$resource) - { - $width = imagesx($resource); - $height = imagesy($resource); - - // 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, $resource, 0, 0, 0, 0, $width, $height); - imagedestroy($resource); - - $resource = $canvas; - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php deleted file mode 100644 index 5f2f23ea3..000000000 --- a/src/Intervention/Image/Gd/Driver.php +++ /dev/null @@ -1,89 +0,0 @@ -coreAvailable()) { - throw new NotSupportedException( - "GD Library extension not available with this PHP installation." - ); - } - - $this->decoder = $decoder ? $decoder : new Decoder; - $this->encoder = $encoder ? $encoder : new Encoder; - } - - /** - * Creates new image instance - * - * @param int $width - * @param int $height - * @param mixed $background - * @return \Intervention\Image\Image - */ - public function newImage($width, $height, $background = null) - { - // create empty resource - $core = imagecreatetruecolor($width, $height); - $image = new Image(new static, $core); - - // set background color - $background = new Color($background); - imagefill($image->getCore(), 0, 0, $background->getInt()); - - return $image; - } - - /** - * Reads given string into color object - * - * @param string $value - * @return AbstractColor - */ - public function parseColor($value) - { - return new Color($value); - } - - /** - * Checks if core module installation is available - * - * @return boolean - */ - protected function coreAvailable() - { - return (extension_loaded('gd') && function_exists('gd_info')); - } - - /** - * Returns clone of given core - * - * @return mixed - */ - public function cloneCore($core) - { - $width = imagesx($core); - $height = imagesy($core); - $clone = imagecreatetruecolor($width, $height); - imagealphablending($clone, false); - imagesavealpha($clone, true); - $transparency = imagecolorallocatealpha($clone, 0, 0, 0, 127); - imagefill($clone, 0, 0, $transparency); - - imagecopy($clone, $core, 0, 0, 0, 0, $width, $height); - - return $clone; - } -} diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php deleted file mode 100644 index 559d60dcc..000000000 --- a/src/Intervention/Image/Gd/Encoder.php +++ /dev/null @@ -1,180 +0,0 @@ -image->getCore(), null, $this->quality); - $this->image->mime = image_type_to_mime_type(IMAGETYPE_JPEG); - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as PNG string - * - * @return string - */ - protected function processPng() - { - ob_start(); - $resource = $this->image->getCore(); - imagealphablending($resource, false); - imagesavealpha($resource, true); - imagepng($resource, null, -1); - $this->image->mime = image_type_to_mime_type(IMAGETYPE_PNG); - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as GIF string - * - * @return string - */ - protected function processGif() - { - ob_start(); - imagegif($this->image->getCore()); - $this->image->mime = image_type_to_mime_type(IMAGETYPE_GIF); - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as WEBP string - * - * @return string - */ - protected function processWebp() - { - if ( ! function_exists('imagewebp')) { - throw new NotSupportedException( - "Webp format is not supported by PHP installation." - ); - } - - ob_start(); - imagepalettetotruecolor($this->image->getCore()); - imagealphablending($this->image->getCore(), true); - imagesavealpha($this->image->getCore(), true); - imagewebp($this->image->getCore(), null, $this->quality); - $this->image->mime = defined('IMAGETYPE_WEBP') ? image_type_to_mime_type(IMAGETYPE_WEBP) : 'image/webp'; - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as TIFF string - * - * @return string - */ - protected function processTiff() - { - throw new NotSupportedException( - "TIFF format is not supported by Gd Driver." - ); - } - - /** - * Processes and returns encoded image as BMP string - * - * @return string - */ - protected function processBmp() - { - if ( ! function_exists('imagebmp')) { - throw new NotSupportedException( - "BMP format is not supported by PHP installation." - ); - } - - ob_start(); - imagebmp($this->image->getCore()); - $this->image->mime = defined('IMAGETYPE_BMP') ? image_type_to_mime_type(IMAGETYPE_BMP) : 'image/bmp'; - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as ICO string - * - * @return string - */ - protected function processIco() - { - throw new NotSupportedException( - "ICO format is not supported by Gd Driver." - ); - } - - /** - * Processes and returns encoded image as PSD string - * - * @return string - */ - protected function processPsd() - { - throw new NotSupportedException( - "PSD format is not supported by Gd Driver." - ); - } - - /** - * Processes and returns encoded image as AVIF string - * - * @return string - */ - protected function processAvif() - { - if ( ! function_exists('imageavif')) { - throw new NotSupportedException( - "AVIF format is not supported by PHP installation." - ); - } - - ob_start(); - $resource = $this->image->getCore(); - imagepalettetotruecolor($resource); - imagealphablending($resource, true); - imagesavealpha($resource, true); - imageavif($resource, null, $this->quality); - $this->image->mime = defined('IMAGETYPE_AVIF') ? image_type_to_mime_type(IMAGETYPE_AVIF) : 'image/avif'; - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Processes and returns encoded image as HEIC string - * - * @return string - */ - protected function processHeic() - { - throw new NotSupportedException( - "HEIC format is not supported by Gd Driver." - ); - } -} diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php deleted file mode 100644 index 3f5888780..000000000 --- a/src/Intervention/Image/Gd/Font.php +++ /dev/null @@ -1,277 +0,0 @@ -size * 0.75)); - } - - /** - * Filter function to access internal integer font values - * - * @return int - */ - private function getInternalFont() - { - $internalfont = is_null($this->file) ? 1 : $this->file; - $internalfont = is_numeric($internalfont) ? $internalfont : false; - - if ( ! in_array($internalfont, [1, 2, 3, 4, 5])) { - throw new NotSupportedException( - sprintf('Internal GD font (%s) not available. Use only 1-5.', $internalfont) - ); - } - - return intval($internalfont); - } - - /** - * Get width of an internal font character - * - * @return int - */ - private function getInternalFontWidth() - { - return $this->getInternalFont() + 4; - } - - /** - * Get height of an internal font character - * - * @return int - */ - private function getInternalFontHeight() - { - switch ($this->getInternalFont()) { - case 1: - return 8; - - case 2: - return 14; - - case 3: - return 14; - - case 4: - return 16; - - case 5: - return 16; - } - } - - /** - * Calculates bounding box of current font setting - * - * @return Array - */ - public function getBoxSize() - { - $box = []; - - if ($this->hasApplicableFontFile()) { - - // imagettfbbox() converts numeric entities to their respective - // character. Preserve any originally double encoded entities to be - // represented as is. - // eg: &#160; will render   rather than its character. - $this->text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $this->text); - $this->text = mb_encode_numericentity($this->text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8'); - - // get bounding box with angle 0 - $box = imagettfbbox($this->getPointSize(), 0, $this->file, $this->text); - - // rotate points manually - if ($this->angle != 0) { - - $angle = pi() * 2 - $this->angle * pi() * 2 / 360; - - for ($i=0; $i<4; $i++) { - $x = $box[$i * 2]; - $y = $box[$i * 2 + 1]; - $box[$i * 2] = cos($angle) * $x - sin($angle) * $y; - $box[$i * 2 + 1] = sin($angle) * $x + cos($angle) * $y; - } - } - - $box['width'] = intval(abs($box[4] - $box[0])); - $box['height'] = intval(abs($box[5] - $box[1])); - - } else { - - // get current internal font size - $width = $this->getInternalFontWidth(); - $height = $this->getInternalFontHeight(); - - if (strlen($this->text) == 0) { - // no text -> no boxsize - $box['width'] = 0; - $box['height'] = 0; - } else { - // calculate boxsize - $box['width'] = strlen($this->text) * $width; - $box['height'] = $height; - } - } - - return $box; - } - - /** - * Draws font to given image at given position - * - * @param Image $image - * @param int $posx - * @param int $posy - * @return void - */ - public function applyToImage(Image $image, $posx = 0, $posy = 0) - { - // parse text color - $color = new Color($this->color); - - if ($this->hasApplicableFontFile()) { - - if ($this->angle != 0 || is_string($this->align) || is_string($this->valign)) { - - $box = $this->getBoxSize(); - - $align = is_null($this->align) ? 'left' : strtolower($this->align); - $valign = is_null($this->valign) ? 'bottom' : strtolower($this->valign); - - // correction on position depending on v/h alignment - switch ($align.'-'.$valign) { - - case 'center-top': - $posx = $posx - round(($box[6]+$box[4])/2); - $posy = $posy - round(($box[7]+$box[5])/2); - break; - - case 'right-top': - $posx = $posx - $box[4]; - $posy = $posy - $box[5]; - break; - - case 'left-top': - $posx = $posx - $box[6]; - $posy = $posy - $box[7]; - break; - - case 'center-center': - case 'center-middle': - $posx = $posx - round(($box[0]+$box[4])/2); - $posy = $posy - round(($box[1]+$box[5])/2); - break; - - case 'right-center': - case 'right-middle': - $posx = $posx - round(($box[2]+$box[4])/2); - $posy = $posy - round(($box[3]+$box[5])/2); - break; - - case 'left-center': - case 'left-middle': - $posx = $posx - round(($box[0]+$box[6])/2); - $posy = $posy - round(($box[1]+$box[7])/2); - break; - - case 'center-bottom': - $posx = $posx - round(($box[0]+$box[2])/2); - $posy = $posy - round(($box[1]+$box[3])/2); - break; - - case 'right-bottom': - $posx = $posx - $box[2]; - $posy = $posy - $box[3]; - break; - - case 'left-bottom': - $posx = $posx - $box[0]; - $posy = $posy - $box[1]; - break; - } - } - - // enable alphablending for imagettftext - imagealphablending($image->getCore(), true); - - // draw ttf text - imagettftext($image->getCore(), $this->getPointSize(), $this->angle, $posx, $posy, $color->getInt(), $this->file, $this->text); - - } else { - - // get box size - $box = $this->getBoxSize(); - $width = $box['width']; - $height = $box['height']; - - // internal font specific position corrections - if ($this->getInternalFont() == 1) { - $top_correction = 1; - $bottom_correction = 2; - } elseif ($this->getInternalFont() == 3) { - $top_correction = 2; - $bottom_correction = 4; - } else { - $top_correction = 3; - $bottom_correction = 4; - } - - // x-position corrections for horizontal alignment - switch (strtolower($this->align)) { - case 'center': - $posx = ceil($posx - ($width / 2)); - break; - - case 'right': - $posx = ceil($posx - $width) + 1; - break; - } - - // y-position corrections for vertical alignment - switch (strtolower($this->valign)) { - case 'center': - case 'middle': - $posy = ceil($posy - ($height / 2)); - break; - - case 'top': - $posy = ceil($posy - $top_correction); - break; - - default: - case 'bottom': - $posy = round($posy - $height + $bottom_correction); - break; - } - - // draw text - imagestring($image->getCore(), $this->getInternalFont(), $posx, $posy, $this->text, $color->getInt()); - } - } - - /** - * Set text kerning - * - * @param string $kerning - * @return void - */ - public function kerning($kerning) - { - throw new \Intervention\Image\Exception\NotSupportedException( - "Kerning is not supported by GD driver." - ); - } - -} diff --git a/src/Intervention/Image/Gd/Shapes/CircleShape.php b/src/Intervention/Image/Gd/Shapes/CircleShape.php deleted file mode 100644 index c3c42144d..000000000 --- a/src/Intervention/Image/Gd/Shapes/CircleShape.php +++ /dev/null @@ -1,40 +0,0 @@ -width = is_numeric($diameter) ? intval($diameter) : $this->diameter; - $this->height = is_numeric($diameter) ? intval($diameter) : $this->diameter; - $this->diameter = is_numeric($diameter) ? intval($diameter) : $this->diameter; - } - - /** - * Draw current circle on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - return parent::applyToImage($image, $x, $y); - } -} diff --git a/src/Intervention/Image/Gd/Shapes/EllipseShape.php b/src/Intervention/Image/Gd/Shapes/EllipseShape.php deleted file mode 100644 index 78e5e4a59..000000000 --- a/src/Intervention/Image/Gd/Shapes/EllipseShape.php +++ /dev/null @@ -1,65 +0,0 @@ -width = is_numeric($width) ? intval($width) : $this->width; - $this->height = is_numeric($height) ? intval($height) : $this->height; - } - - /** - * Draw ellipse instance on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - // parse background color - $background = new Color($this->background); - - if ($this->hasBorder()) { - // slightly smaller ellipse to keep 1px bordered edges clean - imagefilledellipse($image->getCore(), $x, $y, $this->width-1, $this->height-1, $background->getInt()); - - $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); - - // gd's imageellipse doesn't respect imagesetthickness so i use imagearc with 359.9 degrees here - imagearc($image->getCore(), $x, $y, $this->width, $this->height, 0, 359.99, $border_color->getInt()); - } else { - imagefilledellipse($image->getCore(), $x, $y, $this->width, $this->height, $background->getInt()); - } - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Shapes/LineShape.php b/src/Intervention/Image/Gd/Shapes/LineShape.php deleted file mode 100644 index ea38b513e..000000000 --- a/src/Intervention/Image/Gd/Shapes/LineShape.php +++ /dev/null @@ -1,90 +0,0 @@ -x = is_numeric($x) ? intval($x) : $this->x; - $this->y = is_numeric($y) ? intval($y) : $this->y; - } - - /** - * Set current line color - * - * @param string $color - * @return void - */ - public function color($color) - { - $this->color = $color; - } - - /** - * Set current line width in pixels - * - * @param int $width - * @return void - */ - public function width($width) - { - throw new \Intervention\Image\Exception\NotSupportedException( - "Line width is not supported by GD driver." - ); - } - - /** - * Draw current instance of line to given endpoint on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $color = new Color($this->color); - imageline($image->getCore(), $x, $y, $this->x, $this->y, $color->getInt()); - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Shapes/PolygonShape.php b/src/Intervention/Image/Gd/Shapes/PolygonShape.php deleted file mode 100644 index 5e14df40e..000000000 --- a/src/Intervention/Image/Gd/Shapes/PolygonShape.php +++ /dev/null @@ -1,49 +0,0 @@ -points = $points; - } - - /** - * Draw polygon on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $background = new Color($this->background); - imagefilledpolygon($image->getCore(), $this->points, intval(count($this->points) / 2), $background->getInt()); - - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); - imagepolygon($image->getCore(), $this->points, intval(count($this->points) / 2), $border_color->getInt()); - } - - return true; - } -} diff --git a/src/Intervention/Image/Gd/Shapes/RectangleShape.php b/src/Intervention/Image/Gd/Shapes/RectangleShape.php deleted file mode 100644 index 5f69a7f92..000000000 --- a/src/Intervention/Image/Gd/Shapes/RectangleShape.php +++ /dev/null @@ -1,76 +0,0 @@ -x1 = is_numeric($x1) ? intval($x1) : $this->x1; - $this->y1 = is_numeric($y1) ? intval($y1) : $this->y1; - $this->x2 = is_numeric($x2) ? intval($x2) : $this->x2; - $this->y2 = is_numeric($y2) ? intval($y2) : $this->y2; - } - - /** - * Draw rectangle to given image at certain position - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $background = new Color($this->background); - imagefilledrectangle($image->getCore(), $this->x1, $this->y1, $this->x2, $this->y2, $background->getInt()); - - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); - imagerectangle($image->getCore(), $this->x1, $this->y1, $this->x2, $this->y2, $border_color->getInt()); - } - - return true; - } -} diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php deleted file mode 100644 index 7d5a59b7d..000000000 --- a/src/Intervention/Image/Image.php +++ /dev/null @@ -1,370 +0,0 @@ -driver = $driver; - $this->core = $core; - } - - /** - * Magic method to catch all image calls - * usually any AbstractCommand - * - * @param string $name - * @param Array $arguments - * @return mixed - */ - public function __call($name, $arguments) - { - $command = $this->driver->executeCommand($this, $name, $arguments); - return $command->hasOutput() ? $command->getOutput() : $this; - } - - /** - * Starts encoding of current image - * - * @param string $format - * @param int $quality - * @return \Intervention\Image\Image - */ - public function encode($format = null, $quality = 90) - { - return $this->driver->encode($this, $format, $quality); - } - - /** - * Saves encoded image in filesystem - * - * @param string $path - * @param int $quality - * @param string $format - * @return \Intervention\Image\Image - */ - public function save($path = null, $quality = null, $format = null) - { - $path = is_null($path) ? $this->basePath() : $path; - - if (is_null($path)) { - throw new NotWritableException( - "Can't write to undefined path." - ); - } - - if ($format === null) { - $format = pathinfo($path, PATHINFO_EXTENSION); - } - - $data = $this->encode($format, $quality); - $saved = @file_put_contents($path, $data); - - if ($saved === false) { - throw new NotWritableException( - "Can't write image data to path ({$path})" - ); - } - - // set new file info - $this->setFileInfoFromPath($path); - - return $this; - } - - /** - * Runs a given filter on current image - * - * @param FiltersFilterInterface $filter - * @return \Intervention\Image\Image - */ - public function filter(Filters\FilterInterface $filter) - { - return $filter->applyFilter($this); - } - - /** - * Returns current image driver - * - * @return \Intervention\Image\AbstractDriver - */ - public function getDriver() - { - return $this->driver; - } - - /** - * Sets current image driver - * @param AbstractDriver $driver - */ - public function setDriver(AbstractDriver $driver) - { - $this->driver = $driver; - - return $this; - } - - /** - * Returns current image resource/obj - * - * @return mixed - */ - public function getCore() - { - return $this->core; - } - - /** - * Sets current image resource - * - * @param mixed $core - */ - public function setCore($core) - { - $this->core = $core; - - return $this; - } - - /** - * Returns current image backup - * - * @param string $name - * @return mixed - */ - public function getBackup($name = null) - { - $name = is_null($name) ? 'default' : $name; - - if ( ! $this->backupExists($name)) { - throw new RuntimeException( - "Backup with name ({$name}) not available. Call backup() before reset()." - ); - } - - return $this->backups[$name]; - } - - /** - * Returns all backups attached to image - * - * @return array - */ - public function getBackups() - { - return $this->backups; - } - - /** - * Sets current image backup - * - * @param mixed $resource - * @param string $name - * @return self - */ - public function setBackup($resource, $name = null) - { - $name = is_null($name) ? 'default' : $name; - - $this->backups[$name] = $resource; - - return $this; - } - - /** - * Checks if named backup exists - * - * @param string $name - * @return bool - */ - private function backupExists($name) - { - return array_key_exists($name, $this->backups); - } - - /** - * Checks if current image is already encoded - * - * @return boolean - */ - public function isEncoded() - { - return ! empty($this->encoded); - } - - /** - * Returns encoded image data of current image - * - * @return string - */ - public function getEncoded() - { - return $this->encoded; - } - - /** - * Sets encoded image buffer - * - * @param string $value - */ - public function setEncoded($value) - { - $this->encoded = $value; - - return $this; - } - - /** - * Calculates current image width - * - * @return int - */ - public function getWidth() - { - return $this->getSize()->width; - } - - /** - * Alias of getWidth() - * - * @return int - */ - public function width() - { - return $this->getWidth(); - } - - /** - * Calculates current image height - * - * @return int - */ - public function getHeight() - { - return $this->getSize()->height; - } - - /** - * Alias of getHeight - * - * @return int - */ - public function height() - { - return $this->getHeight(); - } - - /** - * Reads mime type - * - * @return string - */ - public function mime() - { - return $this->mime; - } - - /** - * Returns encoded image data in string conversion - * - * @return string - */ - public function __toString() - { - return $this->encoded; - } - - /** - * Cloning an image - */ - public function __clone() - { - $this->core = $this->driver->cloneCore($this->core); - } -} diff --git a/src/Intervention/Image/ImageManager.php b/src/Intervention/Image/ImageManager.php deleted file mode 100644 index 324fe7fa3..000000000 --- a/src/Intervention/Image/ImageManager.php +++ /dev/null @@ -1,142 +0,0 @@ - 'gd' - ]; - - /** - * Creates new instance of Image Manager - * - * @param array $config - */ - public function __construct(array $config = []) - { - $this->checkRequirements(); - $this->configure($config); - } - - /** - * Overrides configuration settings - * - * @param array $config - * - * @return self - */ - public function configure(array $config = []) - { - $this->config = array_replace($this->config, $config); - - return $this; - } - - /** - * Initiates an Image instance from different input types - * - * @param mixed $data - * - * @return \Intervention\Image\Image - */ - public function make($data) - { - return $this->createDriver()->init($data); - } - - /** - * Creates an empty image canvas - * - * @param int $width - * @param int $height - * @param mixed $background - * - * @return \Intervention\Image\Image - */ - public function canvas($width, $height, $background = null) - { - return $this->createDriver()->newImage($width, $height, $background); - } - - /** - * Create new cached image and run callback - * (requires additional package intervention/imagecache) - * - * @param Closure $callback - * @param int $lifetime - * @param boolean $returnObj - * - * @return Image - */ - public function cache(Closure $callback, $lifetime = null, $returnObj = false) - { - if (class_exists('Intervention\\Image\\ImageCache')) { - // create imagecache - $imagecache = new ImageCache($this); - - // run callback - if (is_callable($callback)) { - $callback($imagecache); - } - - return $imagecache->get($lifetime, $returnObj); - } - - throw new MissingDependencyException( - "Please install package intervention/imagecache before running this function." - ); - } - - /** - * Creates a driver instance according to config settings - * - * @return \Intervention\Image\AbstractDriver - */ - private function createDriver() - { - if (is_string($this->config['driver'])) { - $drivername = ucfirst($this->config['driver']); - $driverclass = sprintf('Intervention\\Image\\%s\\Driver', $drivername); - - if (class_exists($driverclass)) { - return new $driverclass; - } - - throw new NotSupportedException( - "Driver ({$drivername}) could not be instantiated." - ); - } - - if ($this->config['driver'] instanceof AbstractDriver) { - return $this->config['driver']; - } - - throw new NotSupportedException( - "Unknown driver type." - ); - } - - /** - * Check if all requirements are available - * - * @return void - */ - private function checkRequirements() - { - if ( ! function_exists('finfo_buffer')) { - throw new MissingDependencyException( - "PHP Fileinfo extension must be installed/enabled to use Intervention Image." - ); - } - } -} diff --git a/src/Intervention/Image/ImageManagerStatic.php b/src/Intervention/Image/ImageManagerStatic.php deleted file mode 100644 index a1b564264..000000000 --- a/src/Intervention/Image/ImageManagerStatic.php +++ /dev/null @@ -1,88 +0,0 @@ -configure($config); - } - - /** - * Statically initiates an Image instance from different input types - * - * @param mixed $data - * - * @return \Intervention\Image\Image - * @throws \Intervention\Image\Exception\NotReadableException - */ - public static function make($data) - { - return self::getManager()->make($data); - } - - /** - * Statically creates an empty image canvas - * - * @param int $width - * @param int $height - * @param mixed $background - * - * @return \Intervention\Image\Image - */ - public static function canvas($width, $height, $background = null) - { - return self::getManager()->canvas($width, $height, $background); - } - - /** - * Create new cached image and run callback statically - * - * @param Closure $callback - * @param int $lifetime - * @param boolean $returnObj - * - * @return mixed - */ - public static function cache(Closure $callback, $lifetime = null, $returnObj = false) - { - return self::getManager()->cache($callback, $lifetime, $returnObj); - } -} diff --git a/src/Intervention/Image/ImageServiceProvider.php b/src/Intervention/Image/ImageServiceProvider.php deleted file mode 100644 index f99fe4a37..000000000 --- a/src/Intervention/Image/ImageServiceProvider.php +++ /dev/null @@ -1,87 +0,0 @@ -provider = $this->getProvider(); - } - - /** - * Bootstrap the application events. - * - * @return void - */ - public function boot() - { - if (method_exists($this->provider, 'boot')) { - return $this->provider->boot(); - } - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - return $this->provider->register(); - } - - /** - * Return ServiceProvider according to Laravel version - * - * @return \Intervention\Image\Provider\ProviderInterface - */ - private function getProvider() - { - if ($this->app instanceof LumenApplication) { - $provider = '\Intervention\Image\ImageServiceProviderLumen'; - } elseif (version_compare(IlluminateApplication::VERSION, '5.0', '<')) { - $provider = '\Intervention\Image\ImageServiceProviderLaravel4'; - } else { - $provider = '\Intervention\Image\ImageServiceProviderLaravelRecent'; - } - - return new $provider($this->app); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() - { - return ['image']; - } -} diff --git a/src/Intervention/Image/ImageServiceProviderLaravel4.php b/src/Intervention/Image/ImageServiceProviderLaravel4.php deleted file mode 100755 index 3b1388f25..000000000 --- a/src/Intervention/Image/ImageServiceProviderLaravel4.php +++ /dev/null @@ -1,112 +0,0 @@ -package('intervention/image'); - - // try to create imagecache route only if imagecache is present - if (class_exists('Intervention\\Image\\ImageCache')) { - - $app = $this->app; - - // load imagecache config - $app['config']->package('intervention/imagecache', __DIR__.'/../../../../imagecache/src/config', 'imagecache'); - $config = $app['config']; - - // create dynamic manipulation route - if (is_string($config->get('imagecache::route'))) { - - // add original to route templates - $config->set('imagecache::templates.original', null); - - // setup image manipulator route - $app['router']->get($config->get('imagecache::route').'/{template}/{filename}', ['as' => 'imagecache', function ($template, $filename) use ($app, $config) { - - // disable session cookies for image route - $app['config']->set('session.driver', 'array'); - - // find file - foreach ($config->get('imagecache::paths') as $path) { - // don't allow '..' in filenames - $image_path = $path.'/'.str_replace('..', '', $filename); - if (file_exists($image_path) && is_file($image_path)) { - break; - } else { - $image_path = false; - } - } - - // abort if file not found - if ($image_path === false) { - $app->abort(404); - } - - // define template callback - $callback = $config->get("imagecache::templates.{$template}"); - - if (is_callable($callback) || class_exists($callback)) { - - // image manipulation based on callback - $content = $app['image']->cache(function ($image) use ($image_path, $callback) { - - switch (true) { - case is_callable($callback): - return $callback($image->make($image_path)); - break; - - case class_exists($callback): - return $image->make($image_path)->filter(new $callback); - break; - } - - }, $config->get('imagecache::lifetime')); - - } else { - - // get original image file contents - $content = file_get_contents($image_path); - } - - // define mime type - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $content); - - // return http response - return new IlluminateResponse($content, 200, [ - 'Content-Type' => $mime, - 'Cache-Control' => 'max-age='.($config->get('imagecache::lifetime')*60).', public', - 'Etag' => md5($content) - ]); - - }])->where(['template' => join('|', array_keys($config->get('imagecache::templates'))), 'filename' => '[ \w\\.\\/\\-]+']); - } - } - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $app = $this->app; - - $app['image'] = $app->share(function ($app) { - return new ImageManager($app['config']->get('image::config')); - }); - - $app->alias('image', 'Intervention\Image\ImageManager'); - } -} diff --git a/src/Intervention/Image/ImageServiceProviderLaravelRecent.php b/src/Intervention/Image/ImageServiceProviderLaravelRecent.php deleted file mode 100644 index 17a84d0ac..000000000 --- a/src/Intervention/Image/ImageServiceProviderLaravelRecent.php +++ /dev/null @@ -1,106 +0,0 @@ -publishes([ - __DIR__.'/../../config/config.php' => config_path('image.php') - ]); - - // setup intervention/imagecache if package is installed - $this->cacheIsInstalled() ? $this->bootstrapImageCache() : null; - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $app = $this->app; - - // merge default config - $this->mergeConfigFrom( - __DIR__.'/../../config/config.php', - 'image' - ); - - // create image - $app->singleton('image', function ($app) { - return new ImageManager($this->getImageConfig($app)); - }); - - $app->alias('image', 'Intervention\Image\ImageManager'); - } - - /** - * Bootstrap imagecache - * - * @return void - */ - protected function bootstrapImageCache() - { - $app = $this->app; - $config = __DIR__.'/../../../../imagecache/src/config/config.php'; - - $this->publishes([ - $config => config_path('imagecache.php') - ]); - - // merge default config - $this->mergeConfigFrom( - $config, - 'imagecache' - ); - - // imagecache route - if (is_string(config('imagecache.route'))) { - - $filename_pattern = '[ \w\\.\\/\\-\\@\(\)\=]+'; - - // route to access template applied image file - $app['router']->get(config('imagecache.route').'/{template}/{filename}', [ - 'uses' => 'Intervention\Image\ImageCacheController@getResponse', - 'as' => 'imagecache' - ])->where(['filename' => $filename_pattern]); - } - } - - /** - * Return image configuration as array - * - * @param Application $app - * @return array - */ - private function getImageConfig($app) - { - $config = $app['config']->get('image'); - - if (is_null($config)) { - return []; - } - - return $config; - } -} diff --git a/src/Intervention/Image/ImageServiceProviderLeague.php b/src/Intervention/Image/ImageServiceProviderLeague.php deleted file mode 100644 index b756a61f5..000000000 --- a/src/Intervention/Image/ImageServiceProviderLeague.php +++ /dev/null @@ -1,42 +0,0 @@ -config = $config; - } - - /** - * Register the server provider. - * - * @return void - */ - public function register() - { - $this->getContainer()->share('Intervention\Image\ImageManager', function () { - return new ImageManager($this->config); - }); - } -} diff --git a/src/Intervention/Image/ImageServiceProviderLumen.php b/src/Intervention/Image/ImageServiceProviderLumen.php deleted file mode 100644 index 4a381ccd4..000000000 --- a/src/Intervention/Image/ImageServiceProviderLumen.php +++ /dev/null @@ -1,34 +0,0 @@ -app; - - // merge default config - $this->mergeConfigFrom( - __DIR__.'/../../config/config.php', - 'image' - ); - - // set configuration - $app->configure('image'); - - // create image - $app->singleton('image',function ($app) { - return new ImageManager($app['config']->get('image')); - }); - - $app->alias('image', 'Intervention\Image\ImageManager'); - } -} diff --git a/src/Intervention/Image/Imagick/Color.php b/src/Intervention/Image/Imagick/Color.php deleted file mode 100644 index a41ff3a6e..000000000 --- a/src/Intervention/Image/Imagick/Color.php +++ /dev/null @@ -1,279 +0,0 @@ -> 24) & 0xFF; - $r = ($value >> 16) & 0xFF; - $g = ($value >> 8) & 0xFF; - $b = $value & 0xFF; - $a = $this->rgb2alpha($a); - - $this->setPixel($r, $g, $b, $a); - } - - /** - * Initiates color object from given array - * - * @param array $value - * @return \Intervention\Image\AbstractColor - */ - public function initFromArray($array) - { - $array = array_values($array); - - if (count($array) == 4) { - - // color array with alpha value - list($r, $g, $b, $a) = $array; - - } elseif (count($array) == 3) { - - // color array without alpha value - list($r, $g, $b) = $array; - $a = 1; - } - - $this->setPixel($r, $g, $b, $a); - } - - /** - * Initiates color object from given string - * - * @param string $value - * - * @return \Intervention\Image\AbstractColor - */ - public function initFromString($value) - { - if ($color = $this->rgbaFromString($value)) { - $this->setPixel($color[0], $color[1], $color[2], $color[3]); - } - } - - /** - * Initiates color object from given ImagickPixel object - * - * @param ImagickPixel $value - * - * @return \Intervention\Image\AbstractColor - */ - public function initFromObject($value) - { - if (is_a($value, '\ImagickPixel')) { - $this->pixel = $value; - } - } - - /** - * Initiates color object from given R, G and B values - * - * @param int $r - * @param int $g - * @param int $b - * - * @return \Intervention\Image\AbstractColor - */ - public function initFromRgb($r, $g, $b) - { - $this->setPixel($r, $g, $b); - } - - /** - * Initiates color object from given R, G, B and A values - * - * @param int $r - * @param int $g - * @param int $b - * @param float $a - * - * @return \Intervention\Image\AbstractColor - */ - public function initFromRgba($r, $g, $b, $a) - { - $this->setPixel($r, $g, $b, $a); - } - - /** - * Calculates integer value of current color instance - * - * @return int - */ - public function getInt() - { - $r = $this->getRedValue(); - $g = $this->getGreenValue(); - $b = $this->getBlueValue(); - $a = intval(round($this->getAlphaValue() * 255)); - - return intval(($a << 24) + ($r << 16) + ($g << 8) + $b); - } - - /** - * Calculates hexadecimal value of current color instance - * - * @param string $prefix - * - * @return string - */ - public function getHex($prefix = '') - { - return sprintf('%s%02x%02x%02x', $prefix, - $this->getRedValue(), - $this->getGreenValue(), - $this->getBlueValue() - ); - } - - /** - * Calculates RGB(A) in array format of current color instance - * - * @return array - */ - public function getArray() - { - return [ - $this->getRedValue(), - $this->getGreenValue(), - $this->getBlueValue(), - $this->getAlphaValue() - ]; - } - - /** - * Calculates RGBA in string format of current color instance - * - * @return string - */ - public function getRgba() - { - return sprintf('rgba(%d, %d, %d, %.2F)', - $this->getRedValue(), - $this->getGreenValue(), - $this->getBlueValue(), - $this->getAlphaValue() - ); - } - - /** - * Determines if current color is different from given color - * - * @param AbstractColor $color - * @param int $tolerance - * @return boolean - */ - public function differs(AbstractColor $color, $tolerance = 0) - { - $color_tolerance = round($tolerance * 2.55); - $alpha_tolerance = round($tolerance); - - $delta = [ - 'r' => abs($color->getRedValue() - $this->getRedValue()), - 'g' => abs($color->getGreenValue() - $this->getGreenValue()), - 'b' => abs($color->getBlueValue() - $this->getBlueValue()), - 'a' => abs($color->getAlphaValue() - $this->getAlphaValue()) - ]; - - return ( - $delta['r'] > $color_tolerance or - $delta['g'] > $color_tolerance or - $delta['b'] > $color_tolerance or - $delta['a'] > $alpha_tolerance - ); - } - - /** - * Returns RGB red value of current color - * - * @return int - */ - public function getRedValue() - { - return intval(round($this->pixel->getColorValue(\Imagick::COLOR_RED) * 255)); - } - - /** - * Returns RGB green value of current color - * - * @return int - */ - public function getGreenValue() - { - return intval(round($this->pixel->getColorValue(\Imagick::COLOR_GREEN) * 255)); - } - - /** - * Returns RGB blue value of current color - * - * @return int - */ - public function getBlueValue() - { - return intval(round($this->pixel->getColorValue(\Imagick::COLOR_BLUE) * 255)); - } - - /** - * Returns RGB alpha value of current color - * - * @return float - */ - public function getAlphaValue() - { - return round($this->pixel->getColorValue(\Imagick::COLOR_ALPHA), 2); - } - - /** - * Initiates ImagickPixel from given RGBA values - * - * @return \ImagickPixel - */ - private function setPixel($r, $g, $b, $a = null) - { - $a = is_null($a) ? 1 : $a; - - return $this->pixel = new \ImagickPixel( - sprintf('rgba(%d, %d, %d, %.2F)', $r, $g, $b, $a) - ); - } - - /** - * Returns current color as ImagickPixel - * - * @return \ImagickPixel - */ - public function getPixel() - { - return $this->pixel; - } - - /** - * Calculates RGA integer alpha value into float value - * - * @param int $value - * @return float - */ - private function rgb2alpha($value) - { - // (255 -> 1.0) / (0 -> 0.0) - return (float) round($value/255, 2); - } - -} diff --git a/src/Intervention/Image/Imagick/Commands/BackupCommand.php b/src/Intervention/Image/Imagick/Commands/BackupCommand.php deleted file mode 100644 index 76b4f72bb..000000000 --- a/src/Intervention/Image/Imagick/Commands/BackupCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->value(); - - // clone current image resource - $clone = clone $image; - $image->setBackup($clone->getCore(), $backupName); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/BlurCommand.php b/src/Intervention/Image/Imagick/Commands/BlurCommand.php deleted file mode 100644 index d2533e0ea..000000000 --- a/src/Intervention/Image/Imagick/Commands/BlurCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(0, 100)->value(1); - - return $image->getCore()->blurImage(1 * $amount, 0.5 * $amount); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php b/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php deleted file mode 100644 index 03ac8478d..000000000 --- a/src/Intervention/Image/Imagick/Commands/BrightnessCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - - return $image->getCore()->modulateImage(100 + $level, 100, 100); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php b/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php deleted file mode 100644 index 3a6506f6d..000000000 --- a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php +++ /dev/null @@ -1,44 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - $green = $this->argument(1)->between(-100, 100)->required()->value(); - $blue = $this->argument(2)->between(-100, 100)->required()->value(); - - // normalize colorize levels - $red = $this->normalizeLevel($red); - $green = $this->normalizeLevel($green); - $blue = $this->normalizeLevel($blue); - - $qrange = $image->getCore()->getQuantumRange(); - - // apply - $image->getCore()->levelImage(0, $red, $qrange['quantumRangeLong'], \Imagick::CHANNEL_RED); - $image->getCore()->levelImage(0, $green, $qrange['quantumRangeLong'], \Imagick::CHANNEL_GREEN); - $image->getCore()->levelImage(0, $blue, $qrange['quantumRangeLong'], \Imagick::CHANNEL_BLUE); - - return true; - } - - private function normalizeLevel($level) - { - if ($level > 0) { - return $level/5; - } else { - return ($level+100)/100; - } - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php b/src/Intervention/Image/Imagick/Commands/ContrastCommand.php deleted file mode 100644 index c4847c61d..000000000 --- a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(-100, 100)->required()->value(); - - return $image->getCore()->sigmoidalContrastImage($level > 0, $level / 4, 0); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/CropCommand.php b/src/Intervention/Image/Imagick/Commands/CropCommand.php deleted file mode 100644 index 618edea7d..000000000 --- a/src/Intervention/Image/Imagick/Commands/CropCommand.php +++ /dev/null @@ -1,45 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->required()->value(); - $x = $this->argument(2)->type('digit')->value(); - $y = $this->argument(3)->type('digit')->value(); - - if (is_null($width) || is_null($height)) { - throw new InvalidArgumentException( - "Width and height of cutout needs to be defined." - ); - } - - $cropped = new Size($width, $height); - $position = new Point($x, $y); - - // align boxes - if (is_null($x) && is_null($y)) { - $position = $image->getSize()->align('center')->relativePosition($cropped->align('center')); - } - - // crop image core - $image->getCore()->cropImage($cropped->width, $cropped->height, $position->x, $position->y); - $image->getCore()->setImagePage(0,0,0,0); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/DestroyCommand.php b/src/Intervention/Image/Imagick/Commands/DestroyCommand.php deleted file mode 100644 index 58c9556e4..000000000 --- a/src/Intervention/Image/Imagick/Commands/DestroyCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -getCore()->clear(); - - // destroy backups - foreach ($image->getBackups() as $backup) { - $backup->clear(); - } - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ExifCommand.php b/src/Intervention/Image/Imagick/Commands/ExifCommand.php deleted file mode 100644 index 521b38b09..000000000 --- a/src/Intervention/Image/Imagick/Commands/ExifCommand.php +++ /dev/null @@ -1,63 +0,0 @@ -preferExtension = false; - } - - /** - * Read Exif data from the given image - * - * @param \Intervention\Image\Image $image - * @return boolean - */ - public function execute($image) - { - if ($this->preferExtension && function_exists('exif_read_data')) { - return parent::execute($image); - } - - $core = $image->getCore(); - - if ( ! method_exists($core, 'getImageProperties')) { - throw new NotSupportedException( - "Reading Exif data is not supported by this PHP installation." - ); - } - - $requestedKey = $this->argument(0)->value(); - if ($requestedKey !== null) { - $this->setOutput($core->getImageProperty('exif:' . $requestedKey)); - return true; - } - - $exif = []; - $properties = $core->getImageProperties(); - foreach ($properties as $key => $value) { - if (substr($key, 0, 5) !== 'exif:') { - continue; - } - - $exif[substr($key, 5)] = $value; - } - - $this->setOutput($exif); - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/FillCommand.php b/src/Intervention/Image/Imagick/Commands/FillCommand.php deleted file mode 100644 index 82baac532..000000000 --- a/src/Intervention/Image/Imagick/Commands/FillCommand.php +++ /dev/null @@ -1,105 +0,0 @@ -argument(0)->value(); - $x = $this->argument(1)->type('digit')->value(); - $y = $this->argument(2)->type('digit')->value(); - - $imagick = $image->getCore(); - - try { - // set image filling - $source = new Decoder; - $filling = $source->init($filling); - - } catch (NotReadableException $e) { - - // set solid color filling - $filling = new Color($filling); - } - - // flood fill if coordinates are set - if (is_int($x) && is_int($y)) { - - // flood fill with texture - if ($filling instanceof Image) { - - // create tile - $tile = clone $image->getCore(); - - // mask away color at position - $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); - - // create canvas - $canvas = clone $image->getCore(); - - // fill canvas with texture - $canvas = $canvas->textureImage($filling->getCore()); - - // merge canvas and tile - $canvas->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); - - // replace image core - $image->setCore($canvas); - - // flood fill with color - } elseif ($filling instanceof Color) { - - // create canvas with filling - $canvas = new \Imagick; - $canvas->newImage($image->getWidth(), $image->getHeight(), $filling->getPixel(), 'png'); - - // create tile to put on top - $tile = clone $image->getCore(); - - // mask away color at pos. - $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); - - // save alpha channel of original image - $alpha = clone $image->getCore(); - - // merge original with canvas and tile - $image->getCore()->compositeImage($canvas, \Imagick::COMPOSITE_DEFAULT, 0, 0); - $image->getCore()->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); - - // restore alpha channel of original image - $image->getCore()->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - } - - } else { - - if ($filling instanceof Image) { - - // fill whole image with texture - $image->setCore($image->getCore()->textureImage($filling->getCore())); - - } elseif ($filling instanceof Color) { - - // fill whole image with color - $draw = new \ImagickDraw(); - $draw->setFillColor($filling->getPixel()); - $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); - $image->getCore()->drawImage($draw); - } - } - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/FitCommand.php b/src/Intervention/Image/Imagick/Commands/FitCommand.php deleted file mode 100644 index 6d62ba677..000000000 --- a/src/Intervention/Image/Imagick/Commands/FitCommand.php +++ /dev/null @@ -1,42 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->value($width); - $constraints = $this->argument(2)->type('closure')->value(); - $position = $this->argument(3)->type('string')->value('center'); - - // calculate size - $cropped = $image->getSize()->fit(new Size($width, $height), $position); - $resized = clone $cropped; - $resized = $resized->resize($width, $height, $constraints); - - // crop image - $image->getCore()->cropImage( - $cropped->width, - $cropped->height, - $cropped->pivot->x, - $cropped->pivot->y - ); - - // resize image - $image->getCore()->scaleImage($resized->getWidth(), $resized->getHeight()); - $image->getCore()->setImagePage(0,0,0,0); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/FlipCommand.php b/src/Intervention/Image/Imagick/Commands/FlipCommand.php deleted file mode 100644 index abae16ad1..000000000 --- a/src/Intervention/Image/Imagick/Commands/FlipCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -argument(0)->value('h'); - - if (in_array(strtolower($mode), [2, 'v', 'vert', 'vertical'])) { - // flip vertical - return $image->getCore()->flipImage(); - } else { - // flip horizontal - return $image->getCore()->flopImage(); - } - } -} diff --git a/src/Intervention/Image/Imagick/Commands/GammaCommand.php b/src/Intervention/Image/Imagick/Commands/GammaCommand.php deleted file mode 100644 index 200515f3b..000000000 --- a/src/Intervention/Image/Imagick/Commands/GammaCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - - return $image->getCore()->gammaImage($gamma); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php b/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php deleted file mode 100644 index ccccedb03..000000000 --- a/src/Intervention/Image/Imagick/Commands/GetSizeCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -getCore(); - - $this->setOutput(new Size( - $core->getImageWidth(), - $core->getImageHeight() - )); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php b/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php deleted file mode 100644 index df0ff5b57..000000000 --- a/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -getCore()->modulateImage(100, 0, 100); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/HeightenCommand.php b/src/Intervention/Image/Imagick/Commands/HeightenCommand.php deleted file mode 100644 index 0b61e50c1..000000000 --- a/src/Intervention/Image/Imagick/Commands/HeightenCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $additionalConstraints = $this->argument(1)->type('closure')->value(); - - $this->arguments[0] = null; - $this->arguments[1] = $height; - $this->arguments[2] = function ($constraint) use ($additionalConstraints) { - $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) - $additionalConstraints($constraint); - }; - - return parent::execute($image); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/InsertCommand.php b/src/Intervention/Image/Imagick/Commands/InsertCommand.php deleted file mode 100644 index 2a9974367..000000000 --- a/src/Intervention/Image/Imagick/Commands/InsertCommand.php +++ /dev/null @@ -1,33 +0,0 @@ -argument(0)->required()->value(); - $position = $this->argument(1)->type('string')->value(); - $x = $this->argument(2)->type('digit')->value(0); - $y = $this->argument(3)->type('digit')->value(0); - - // build watermark - $watermark = $image->getDriver()->init($source); - - // define insertion point - $image_size = $image->getSize()->align($position, $x, $y); - $watermark_size = $watermark->getSize()->align($position); - $target = $image_size->relativePosition($watermark_size); - - // insert image at position - return $image->getCore()->compositeImage($watermark->getCore(), \Imagick::COMPOSITE_DEFAULT, $target->x, $target->y); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php b/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php deleted file mode 100644 index 913cab7e0..000000000 --- a/src/Intervention/Image/Imagick/Commands/InterlaceCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -argument(0)->type('bool')->value(true); - - if ($mode) { - $mode = \Imagick::INTERLACE_LINE; - } else { - $mode = \Imagick::INTERLACE_NO; - } - - $image->getCore()->setInterlaceScheme($mode); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/InvertCommand.php b/src/Intervention/Image/Imagick/Commands/InvertCommand.php deleted file mode 100644 index 1d134301b..000000000 --- a/src/Intervention/Image/Imagick/Commands/InvertCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -getCore()->negateImage(false); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php b/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php deleted file mode 100644 index 16f9b82e9..000000000 --- a/src/Intervention/Image/Imagick/Commands/LimitColorsCommand.php +++ /dev/null @@ -1,59 +0,0 @@ -argument(0)->value(); - $matte = $this->argument(1)->value(); - - // get current image size - $size = $image->getSize(); - - // build 2 color alpha mask from original alpha - $alpha = clone $image->getCore(); - $alpha->separateImageChannel(\Imagick::CHANNEL_ALPHA); - $alpha->transparentPaintImage('#ffffff', 0, 0, false); - $alpha->separateImageChannel(\Imagick::CHANNEL_ALPHA); - $alpha->negateImage(false); - - if ($matte) { - - // get matte color - $mattecolor = $image->getDriver()->parseColor($matte)->getPixel(); - - // create matte image - $canvas = new \Imagick; - $canvas->newImage($size->width, $size->height, $mattecolor, 'png'); - - // lower colors of original and copy to matte - $image->getCore()->quantizeImage($count, \Imagick::COLORSPACE_RGB, 0, false, false); - $canvas->compositeImage($image->getCore(), \Imagick::COMPOSITE_DEFAULT, 0, 0); - - // copy new alpha to canvas - $canvas->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - - // replace core - $image->setCore($canvas); - - } else { - - $image->getCore()->quantizeImage($count, \Imagick::COLORSPACE_RGB, 0, false, false); - $image->getCore()->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - - } - - return true; - - } -} diff --git a/src/Intervention/Image/Imagick/Commands/MaskCommand.php b/src/Intervention/Image/Imagick/Commands/MaskCommand.php deleted file mode 100644 index af9d6b2f5..000000000 --- a/src/Intervention/Image/Imagick/Commands/MaskCommand.php +++ /dev/null @@ -1,60 +0,0 @@ -argument(0)->value(); - $mask_w_alpha = $this->argument(1)->type('bool')->value(false); - - // get imagick - $imagick = $image->getCore(); - - // build mask image from source - $mask = $image->getDriver()->init($mask_source); - - // resize mask to size of current image (if necessary) - $image_size = $image->getSize(); - if ($mask->getSize() != $image_size) { - $mask->resize($image_size->width, $image_size->height); - } - - $imagick->setImageMatte(true); - - if ($mask_w_alpha) { - - // just mask with alpha map - $imagick->compositeImage($mask->getCore(), \Imagick::COMPOSITE_DSTIN, 0, 0); - - } else { - - // get alpha channel of original as greyscale image - $original_alpha = clone $imagick; - $original_alpha->separateImageChannel(\Imagick::CHANNEL_ALPHA); - - // use red channel from mask ask alpha - $mask_alpha = clone $mask->getCore(); - $mask_alpha->compositeImage($mask->getCore(), \Imagick::COMPOSITE_DEFAULT, 0, 0); - // $mask_alpha->setImageAlphaChannel(\Imagick::ALPHACHANNEL_DEACTIVATE); - $mask_alpha->separateImageChannel(\Imagick::CHANNEL_ALL); - - // combine both alphas from original and mask - $original_alpha->compositeImage($mask_alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - - // mask the image with the alpha combination - $imagick->compositeImage($original_alpha, \Imagick::COMPOSITE_DSTIN, 0, 0); - } - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/OpacityCommand.php b/src/Intervention/Image/Imagick/Commands/OpacityCommand.php deleted file mode 100644 index b4708d899..000000000 --- a/src/Intervention/Image/Imagick/Commands/OpacityCommand.php +++ /dev/null @@ -1,23 +0,0 @@ -argument(0)->between(0, 100)->required()->value(); - - $transparency = $transparency > 0 ? (100 / $transparency) : 1000; - - return $image->getCore()->evaluateImage(\Imagick::EVALUATE_DIVIDE, $transparency, \Imagick::CHANNEL_ALPHA); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/PickColorCommand.php b/src/Intervention/Image/Imagick/Commands/PickColorCommand.php deleted file mode 100644 index 978a1284e..000000000 --- a/src/Intervention/Image/Imagick/Commands/PickColorCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $y = $this->argument(1)->type('digit')->required()->value(); - $format = $this->argument(2)->type('string')->value('array'); - - // pick color - $color = new Color($image->getCore()->getImagePixelColor($x, $y)); - - // format to output - $this->setOutput($color->format($format)); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/PixelCommand.php b/src/Intervention/Image/Imagick/Commands/PixelCommand.php deleted file mode 100644 index 6eb6ae715..000000000 --- a/src/Intervention/Image/Imagick/Commands/PixelCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -argument(0)->required()->value(); - $color = new Color($color); - $x = $this->argument(1)->type('digit')->required()->value(); - $y = $this->argument(2)->type('digit')->required()->value(); - - // prepare pixel - $draw = new \ImagickDraw; - $draw->setFillColor($color->getPixel()); - $draw->point($x, $y); - - // apply pixel - return $image->getCore()->drawImage($draw); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php b/src/Intervention/Image/Imagick/Commands/PixelateCommand.php deleted file mode 100644 index 6fe794913..000000000 --- a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -argument(0)->type('digit')->value(10); - - $width = $image->getWidth(); - $height = $image->getHeight(); - - $image->getCore()->scaleImage(max(1, ($width / $size)), max(1, ($height / $size))); - $image->getCore()->scaleImage($width, $height); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ResetCommand.php b/src/Intervention/Image/Imagick/Commands/ResetCommand.php deleted file mode 100644 index 77b7f3366..000000000 --- a/src/Intervention/Image/Imagick/Commands/ResetCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -argument(0)->value(); - - $backup = $image->getBackup($backupName); - - if ($backup instanceof \Imagick) { - - // destroy current core - $image->getCore()->clear(); - - // clone backup - $backup = clone $backup; - - // reset to new resource - $image->setCore($backup); - - return true; - } - - throw new RuntimeException( - "Backup not available. Call backup({$backupName}) before reset()." - ); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php deleted file mode 100644 index ecf076106..000000000 --- a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php +++ /dev/null @@ -1,91 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $height = $this->argument(1)->type('digit')->required()->value(); - $anchor = $this->argument(2)->value('center'); - $relative = $this->argument(3)->type('boolean')->value(false); - $bgcolor = $this->argument(4)->value(); - - $original_width = $image->getWidth(); - $original_height = $image->getHeight(); - - // check of only width or height is set - $width = is_null($width) ? $original_width : intval($width); - $height = is_null($height) ? $original_height : intval($height); - - // check on relative width/height - if ($relative) { - $width = $original_width + $width; - $height = $original_height + $height; - } - - // check for negative width/height - $width = ($width <= 0) ? $width + $original_width : $width; - $height = ($height <= 0) ? $height + $original_height : $height; - - // create new canvas - $canvas = $image->getDriver()->newImage($width, $height, $bgcolor); - - // set copy position - $canvas_size = $canvas->getSize()->align($anchor); - $image_size = $image->getSize()->align($anchor); - $canvas_pos = $image_size->relativePosition($canvas_size); - $image_pos = $canvas_size->relativePosition($image_size); - - if ($width <= $original_width) { - $dst_x = 0; - $src_x = $canvas_pos->x; - $src_w = $canvas_size->width; - } else { - $dst_x = $image_pos->x; - $src_x = 0; - $src_w = $original_width; - } - - if ($height <= $original_height) { - $dst_y = 0; - $src_y = $canvas_pos->y; - $src_h = $canvas_size->height; - } else { - $dst_y = $image_pos->y; - $src_y = 0; - $src_h = $original_height; - } - - // make image area transparent to keep transparency - // even if background-color is set - $rect = new \ImagickDraw; - $fill = $canvas->pickColor(0, 0, 'hex'); - $fill = $fill == '#ff0000' ? '#00ff00' : '#ff0000'; - $rect->setFillColor($fill); - $rect->rectangle($dst_x, $dst_y, $dst_x + $src_w - 1, $dst_y + $src_h - 1); - $canvas->getCore()->drawImage($rect); - $canvas->getCore()->transparentPaintImage($fill, 0, 0, false); - - $canvas->getCore()->setImageColorspace($image->getCore()->getImageColorspace()); - - // copy image into new canvas - $image->getCore()->cropImage($src_w, $src_h, $src_x, $src_y); - $canvas->getCore()->compositeImage($image->getCore(), \Imagick::COMPOSITE_DEFAULT, $dst_x, $dst_y); - $canvas->getCore()->setImagePage(0,0,0,0); - - // set new core to canvas - $image->setCore($canvas->getCore()); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCommand.php deleted file mode 100644 index 3d4dc5bed..000000000 --- a/src/Intervention/Image/Imagick/Commands/ResizeCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -argument(0)->value(); - $height = $this->argument(1)->value(); - $constraints = $this->argument(2)->type('closure')->value(); - - // resize box - $resized = $image->getSize()->resize($width, $height, $constraints); - - // modify image - $image->getCore()->scaleImage($resized->getWidth(), $resized->getHeight()); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/RotateCommand.php b/src/Intervention/Image/Imagick/Commands/RotateCommand.php deleted file mode 100644 index b3e12a535..000000000 --- a/src/Intervention/Image/Imagick/Commands/RotateCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -argument(0)->type('numeric')->required()->value(); - $color = $this->argument(1)->value(); - $color = new Color($color); - - // restrict rotations beyond 360 degrees, since the end result is the same - $angle = fmod($angle, 360); - - // rotate image - $image->getCore()->rotateImage($color->getPixel(), ($angle * -1)); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/SharpenCommand.php b/src/Intervention/Image/Imagick/Commands/SharpenCommand.php deleted file mode 100644 index bc5e393f2..000000000 --- a/src/Intervention/Image/Imagick/Commands/SharpenCommand.php +++ /dev/null @@ -1,21 +0,0 @@ -argument(0)->between(0, 100)->value(10); - - return $image->getCore()->unsharpMaskImage(1, 1, $amount / 6.25, 0); - } -} diff --git a/src/Intervention/Image/Imagick/Commands/TrimCommand.php b/src/Intervention/Image/Imagick/Commands/TrimCommand.php deleted file mode 100644 index bdc897be7..000000000 --- a/src/Intervention/Image/Imagick/Commands/TrimCommand.php +++ /dev/null @@ -1,121 +0,0 @@ -argument(0)->type('string')->value(); - $away = $this->argument(1)->value(); - $tolerance = $this->argument(2)->type('numeric')->value(0); - $feather = $this->argument(3)->type('numeric')->value(0); - - $width = $image->getWidth(); - $height = $image->getHeight(); - - $checkTransparency = false; - - // define borders to trim away - if (is_null($away)) { - $away = ['top', 'right', 'bottom', 'left']; - } elseif (is_string($away)) { - $away = [$away]; - } - - // lower border names - foreach ($away as $key => $value) { - $away[$key] = strtolower($value); - } - - // define base color position - switch (strtolower($base)) { - case 'transparent': - case 'trans': - $checkTransparency = true; - $base_x = 0; - $base_y = 0; - break; - - case 'bottom-right': - case 'right-bottom': - $base_x = $width - 1; - $base_y = $height - 1; - break; - - default: - case 'top-left': - case 'left-top': - $base_x = 0; - $base_y = 0; - break; - } - - // pick base color - if ($checkTransparency) { - $base_color = new Color; // color will only be used to compare alpha channel - } else { - $base_color = $image->pickColor($base_x, $base_y, 'object'); - } - - // trim on clone to get only coordinates - $trimed = clone $image->getCore(); - - // add border to trim specific color - $trimed->borderImage($base_color->getPixel(), 1, 1); - - // trim image - $trimed->trimImage(65850 / 100 * $tolerance); - - // get coordinates of trim - $imagePage = $trimed->getImagePage(); - list($crop_x, $crop_y) = [$imagePage['x']-1, $imagePage['y']-1]; - // $trimed->setImagePage(0, 0, 0, 0); - list($crop_width, $crop_height) = [$trimed->width, $trimed->height]; - - // adjust settings if right should not be trimed - if ( ! in_array('right', $away)) { - $crop_width = $crop_width + ($width - ($width - $crop_x)); - } - - // adjust settings if bottom should not be trimed - if ( ! in_array('bottom', $away)) { - $crop_height = $crop_height + ($height - ($height - $crop_y)); - } - - // adjust settings if left should not be trimed - if ( ! in_array('left', $away)) { - $crop_width = $crop_width + $crop_x; - $crop_x = 0; - } - - // adjust settings if top should not be trimed - if ( ! in_array('top', $away)) { - $crop_height = $crop_height + $crop_y; - $crop_y = 0; - } - - // add feather - $crop_width = min($width, ($crop_width + $feather * 2)); - $crop_height = min($height, ($crop_height + $feather * 2)); - $crop_x = max(0, ($crop_x - $feather)); - $crop_y = max(0, ($crop_y - $feather)); - - // finally crop based on page - $image->getCore()->cropImage($crop_width, $crop_height, $crop_x, $crop_y); - $image->getCore()->setImagePage(0,0,0,0); - - $trimed->destroy(); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Commands/WidenCommand.php b/src/Intervention/Image/Imagick/Commands/WidenCommand.php deleted file mode 100644 index a1967534c..000000000 --- a/src/Intervention/Image/Imagick/Commands/WidenCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -argument(0)->type('digit')->required()->value(); - $additionalConstraints = $this->argument(1)->type('closure')->value(); - - $this->arguments[0] = $width; - $this->arguments[1] = null; - $this->arguments[2] = function ($constraint) use ($additionalConstraints) { - $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) - $additionalConstraints($constraint); - }; - - return parent::execute($image); - } -} diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php deleted file mode 100644 index f4dde9a84..000000000 --- a/src/Intervention/Image/Imagick/Decoder.php +++ /dev/null @@ -1,124 +0,0 @@ -setBackgroundColor(new \ImagickPixel('transparent')); - $core->readImage($path); - $core->setImageType(defined('\Imagick::IMGTYPE_TRUECOLORALPHA') ? \Imagick::IMGTYPE_TRUECOLORALPHA : \Imagick::IMGTYPE_TRUECOLORMATTE); - - } catch (\ImagickException $e) { - throw new \Intervention\Image\Exception\NotReadableException( - "Unable to read image from path ({$path}).", - 0, - $e - ); - } - - // build image - $image = $this->initFromImagick($core); - $image->setFileInfoFromPath($path); - - return $image; - } - - /** - * Initiates new image from GD resource - * - * @param Resource $resource - * @return \Intervention\Image\Image - */ - public function initFromGdResource($resource) - { - throw new NotSupportedException( - 'Imagick driver is unable to init from GD resource.' - ); - } - - /** - * Initiates new image from Imagick object - * - * @param Imagick $object - * @return \Intervention\Image\Image - */ - public function initFromImagick(\Imagick $object) - { - // currently animations are not supported - // so all images are turned into static - $object = $this->removeAnimation($object); - - // reset image orientation - $object->setImageOrientation(\Imagick::ORIENTATION_UNDEFINED); - - return new Image(new Driver, $object); - } - - /** - * Initiates new image from binary data - * - * @param string $data - * @return \Intervention\Image\Image - */ - public function initFromBinary($binary) - { - $core = new \Imagick; - - try { - $core->setBackgroundColor(new \ImagickPixel('transparent')); - - $core->readImageBlob($binary); - - } catch (\ImagickException $e) { - throw new NotReadableException( - "Unable to read image from binary data.", - 0, - $e - ); - } - - // build image - $image = $this->initFromImagick($core); - $image->mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $binary); - - return $image; - } - - /** - * Turns object into one frame Imagick object - * by removing all frames except first - * - * @param Imagick $object - * @return Imagick - */ - private function removeAnimation(\Imagick $object) - { - $imagick = new \Imagick; - - foreach ($object as $frame) { - $imagick->addImage($frame->getImage()); - break; - } - - $object->destroy(); - - return $imagick; - } -} diff --git a/src/Intervention/Image/Imagick/Driver.php b/src/Intervention/Image/Imagick/Driver.php deleted file mode 100644 index bb26ca0fe..000000000 --- a/src/Intervention/Image/Imagick/Driver.php +++ /dev/null @@ -1,74 +0,0 @@ -coreAvailable()) { - throw new NotSupportedException( - "ImageMagick module not available with this PHP installation." - ); - } - - $this->decoder = $decoder ? $decoder : new Decoder; - $this->encoder = $encoder ? $encoder : new Encoder; - } - - /** - * Creates new image instance - * - * @param int $width - * @param int $height - * @param mixed $background - * @return \Intervention\Image\Image - */ - public function newImage($width, $height, $background = null) - { - $background = new Color($background); - - // create empty core - $core = new \Imagick; - $core->newImage($width, $height, $background->getPixel(), 'png'); - $core->setType(\Imagick::IMGTYPE_UNDEFINED); - $core->setImageType(\Imagick::IMGTYPE_UNDEFINED); - $core->setColorspace(\Imagick::COLORSPACE_UNDEFINED); - - // build image - $image = new Image(new static, $core); - - return $image; - } - - /** - * Reads given string into color object - * - * @param string $value - * @return AbstractColor - */ - public function parseColor($value) - { - return new Color($value); - } - - /** - * Checks if core module installation is available - * - * @return boolean - */ - protected function coreAvailable() - { - return (extension_loaded('imagick') && class_exists('Imagick')); - } -} diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php deleted file mode 100644 index feb8a9d53..000000000 --- a/src/Intervention/Image/Imagick/Encoder.php +++ /dev/null @@ -1,239 +0,0 @@ -image->getCore(); - $imagick->setImageBackgroundColor('white'); - $imagick->setBackgroundColor('white'); - $imagick = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_MERGE); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setCompressionQuality($this->quality); - $imagick->setImageCompressionQuality($this->quality); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as PNG string - * - * @return string - */ - protected function processPng() - { - $format = 'png'; - $compression = \Imagick::COMPRESSION_ZIP; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_PNG); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as GIF string - * - * @return string - */ - protected function processGif() - { - $format = 'gif'; - $compression = \Imagick::COMPRESSION_LZW; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_GIF); - - return $imagick->getImagesBlob(); - } - - protected function processWebp() - { - if ( ! \Imagick::queryFormats('WEBP')) { - throw new NotSupportedException( - "Webp format is not supported by Imagick installation." - ); - } - - $format = 'webp'; - $compression = \Imagick::COMPRESSION_JPEG; - - $imagick = $this->image->getCore(); - $imagick->setImageBackgroundColor(new \ImagickPixel('transparent')); - - $imagick = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_MERGE); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setImageCompressionQuality($this->quality); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as TIFF string - * - * @return string - */ - protected function processTiff() - { - $format = 'tiff'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setCompressionQuality($this->quality); - $imagick->setImageCompressionQuality($this->quality); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_TIFF_II); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as BMP string - * - * @return string - */ - protected function processBmp() - { - $format = 'bmp'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_BMP); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as ICO string - * - * @return string - */ - protected function processIco() - { - $format = 'ico'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_ICO); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as PSD string - * - * @return string - */ - protected function processPsd() - { - $format = 'psd'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - - $this->image->mime = image_type_to_mime_type(IMAGETYPE_PSD); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as AVIF string - * - * @return string - */ - protected function processAvif() - { - if ( ! \Imagick::queryFormats('AVIF')) { - throw new NotSupportedException( - "AVIF format is not supported by Imagick installation." - ); - } - - $format = 'avif'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setCompressionQuality($this->quality); - $imagick->setImageCompressionQuality($this->quality); - - return $imagick->getImagesBlob(); - } - - /** - * Processes and returns encoded image as HEIC string - * - * @return string - */ - protected function processHeic() - { - if ( ! \Imagick::queryFormats('HEIC')) { - throw new NotSupportedException( - "HEIC format is not supported by Imagick installation." - ); - } - - $format = 'heic'; - $compression = \Imagick::COMPRESSION_UNDEFINED; - - $imagick = $this->image->getCore(); - $imagick->setFormat($format); - $imagick->setImageFormat($format); - $imagick->setCompression($compression); - $imagick->setImageCompression($compression); - $imagick->setCompressionQuality($this->quality); - $imagick->setImageCompressionQuality($this->quality); - - return $imagick->getImagesBlob(); - } -} diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php deleted file mode 100644 index 9869d067a..000000000 --- a/src/Intervention/Image/Imagick/Font.php +++ /dev/null @@ -1,122 +0,0 @@ -setStrokeAntialias(true); - $draw->setTextAntialias(true); - - // set font file - if ($this->hasApplicableFontFile()) { - $draw->setFont($this->file); - } else { - throw new RuntimeException( - "Font file must be provided to apply text to image." - ); - } - - // parse text color - $color = new Color($this->color); - - $draw->setFontSize($this->size); - $draw->setFillColor($color->getPixel()); - $draw->setTextKerning($this->kerning); - - // align horizontal - switch (strtolower($this->align)) { - case 'center': - $align = \Imagick::ALIGN_CENTER; - break; - - case 'right': - $align = \Imagick::ALIGN_RIGHT; - break; - - default: - $align = \Imagick::ALIGN_LEFT; - break; - } - - $draw->setTextAlignment($align); - - // align vertical - if (strtolower($this->valign) != 'bottom') { - - // corrections on y-position - switch (strtolower($this->valign)) { - case 'center': - case 'middle': - // calculate box size - $dimensions = $image->getCore()->queryFontMetrics($draw, $this->text); - $posy = $posy + $dimensions['textHeight'] * 0.65 / 2; - break; - - case 'top': - // calculate box size - $dimensions = $image->getCore()->queryFontMetrics($draw, $this->text, false); - $posy = $posy + $dimensions['characterHeight']; - break; - } - } - - // apply to image - $image->getCore()->annotateImage($draw, $posx, $posy, $this->angle * (-1), $this->text); - } - - /** - * Calculates bounding box of current font setting - * - * @return array - */ - public function getBoxSize() - { - $box = []; - - // build draw object - $draw = new \ImagickDraw(); - $draw->setStrokeAntialias(true); - $draw->setTextAntialias(true); - - // set font file - if ($this->hasApplicableFontFile()) { - $draw->setFont($this->file); - } else { - throw new RuntimeException( - "Font file must be provided to apply text to image." - ); - } - - $draw->setFontSize($this->size); - - $dimensions = (new \Imagick())->queryFontMetrics($draw, $this->text); - - if (strlen($this->text) == 0) { - // no text -> no boxsize - $box['width'] = 0; - $box['height'] = 0; - } else { - // get boxsize - $box['width'] = intval(abs($dimensions['textWidth'])); - $box['height'] = intval(abs($dimensions['textHeight'])); - } - - return $box; - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/CircleShape.php b/src/Intervention/Image/Imagick/Shapes/CircleShape.php deleted file mode 100644 index 24172ea11..000000000 --- a/src/Intervention/Image/Imagick/Shapes/CircleShape.php +++ /dev/null @@ -1,40 +0,0 @@ -width = is_numeric($diameter) ? intval($diameter) : $this->diameter; - $this->height = is_numeric($diameter) ? intval($diameter) : $this->diameter; - $this->diameter = is_numeric($diameter) ? intval($diameter) : $this->diameter; - } - - /** - * Draw current circle on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - return parent::applyToImage($image, $x, $y); - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php b/src/Intervention/Image/Imagick/Shapes/EllipseShape.php deleted file mode 100644 index 99694b97b..000000000 --- a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php +++ /dev/null @@ -1,66 +0,0 @@ -width = is_numeric($width) ? intval($width) : $this->width; - $this->height = is_numeric($height) ? intval($height) : $this->height; - } - - /** - * Draw ellipse instance on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $circle = new \ImagickDraw; - - // set background - $bgcolor = new Color($this->background); - $circle->setFillColor($bgcolor->getPixel()); - - // set border - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - $circle->setStrokeWidth($this->border_width); - $circle->setStrokeColor($border_color->getPixel()); - } - - $circle->ellipse($x, $y, $this->width / 2, $this->height / 2, 0, 360); - - $image->getCore()->drawImage($circle); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/LineShape.php b/src/Intervention/Image/Imagick/Shapes/LineShape.php deleted file mode 100644 index 0d6b3297d..000000000 --- a/src/Intervention/Image/Imagick/Shapes/LineShape.php +++ /dev/null @@ -1,94 +0,0 @@ -x = is_numeric($x) ? intval($x) : $this->x; - $this->y = is_numeric($y) ? intval($y) : $this->y; - } - - /** - * Set current line color - * - * @param string $color - * @return void - */ - public function color($color) - { - $this->color = $color; - } - - /** - * Set current line width in pixels - * - * @param int $width - * @return void - */ - public function width($width) - { - $this->width = $width; - } - - /** - * Draw current instance of line to given endpoint on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $line = new \ImagickDraw; - - $color = new Color($this->color); - $line->setStrokeColor($color->getPixel()); - $line->setStrokeWidth($this->width); - - $line->line($this->x, $this->y, $x, $y); - $image->getCore()->drawImage($line); - - return true; - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php deleted file mode 100644 index 45f44ad88..000000000 --- a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php +++ /dev/null @@ -1,81 +0,0 @@ -points = $this->formatPoints($points); - } - - /** - * Draw polygon on given image - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $polygon = new \ImagickDraw; - - // set background - $bgcolor = new Color($this->background); - $polygon->setFillColor($bgcolor->getPixel()); - - // set border - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - $polygon->setStrokeWidth($this->border_width); - $polygon->setStrokeColor($border_color->getPixel()); - } - - $polygon->polygon($this->points); - - $image->getCore()->drawImage($polygon); - - return true; - } - - /** - * Format polygon points to Imagick format - * - * @param Array $points - * @return Array - */ - private function formatPoints($points) - { - $ipoints = []; - $count = 1; - - foreach ($points as $key => $value) { - if ($count%2 === 0) { - $y = $value; - $ipoints[] = ['x' => $x, 'y' => $y]; - } else { - $x = $value; - } - $count++; - } - - return $ipoints; - } -} diff --git a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php b/src/Intervention/Image/Imagick/Shapes/RectangleShape.php deleted file mode 100644 index 1166d77b7..000000000 --- a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php +++ /dev/null @@ -1,84 +0,0 @@ -x1 = is_numeric($x1) ? intval($x1) : $this->x1; - $this->y1 = is_numeric($y1) ? intval($y1) : $this->y1; - $this->x2 = is_numeric($x2) ? intval($x2) : $this->x2; - $this->y2 = is_numeric($y2) ? intval($y2) : $this->y2; - } - - /** - * Draw rectangle to given image at certain position - * - * @param Image $image - * @param int $x - * @param int $y - * @return boolean - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - $rectangle = new \ImagickDraw; - - // set background - $bgcolor = new Color($this->background); - $rectangle->setFillColor($bgcolor->getPixel()); - - // set border - if ($this->hasBorder()) { - $border_color = new Color($this->border_color); - $rectangle->setStrokeWidth($this->border_width); - $rectangle->setStrokeColor($border_color->getPixel()); - } - - $rectangle->rectangle($this->x1, $this->y1, $this->x2, $this->y2); - - $image->getCore()->drawImage($rectangle); - - return true; - } -} diff --git a/src/Intervention/Image/Point.php b/src/Intervention/Image/Point.php deleted file mode 100644 index d51452e02..000000000 --- a/src/Intervention/Image/Point.php +++ /dev/null @@ -1,64 +0,0 @@ -x = is_numeric($x) ? intval($x) : 0; - $this->y = is_numeric($y) ? intval($y) : 0; - } - - /** - * Sets X coordinate - * - * @param int $x - */ - public function setX($x) - { - $this->x = intval($x); - } - - /** - * Sets Y coordinate - * - * @param int $y - */ - public function setY($y) - { - $this->y = intval($y); - } - - /** - * Sets both X and Y coordinate - * - * @param int $x - * @param int $y - */ - public function setPosition($x, $y) - { - $this->setX($x); - $this->setY($y); - } -} diff --git a/src/Intervention/Image/Response.php b/src/Intervention/Image/Response.php deleted file mode 100644 index 8e7940811..000000000 --- a/src/Intervention/Image/Response.php +++ /dev/null @@ -1,78 +0,0 @@ -image = $image; - $this->format = $format ? $format : $image->mime; - $this->quality = $quality ? $quality : 90; - } - - /** - * Builds response according to settings - * - * @return mixed - */ - public function make() - { - $this->image->encode($this->format, $this->quality); - $data = $this->image->getEncoded(); - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data); - $length = strlen($data); - - if (function_exists('app') && is_a($app = app(), 'Illuminate\Foundation\Application')) { - - $response = IlluminateResponse::make($data); - $response->header('Content-Type', $mime); - $response->header('Content-Length', $length); - - } elseif (class_exists('\Symfony\Component\HttpFoundation\Response')) { - - $response = SymfonyResponse::create($data); - $response->headers->set('Content-Type', $mime); - $response->headers->set('Content-Length', $length); - - } else { - - header('Content-Type: ' . $mime); - header('Content-Length: ' . $length); - $response = $data; - } - - return $response; - } -} diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php deleted file mode 100644 index f8b6dc8e7..000000000 --- a/src/Intervention/Image/Size.php +++ /dev/null @@ -1,374 +0,0 @@ -width = is_numeric($width) ? intval($width) : 1; - $this->height = is_numeric($height) ? intval($height) : 1; - $this->pivot = $pivot ? $pivot : new Point; - } - - /** - * Set the width and height absolutely - * - * @param int $width - * @param int $height - */ - public function set($width, $height) - { - $this->width = $width; - $this->height = $height; - } - - /** - * Set current pivot point - * - * @param Point $point - */ - public function setPivot(Point $point) - { - $this->pivot = $point; - } - - /** - * Get the current width - * - * @return int - */ - public function getWidth() - { - return $this->width; - } - - /** - * Get the current height - * - * @return int - */ - public function getHeight() - { - return $this->height; - } - - /** - * Calculate the current aspect ratio - * - * @return float - */ - public function getRatio() - { - return $this->width / $this->height; - } - - /** - * Resize to desired width and/or height - * - * @param int $width - * @param int $height - * @param Closure $callback - * @return Size - */ - public function resize($width, $height, Closure $callback = null) - { - if (is_null($width) && is_null($height)) { - throw new InvalidArgumentException( - "Width or height needs to be defined." - ); - } - - // new size with dominant width - $dominant_w_size = clone $this; - $dominant_w_size->resizeHeight($height, $callback); - $dominant_w_size->resizeWidth($width, $callback); - - // new size with dominant height - $dominant_h_size = clone $this; - $dominant_h_size->resizeWidth($width, $callback); - $dominant_h_size->resizeHeight($height, $callback); - - // decide which size to use - if ($dominant_h_size->fitsInto(new self($width, $height))) { - $this->set($dominant_h_size->width, $dominant_h_size->height); - } else { - $this->set($dominant_w_size->width, $dominant_w_size->height); - } - - return $this; - } - - /** - * Scale size according to given constraints - * - * @param int $width - * @param Closure $callback - * @return Size - */ - private function resizeWidth($width, Closure $callback = null) - { - $constraint = $this->getConstraint($callback); - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $max_width = $constraint->getSize()->getWidth(); - $max_height = $constraint->getSize()->getHeight(); - } - - if (is_numeric($width)) { - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $this->width = ($width > $max_width) ? $max_width : $width; - } else { - $this->width = $width; - } - - if ($constraint->isFixed(Constraint::ASPECTRATIO)) { - $h = max(1, intval(round($this->width / $constraint->getSize()->getRatio()))); - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $this->height = ($h > $max_height) ? $max_height : $h; - } else { - $this->height = $h; - } - } - } - } - - /** - * Scale size according to given constraints - * - * @param int $height - * @param Closure $callback - * @return Size - */ - private function resizeHeight($height, Closure $callback = null) - { - $constraint = $this->getConstraint($callback); - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $max_width = $constraint->getSize()->getWidth(); - $max_height = $constraint->getSize()->getHeight(); - } - - if (is_numeric($height)) { - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $this->height = ($height > $max_height) ? $max_height : $height; - } else { - $this->height = $height; - } - - if ($constraint->isFixed(Constraint::ASPECTRATIO)) { - $w = max(1, intval(round($this->height * $constraint->getSize()->getRatio()))); - - if ($constraint->isFixed(Constraint::UPSIZE)) { - $this->width = ($w > $max_width) ? $max_width : $w; - } else { - $this->width = $w; - } - } - } - } - - /** - * Calculate the relative position to another Size - * based on the pivot point settings of both sizes. - * - * @param Size $size - * @return \Intervention\Image\Point - */ - public function relativePosition(Size $size) - { - $x = $this->pivot->x - $size->pivot->x; - $y = $this->pivot->y - $size->pivot->y; - - return new Point($x, $y); - } - - /** - * Resize given Size to best fitting size of current size. - * - * @param Size $size - * @return \Intervention\Image\Size - */ - public function fit(Size $size, $position = 'center') - { - // create size with auto height - $auto_height = clone $size; - - $auto_height->resize($this->width, null, function ($constraint) { - $constraint->aspectRatio(); - }); - - // decide which version to use - if ($auto_height->fitsInto($this)) { - - $size = $auto_height; - - } else { - - // create size with auto width - $auto_width = clone $size; - - $auto_width->resize(null, $this->height, function ($constraint) { - $constraint->aspectRatio(); - }); - - $size = $auto_width; - } - - $this->align($position); - $size->align($position); - $size->setPivot($this->relativePosition($size)); - - return $size; - } - - /** - * Checks if given size fits into current size - * - * @param Size $size - * @return boolean - */ - public function fitsInto(Size $size) - { - return ($this->width <= $size->width) && ($this->height <= $size->height); - } - - /** - * Aligns current size's pivot point to given position - * and moves point automatically by offset. - * - * @param string $position - * @param int $offset_x - * @param int $offset_y - * @return \Intervention\Image\Size - */ - public function align($position, $offset_x = 0, $offset_y = 0) - { - switch (strtolower($position)) { - - case 'top': - case 'top-center': - case 'top-middle': - case 'center-top': - case 'middle-top': - $x = intval($this->width / 2); - $y = 0 + $offset_y; - break; - - case 'top-right': - case 'right-top': - $x = $this->width - $offset_x; - $y = 0 + $offset_y; - break; - - case 'left': - case 'left-center': - case 'left-middle': - case 'center-left': - case 'middle-left': - $x = 0 + $offset_x; - $y = intval($this->height / 2); - break; - - case 'right': - case 'right-center': - case 'right-middle': - case 'center-right': - case 'middle-right': - $x = $this->width - $offset_x; - $y = intval($this->height / 2); - break; - - case 'bottom-left': - case 'left-bottom': - $x = 0 + $offset_x; - $y = $this->height - $offset_y; - break; - - case 'bottom': - case 'bottom-center': - case 'bottom-middle': - case 'center-bottom': - case 'middle-bottom': - $x = intval($this->width / 2); - $y = $this->height - $offset_y; - break; - - case 'bottom-right': - case 'right-bottom': - $x = $this->width - $offset_x; - $y = $this->height - $offset_y; - break; - - case 'center': - case 'middle': - case 'center-center': - case 'middle-middle': - $x = intval($this->width / 2) + $offset_x; - $y = intval($this->height / 2) + $offset_y; - break; - - default: - case 'top-left': - case 'left-top': - $x = 0 + $offset_x; - $y = 0 + $offset_y; - break; - } - - $this->pivot->setPosition($x, $y); - - return $this; - } - - /** - * Runs constraints on current size - * - * @param Closure $callback - * @return \Intervention\Image\Constraint - */ - private function getConstraint(Closure $callback = null) - { - $constraint = new Constraint(clone $this); - - if (is_callable($callback)) { - $callback($constraint); - } - - return $constraint; - } -} diff --git a/src/Traits/CanDecodeDataUri.php b/src/Traits/CanDecodeDataUri.php new file mode 100644 index 000000000..350f4096d --- /dev/null +++ b/src/Traits/CanDecodeDataUri.php @@ -0,0 +1,75 @@ +\w+\/[-+.\w]+)?(?P(;[-\w]+=[-\w]+)*)(?P;base64)?,(?P.*)/"; + $result = preg_match($pattern, $value, $matches); + + return new class ($matches, $result) + { + private $matches; + private $result; + + public function __construct($matches, $result) + { + $this->matches = $matches; + $this->result = $result; + } + + public function isValid(): bool + { + return (bool) $this->result; + } + + public function mediaType(): ?string + { + if (isset($this->matches['mediatype']) && !empty($this->matches['mediatype'])) { + return $this->matches['mediatype']; + } + + return null; + } + + public function hasMediaType(): bool + { + return !empty($this->mediaType()); + } + + public function parameters(): array + { + if (isset($this->matches['parameters']) && !empty($this->matches['parameters'])) { + return explode(';', trim($this->matches['parameters'], ';')); + } + + return []; + } + + public function isBase64Encoded(): bool + { + if (isset($this->matches['base64']) && $this->matches['base64'] === ';base64') { + return true; + } + + return false; + } + + public function data(): ?string + { + if (isset($this->matches['data']) && !empty($this->matches['data'])) { + return $this->matches['data']; + } + + return null; + } + }; + } +} diff --git a/src/Traits/CanResolveDriverClass.php b/src/Traits/CanResolveDriverClass.php new file mode 100644 index 000000000..c7447e4f8 --- /dev/null +++ b/src/Traits/CanResolveDriverClass.php @@ -0,0 +1,48 @@ +getCurrentDriver()), + $classname + ); + + try { + $reflection = new ReflectionClass($classname); + } catch (ReflectionException $e) { + throw new RuntimeException( + 'Class (' . $classname . ') could not be resolved for current driver.' + ); + } + + return $reflection->newInstanceArgs($arguments); + } + + protected function getCurrentDriver() + { + $pattern = '/Intervention\\\Image\\\Drivers\\\(?P[A-Za-z]+)/'; + preg_match($pattern, get_class($this), $matches); + + if (! array_key_exists('driver', $matches)) { + throw new RuntimeException('Current driver could not be resolved.'); + } + + return strtolower($matches['driver']); + } +} diff --git a/src/Traits/CanValidateBase64.php b/src/Traits/CanValidateBase64.php new file mode 100644 index 000000000..7bfe1f21e --- /dev/null +++ b/src/Traits/CanValidateBase64.php @@ -0,0 +1,15 @@ + 4) { + return false; + } + + // validate rgb values + foreach ($input as $value) { + if ($value < 0 || $value > 255) { + return false; + } + } + + // validate alpha value + if ($input[3] > 1 || $input[3] < 0) { + return false; + } + + return true; + } +} diff --git a/src/config/config.php b/src/config/config.php deleted file mode 100644 index 2b1d2c3e1..000000000 --- a/src/config/config.php +++ /dev/null @@ -1,20 +0,0 @@ - 'gd' - -]; diff --git a/tests/AbstractColorTest.php b/tests/AbstractColorTest.php deleted file mode 100644 index 0baeaa3b0..000000000 --- a/tests/AbstractColorTest.php +++ /dev/null @@ -1,20 +0,0 @@ -getMockForAbstractClass('\Intervention\Image\AbstractColor'); - $color->format('xxxxxxxxxxxxxxxxxxxxxxx'); - } -} diff --git a/tests/AbstractCommandTest.php b/tests/AbstractCommandTest.php deleted file mode 100644 index a89bee49d..000000000 --- a/tests/AbstractCommandTest.php +++ /dev/null @@ -1,48 +0,0 @@ -getTestCommand(); - $this->assertEquals('foo', $command->argument(0)->value()); - $this->assertEquals('bar', $command->argument(1)->value()); - } - - public function testGetOutput() - { - $command = $this->getTestCommand(); - $command->setOutput('foo'); - $this->assertEquals('foo', $command->getOutput()); - } - - public function testHasOutput() - { - $command = $this->getTestCommand(); - $this->assertEquals(false, $command->hasOutput()); - $command->setOutput('foo'); - $this->assertEquals(true, $command->hasOutput()); - } - - public function testSetOutput() - { - $command = $this->getTestCommand(); - $command->setOutput('foo'); - $this->assertEquals(true, $command->hasOutput()); - } - - public function getTestCommand() - { - $arguments = ['foo', 'bar']; - $command = $this->getMockForAbstractClass('\Intervention\Image\Commands\AbstractCommand', [$arguments]); - - return $command; - } -} diff --git a/tests/AbstractDecoderTest.php b/tests/AbstractDecoderTest.php deleted file mode 100644 index a06fc8a10..000000000 --- a/tests/AbstractDecoderTest.php +++ /dev/null @@ -1,160 +0,0 @@ -getTestDecoder(new \Imagick); - $this->assertTrue($source->isImagick()); - - $source = $this->getTestDecoder(new stdClass); - $this->assertFalse($source->isImagick()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isImagick()); - } - - public function testIsGdResource() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $source = $this->getTestDecoder($resource); - $this->assertTrue($source->isGdResource()); - - $source = $this->getTestDecoder(tmpfile()); - $this->assertFalse($source->isGdResource()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isGdResource()); - } - - public function testIsFilepath() - { - $source = $this->getTestDecoder(__DIR__.'/AbstractDecoderTest.php'); - $this->assertTrue($source->isFilepath()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isFilepath()); - - $source = $this->getTestDecoder([]); - $this->assertFalse($source->isFilepath()); - - $source = $this->getTestDecoder(new stdClass); - $this->assertFalse($source->isFilepath()); - } - - public function testIsUrl() - { - $source = $this->getTestDecoder('http://foo.bar'); - $this->assertTrue($source->isUrl()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isUrl()); - } - - public function testIsStream() - { - $source = $this->getTestDecoder(fopen(__DIR__ . '/images/test.jpg', 'r')); - $this->assertTrue($source->isStream()); - - $source = $this->getTestDecoder(new \GuzzleHttp\Psr7\Stream(fopen(__DIR__ . '/images/test.jpg', 'r'))); - $this->assertTrue($source->isStream()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isStream()); - } - - public function testIsBinary() - { - $source = $this->getTestDecoder(file_get_contents(__DIR__.'/images/test.jpg')); - $this->assertTrue($source->isBinary()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isBinary()); - - $source = $this->getTestDecoder(1); - $this->assertFalse($source->isBinary()); - - $source = $this->getTestDecoder(0); - $this->assertFalse($source->isBinary()); - - $source = $this->getTestDecoder([1,2,3]); - $this->assertFalse($source->isBinary()); - - $source = $this->getTestDecoder(new stdClass); - $this->assertFalse($source->isBinary()); - } - - public function testIsInterventionImage() - { - $source = $this->getTestDecoder(1); - $this->assertFalse($source->isInterventionImage()); - - $img = Mockery::mock('Intervention\Image\Image'); - $source = $this->getTestDecoder($img); - $this->assertTrue($source->isInterventionImage()); - } - - public function testIsSplFileInfo() - { - $source = $this->getTestDecoder(1); - $this->assertFalse($source->isSplFileInfo()); - - $img = Mockery::mock('SplFileInfo'); - $source = $this->getTestDecoder($img); - $this->assertTrue($source->isSplFileInfo()); - - $img = Mockery::mock('Symfony\Component\HttpFoundation\File\UploadedFile', 'SplFileInfo'); - $this->assertTrue($source->isSplFileInfo()); - } - - public function testIsSymfonyUpload() - { - $source = $this->getTestDecoder(1); - $this->assertFalse($source->isSymfonyUpload()); - - $img = Mockery::mock('Symfony\Component\HttpFoundation\File\UploadedFile'); - $source = $this->getTestDecoder($img); - $this->assertTrue($source->isSymfonyUpload()); - } - - public function testIsDataUrl() - { - $source = $this->getTestDecoder('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'); - $this->assertTrue($source->isDataUrl()); - - $source = $this->getTestDecoder(null); - $this->assertFalse($source->isDataUrl()); - } - - public function testIsBase64() - { - $decoder = $this->getTestDecoder(null); - $this->assertFalse($decoder->isBase64()); - - $decoder = $this->getTestDecoder('random'); - $this->assertFalse($decoder->isBase64()); - - $base64 = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC"; - $decoder = $this->getTestDecoder($base64); - $this->assertTrue($decoder->isBase64()); - - $base64WithNewlines = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . - '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . - 'cRvs4UAAAAAElFTkSuQmCC'; - - $decoder = $this->getTestDecoder($base64WithNewlines); - $this->assertTrue($decoder->isBase64()); - } - - public function getTestDecoder($data) - { - return $this->getMockForAbstractClass('\Intervention\Image\AbstractDecoder', [$data]); - } -} diff --git a/tests/AbstractDriverTest.php b/tests/AbstractDriverTest.php deleted file mode 100644 index 704c09928..000000000 --- a/tests/AbstractDriverTest.php +++ /dev/null @@ -1,21 +0,0 @@ -getMockForAbstractClass('\Intervention\Image\AbstractDriver'); - $command = $driver->executeCommand($image, 'xxxxxxxxxxxxxxxxxxxxxxx', []); - } -} diff --git a/tests/AbstractFontTest.php b/tests/AbstractFontTest.php deleted file mode 100644 index 37dfe56db..000000000 --- a/tests/AbstractFontTest.php +++ /dev/null @@ -1,86 +0,0 @@ -getMockForAbstractClass('\Intervention\Image\AbstractFont', ['test']); - $this->assertEquals('test', $font->text); - } - - public function testText() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->text('test'); - $this->assertEquals('test', $font->text); - } - - public function testSize() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->size(16); - $this->assertEquals(16, $font->size); - } - - public function testColor() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->color('#ffffff'); - $this->assertEquals('#ffffff', $font->color); - } - - public function testAngle() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->angle(30); - $this->assertEquals(30, $font->angle); - } - - public function testAlign() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->align('right'); - $this->assertEquals('right', $font->align); - } - - public function testValign() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->valign('top'); - $this->assertEquals('top', $font->valign); - } - - public function testKerning() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->kerning(10.5); - $this->assertEquals(10.5, $font->kerning); - } - - public function testFile() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->file('test.ttf'); - $this->assertEquals('test.ttf', $font->file); - } - - public function testCountLines() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->text('foo'.PHP_EOL.'bar'.PHP_EOL.'baz'); - $this->assertEquals(3, $font->countLines()); - $font->text("foo\nbar\nbaz"); - $this->assertEquals(3, $font->countLines()); - $font->text('foo - bar - baz'); - $this->assertEquals(3, $font->countLines()); - } -} diff --git a/tests/AbstractShapeTest.php b/tests/AbstractShapeTest.php deleted file mode 100644 index 64f88adc2..000000000 --- a/tests/AbstractShapeTest.php +++ /dev/null @@ -1,43 +0,0 @@ -getMockForAbstractClass('\Intervention\Image\AbstractShape'); - $shape->background('foo'); - $this->assertEquals('foo', $shape->background); - $this->assertEquals(0, $shape->border_width); - } - - public function testBorder() - { - $shape = $this->getMockForAbstractClass('\Intervention\Image\AbstractShape'); - $shape->border(4); - $this->assertEquals(4, $shape->border_width); - $this->assertEquals('#000000', $shape->border_color); - } - - public function testBorderWithColor() - { - $shape = $this->getMockForAbstractClass('\Intervention\Image\AbstractShape'); - $shape->border(3, '#ff00ff'); - $this->assertEquals(3, $shape->border_width); - $this->assertEquals('#ff00ff', $shape->border_color); - } - - public function testHasBorder() - { - $shape = $this->getMockForAbstractClass('\Intervention\Image\AbstractShape'); - $this->assertFalse($shape->hasBorder()); - $shape->border(1); - $this->assertTrue($shape->hasBorder()); - } -} diff --git a/tests/ArgumentTest.php b/tests/ArgumentTest.php deleted file mode 100644 index a40a10800..000000000 --- a/tests/ArgumentTest.php +++ /dev/null @@ -1,422 +0,0 @@ -getMockedCommand(['foo'])); - $this->validateArgument($arg, 'foo'); - - $arg = new Argument($this->getMockedCommand(['foo', 'bar']), 1); - $this->validateArgument($arg, 'bar'); - - $arg = new Argument($this->getMockedCommand(), 0); - $this->validateArgument($arg, null); - } - - public function testRequiredPass() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->required(); - $this->validateArgument($arg, 'foo'); - - $arg = new Argument($this->getMockedCommand([null])); - $arg->required(); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([0])); - $arg->required(); - $this->validateArgument($arg, 0); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testRequiredFail() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->required(); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testRequiredFailSecondParameter() - { - $arg = new Argument($this->getMockedCommand(['foo']), 1); - $arg->required(); - } - - public function testTypeIntegerPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('integer'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([123])); - $arg->type('integer'); - $this->validateArgument($arg, 123); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeIntegerFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('integer'); - } - - public function testTypeArrayPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('array'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([[1,2,3]])); - $arg->type('array'); - $this->validateArgument($arg, [1,2,3]); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeArrayFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('array'); - } - - public function testTypeDigitPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('digit'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([0])); - $arg->type('digit'); - $this->validateArgument($arg, 0); - - $arg = new Argument($this->getMockedCommand([123])); - $arg->type('digit'); - $this->validateArgument($arg, 123); - - $arg = new Argument($this->getMockedCommand([5.0])); - $arg->type('digit'); - $this->validateArgument($arg, 5.0); - - $arg = new Argument($this->getMockedCommand([-10])); - $arg->type('digit'); - $this->validateArgument($arg, -10); - - $arg = new Argument($this->getMockedCommand([-10.0])); - $arg->type('digit'); - $this->validateArgument($arg, -10.0); - - $arg = new Argument($this->getMockedCommand(['12'])); - $arg->type('digit'); - $this->validateArgument($arg, '12'); - - $arg = new Argument($this->getMockedCommand(['12.0'])); - $arg->type('digit'); - $this->validateArgument($arg, '12.0'); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeDigitFailString() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('digit'); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeDigitFailFloat() - { - $arg = new Argument($this->getMockedCommand([12.5])); - $arg->type('digit'); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeDigitFailBool() - { - $arg = new Argument($this->getMockedCommand([true])); - $arg->type('digit'); - } - - public function testTypeNumericPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('numeric'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([12.3])); - $arg->type('numeric'); - $this->validateArgument($arg, 12.3); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeNumericFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('numeric'); - } - - public function testTypeBooleanPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('boolean'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([true])); - $arg->type('boolean'); - $this->validateArgument($arg, true); - - $arg = new Argument($this->getMockedCommand([false])); - $arg->type('boolean'); - $this->validateArgument($arg, false); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeBooleanFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('boolean'); - } - - public function testTypeStringPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('string'); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('string'); - $this->validateArgument($arg, 'foo'); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeStringFail() - { - $arg = new Argument($this->getMockedCommand([12])); - $arg->type('string'); - } - - public function testTypeClosurePass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->type('closure'); - $this->validateArgument($arg, null); - - $c = function ($foo) {}; - $arg = new Argument($this->getMockedCommand([$c])); - $arg->type('closure'); - $this->validateArgument($arg, $c); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testTypeClosureFail() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->type('closure'); - } - - public function testBetweenPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->between(0, 10); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([null])); - $arg->between(0, 10); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([4.5])); - $arg->between(0, 10); - $this->validateArgument($arg, 4.5); - - $arg = new Argument($this->getMockedCommand([4.5])); - $arg->between(10, 1); - $this->validateArgument($arg, 4.5); - - $arg = new Argument($this->getMockedCommand([0])); - $arg->between(0, 10); - $this->validateArgument($arg, 0); - - $arg = new Argument($this->getMockedCommand([10])); - $arg->between(0, 10); - $this->validateArgument($arg, 10); - - $arg = new Argument($this->getMockedCommand([0])); - $arg->between(-100, 100); - $this->validateArgument($arg, 0); - - $arg = new Argument($this->getMockedCommand([-100])); - $arg->between(-100, 100); - $this->validateArgument($arg, -100); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testBetweenFailString() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->between(1, 10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testBetweenFailAbove() - { - $arg = new Argument($this->getMockedCommand([10.9])); - $arg->between(0, 10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testBetweenFailBelow() - { - $arg = new Argument($this->getMockedCommand([-1])); - $arg->between(0, 10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testBetweenFail() - { - $arg = new Argument($this->getMockedCommand([-1000])); - $arg->between(-100, 100); - } - - public function testMinPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->min(10); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([null])); - $arg->min(10); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([50])); - $arg->min(10); - $this->validateArgument($arg, 50); - - $arg = new Argument($this->getMockedCommand([50])); - $arg->min(50); - $this->validateArgument($arg, 50); - - $arg = new Argument($this->getMockedCommand([50])); - $arg->min(-10); - $this->validateArgument($arg, 50); - - $arg = new Argument($this->getMockedCommand([-10])); - $arg->min(-10); - $this->validateArgument($arg, -10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testMinFailString() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->min(10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testMinFail() - { - $arg = new Argument($this->getMockedCommand([10.9])); - $arg->min(11); - } - - public function testMaxPass() - { - $arg = new Argument($this->getMockedCommand([])); - $arg->max(100); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([null])); - $arg->max(100); - $this->validateArgument($arg, null); - - $arg = new Argument($this->getMockedCommand([50])); - $arg->max(100); - $this->validateArgument($arg, 50); - - $arg = new Argument($this->getMockedCommand([100])); - $arg->max(100); - $this->validateArgument($arg, 100); - - $arg = new Argument($this->getMockedCommand([-100])); - $arg->max(-10); - $this->validateArgument($arg, -100); - - $arg = new Argument($this->getMockedCommand([-10])); - $arg->max(-10); - $this->validateArgument($arg, -10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testMaxFailString() - { - $arg = new Argument($this->getMockedCommand(['foo'])); - $arg->max(10); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testMaxFail() - { - $arg = new Argument($this->getMockedCommand([25])); - $arg->max(10); - } - - public function testValueDefault() - { - $arg = new Argument($this->getMockedCommand()); - $value = $arg->value('foo'); - $this->assertEquals('foo', $value); - - $arg = new Argument($this->getMockedCommand([null])); - $value = $arg->value('foo'); - $this->assertEquals('foo', $value); - } - - private function validateArgument($argument, $value) - { - $this->assertInstanceOf('\Intervention\Image\Commands\Argument', $argument); - $this->assertEquals($value, $argument->value()); - } - - private function getMockedCommand($arguments = []) - { - return $this->getMockForAbstractClass('\Intervention\Image\Commands\AbstractCommand', [$arguments]); - } -} diff --git a/tests/BackupCommandTest.php b/tests/BackupCommandTest.php deleted file mode 100644 index f7d6960f3..000000000 --- a/tests/BackupCommandTest.php +++ /dev/null @@ -1,65 +0,0 @@ -shouldReceive('cloneCore')->once(); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image', [$driver]); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setBackup')->once(); - $command = new BackupGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdWithName() - { - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('cloneCore')->once(); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image', [$driver]); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setBackup')->once(); - $command = new BackupGd(['name' => 'fooBackup']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithoutName() - { - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('cloneCore')->once(); - $imagick = Mockery::mock('Imagick'); - $image = Mockery::mock('Intervention\Image\Image', [$driver]); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setBackup')->once(); - $command = new BackupImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithName() - { - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('cloneCore')->once(); - $imagick = Mockery::mock('Imagick'); - $image = Mockery::mock('Intervention\Image\Image', [$driver]); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setBackup')->once(); - $command = new BackupImagick(['name' => 'fooBackup']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/BlurCommandTest.php b/tests/BlurCommandTest.php deleted file mode 100644 index 6267d48e4..000000000 --- a/tests/BlurCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new BlurGd([2]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('blurimage')->with(2, 1)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new BlurImagick([2]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/BrightnessCommandTest.php b/tests/BrightnessCommandTest.php deleted file mode 100644 index 2c225d0bf..000000000 --- a/tests/BrightnessCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new BrightnessGd([12]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('modulateimage')->with(112, 100, 100)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new BrightnessImagick([12]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ChecksumCommandTest.php b/tests/ChecksumCommandTest.php deleted file mode 100644 index 5be7ca68a..000000000 --- a/tests/ChecksumCommandTest.php +++ /dev/null @@ -1,27 +0,0 @@ -shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('pickColor')->times(9)->andReturn($color); - $command = new ChecksumCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('ec9cbdb71be04e26b4a89333f20c273b', $command->getOutput()); - } -} diff --git a/tests/CircleCommandTest.php b/tests/CircleCommandTest.php deleted file mode 100644 index 9c916a14d..000000000 --- a/tests/CircleCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new CircleCommand([250, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new CircleCommand([25, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/CircleShapeTest.php b/tests/CircleShapeTest.php deleted file mode 100644 index 18a51482d..000000000 --- a/tests/CircleShapeTest.php +++ /dev/null @@ -1,52 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\CircleShape', $circle); - $this->assertEquals(250, $circle->diameter); - - } - - public function testGdApplyToImage() - { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $circle = new CircleGd(250); - $result = $circle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\CircleShape', $circle); - $this->assertTrue($result); - } - - public function testImagickConstructor() - { - $circle = new CircleImagick(250); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\CircleShape', $circle); - $this->assertEquals(250, $circle->width); - } - - public function testImagickApplyToImage() - { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $circle = new CircleImagick(250); - $result = $circle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\CircleShape', $circle); - $this->assertTrue($result); - } - -} diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php new file mode 100644 index 000000000..f02d18439 --- /dev/null +++ b/tests/CollectionTest.php @@ -0,0 +1,102 @@ +assertInstanceOf(Collection::class, $collection); + + $collection = Collection::create(['foo', 'bar', 'baz']); + $this->assertInstanceOf(Collection::class, $collection); + } + + public function testIterator() + { + $collection = new Collection(['foo', 'bar', 'baz']); + foreach ($collection as $key => $item) { + switch ($key) { + case 0: + $this->assertEquals('foo', $item); + break; + + case 1: + $this->assertEquals('bar', $item); + break; + + case 2: + $this->assertEquals('baz', $item); + break; + } + } + } + + public function testCount() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals(3, $collection->count()); + $this->assertEquals(3, count($collection)); + } + + public function testFirstLast() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals('foo', $collection->first()); + $this->assertEquals('baz', $collection->last()); + + $collection = new Collection(); + $this->assertNull($collection->first()); + $this->assertNull($collection->last()); + } + + public function testPush() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals(3, $collection->count()); + $result = $collection->push('test'); + $this->assertEquals(4, $collection->count()); + $this->assertInstanceOf(Collection::class, $result); + } + + public function testPushEach() + { + $collection = Collection::create()->pushEach(['foo', 'bar', 'baz'], function ($item) { + return strtoupper($item); + }); + $this->assertEquals(3, $collection->count()); + $this->assertEquals('FOO', $collection->get(0)); + $this->assertEquals('BAR', $collection->get(1)); + $this->assertEquals('BAZ', $collection->get(2)); + } + + public function testGet() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals('foo', $collection->get(0)); + $this->assertEquals('bar', $collection->get(1)); + $this->assertEquals('baz', $collection->get(2)); + $this->assertNull($collection->get(3)); + } + + public function testToArray() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals(['foo', 'bar', 'baz'], $collection->toArray()); + } + + public function testMap(): void + { + $collection = new Collection(['FOO', 'BAR', 'BAZ']); + $mapped = $collection->map(function ($item) { + return strtolower($item); + }); + $this->assertInstanceOf(Collection::class, $collection); + $this->assertInstanceOf(Collection::class, $mapped); + $this->assertEquals(['FOO', 'BAR', 'BAZ'], $collection->toArray()); + $this->assertEquals(['foo', 'bar', 'baz'], $mapped->toArray()); + } +} diff --git a/tests/ColorizeCommandTest.php b/tests/ColorizeCommandTest.php deleted file mode 100644 index 0b3f9ecea..000000000 --- a/tests/ColorizeCommandTest.php +++ /dev/null @@ -1,37 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new ColorizeGd([20, 0, -40]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getquantumrange')->with()->once()->andReturn(['quantumRangeLong' => 42]); - $imagick->shouldReceive('levelimage')->with(0, 4, 42, \Imagick::CHANNEL_RED)->once()->andReturn(true); - $imagick->shouldReceive('levelimage')->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->once()->andReturn(true); - $imagick->shouldReceive('levelimage')->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(4)->andReturn($imagick); - $command = new ColorizeImagick([20, 0, -40]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ConstraintTest.php b/tests/ConstraintTest.php deleted file mode 100644 index 0ac3375d5..000000000 --- a/tests/ConstraintTest.php +++ /dev/null @@ -1,58 +0,0 @@ -getMockedSize(800, 600); - $constraint = new Constraint($size); - $this->assertInstanceOf('Intervention\Image\Constraint', $constraint); - $this->assertFalse($constraint->isFixed(Constraint::ASPECTRATIO)); - $this->assertFalse($constraint->isFixed(Constraint::UPSIZE)); - } - - public function testSetOnlyAspectRatio() - { - $size = $this->getMockedSize(800, 600); - $constraint = new Constraint($size); - $constraint->aspectRatio(); - $this->assertTrue($constraint->isFixed(Constraint::ASPECTRATIO)); - $this->assertFalse($constraint->isFixed(Constraint::UPSIZE)); - } - - public function testSetOnlyUpsize() - { - $size = $this->getMockedSize(800, 600); - $constraint = new Constraint($size); - $constraint->upsize(); - $this->assertFalse($constraint->isFixed(Constraint::ASPECTRATIO)); - $this->assertTrue($constraint->isFixed(Constraint::UPSIZE)); - } - - public function testSetAspectratioAndUpsize() - { - $size = $this->getMockedSize(800, 600); - $constraint = new Constraint($size); - $constraint->aspectRatio(); - $constraint->upsize(); - $this->assertTrue($constraint->isFixed(Constraint::ASPECTRATIO)); - $this->assertTrue($constraint->isFixed(Constraint::UPSIZE)); - } - - private function getMockedSize($width, $height) - { - $size = Mockery::mock('\Intervention\Image\Size', [$width, $height]); - $size->shouldReceive('getWidth')->andReturn($width); - $size->shouldReceive('getHeight')->andReturn($height); - $size->shouldReceive('getRatio')->andReturn($width/$height); - return $size; - } -} diff --git a/tests/ContrastCommandTest.php b/tests/ContrastCommandTest.php deleted file mode 100644 index 9b81917ac..000000000 --- a/tests/ContrastCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new ContrastGd([20]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('sigmoidalcontrastimage')->with(true, 5, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new ContrastImagick([20]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/CropCommandTest.php b/tests/CropCommandTest.php deleted file mode 100644 index 23ac95ba3..000000000 --- a/tests/CropCommandTest.php +++ /dev/null @@ -1,36 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new CropGd([100, 150, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(100, 150, 10, 20)->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($imagick); - $command = new CropImagick([100, 150, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/DestroyCommandTest.php b/tests/DestroyCommandTest.php deleted file mode 100644 index cc788463d..000000000 --- a/tests/DestroyCommandTest.php +++ /dev/null @@ -1,46 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getBackups')->once()->andReturn($backups); - $command = new DestroyGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->with()->andReturn(true); - - $backup = Mockery::mock('Imagick'); - $backup->shouldReceive('clear')->with()->andReturn(true); - $backups = [$backup]; - - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getBackups')->once()->andReturn($backups); - $command = new DestroyImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/DriverTest.php b/tests/DriverTest.php deleted file mode 100644 index 5cd8a36ea..000000000 --- a/tests/DriverTest.php +++ /dev/null @@ -1,61 +0,0 @@ -newImage(300, 200, '00ff00'); - $this->assertInstanceOf('\Intervention\Image\Image', $image); - $this->assertInstanceOf('\Intervention\Image\Gd\Driver', $image->getDriver()); - $this->assertInternalType('resource', $image->getCore()); - } - - public function testNewImageImagick() - { - $driver = new ImagickDriver( - Mockery::mock('\Intervention\Image\Imagick\Decoder'), - Mockery::mock('\Intervention\Image\Imagick\Encoder') - ); - - $image = $driver->newImage(300, 200, '00ff00'); - $this->assertInstanceOf('\Intervention\Image\Image', $image); - $this->assertInstanceOf('\Intervention\Image\Imagick\Driver', $image->getDriver()); - $this->assertInstanceOf('\Imagick', $image->getCore()); - } - - public function testParseColorGd() - { - $driver = new GdDriver( - Mockery::mock('\Intervention\Image\Gd\Decoder'), - Mockery::mock('\Intervention\Image\Gd\Encoder') - ); - - $color = $driver->parseColor('00ff00'); - $this->assertInstanceOf('\Intervention\Image\Gd\Color', $color); - } - - public function testParseColorImagick() - { - $driver = new ImagickDriver( - Mockery::mock('\Intervention\Image\Imagick\Decoder'), - Mockery::mock('\Intervention\Image\Imagick\Encoder') - ); - - $color = $driver->parseColor('00ff00'); - $this->assertInstanceOf('\Intervention\Image\Imagick\Color', $color); - } -} diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php new file mode 100644 index 000000000..848cadee5 --- /dev/null +++ b/tests/Drivers/Gd/ColorTest.php @@ -0,0 +1,75 @@ +assertInstanceOf(Color::class, $this->getTestColor()); + } + + public function testRed(): void + { + $color = $this->getTestColor(255, 0, 0); + $this->assertEquals(255, $color->red()); + } + + public function testGreen(): void + { + $color = $this->getTestColor(0, 150, 0); + $this->assertEquals(150, $color->green()); + } + + public function testBlue(): void + { + $color = $this->getTestColor(0, 0, 120); + $this->assertEquals(120, $color->blue()); + } + + public function testAlpha(): void + { + $color = $this->getTestColor(0, 0, 120, 0); + $this->assertEquals(1, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, 127); + $this->assertEquals(0, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, 64); + $this->assertEquals(.5, $color->alpha()); + } + + public function testToArray(): void + { + $color = $this->getTestColor(0, 0, 120, 0); + $this->assertEquals([0, 0, 120, 1], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, 127); + $this->assertEquals([0, 0, 120, 0], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, 64); + $this->assertEquals([0, 0, 120, .5], $color->toArray()); + } + + public function testToHex(): void + { + $color = $this->getTestColor(181, 55, 23); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + + $color = $this->getTestColor(181, 55, 23, 127); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + } +} diff --git a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php new file mode 100644 index 000000000..585d864ac --- /dev/null +++ b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php @@ -0,0 +1,21 @@ +decode([181, 55, 23, .5]); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(.5, $color->alpha()); + } +} diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php new file mode 100644 index 000000000..3ccb4f4a8 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -0,0 +1,40 @@ +decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->width()); + $this->assertEquals(16, $image->height()); + $this->assertCount(1, $image); + } + + public function testDecodeGif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->width()); + $this->assertEquals(16, $image->height()); + $this->assertCount(1, $image); + } + + public function testDecodeAnimatedGif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(75, $image->width()); + $this->assertEquals(50, $image->height()); + $this->assertCount(4, $image); + } +} diff --git a/tests/Drivers/Gd/Encoders/GifEncoderTest.php b/tests/Drivers/Gd/Encoders/GifEncoderTest.php new file mode 100644 index 000000000..e668ce229 --- /dev/null +++ b/tests/Drivers/Gd/Encoders/GifEncoderTest.php @@ -0,0 +1,40 @@ +setDelay(1); + $frame2 = new Frame($gd2); + $frame2->setDelay(.2); + $frame3 = new Frame($gd3); + $frame3->setDelay(1); + + return new Image(new Collection([$frame1, $frame2, $frame3])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new GifEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageGif())); + } +} diff --git a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php new file mode 100644 index 000000000..17af9db1b --- /dev/null +++ b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php @@ -0,0 +1,29 @@ +getTestImage(); + $encoder = new JpegEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg)); + } +} \ No newline at end of file diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php new file mode 100644 index 000000000..b17feb516 --- /dev/null +++ b/tests/Drivers/Gd/FrameTest.php @@ -0,0 +1,79 @@ +getTestFrame(); + $this->assertInstanceOf(Frame::class, $frame); + } + + public function testSetGetDelay() + { + $frame = $this->getTestFrame(); + $this->assertEquals(0, $frame->getDelay()); + + $result = $frame->setDelay(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getDelay()); + } + + public function testSetGetDispose() + { + $frame = $this->getTestFrame(); + $this->assertEquals(1, $frame->getDispose()); + + $result = $frame->setDispose(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getDispose()); + } + + public function testSetGetOffsetLeft() + { + $frame = $this->getTestFrame(); + $this->assertEquals(0, $frame->getOffsetLeft()); + + $result = $frame->setOffsetLeft(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetLeft()); + } + + public function testSetGetOffsetTop() + { + $frame = $this->getTestFrame(); + $this->assertEquals(0, $frame->getOffsetTop()); + + $result = $frame->setOffsetTop(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetTop()); + } + + public function testSetGetOffset() + { + $frame = $this->getTestFrame(); + $this->assertEquals(0, $frame->getOffsetTop()); + $this->assertEquals(0, $frame->getOffsetLeft()); + + $result = $frame->setOffset(100, 200); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetLeft()); + $this->assertEquals(200, $frame->getOffsetTop()); + } + + public function testToImage(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Image::class, $frame->toImage()); + } +} diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php new file mode 100644 index 000000000..20bc9abd8 --- /dev/null +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -0,0 +1,17 @@ +newImage(3, 2); + $this->assertInstanceOf(Image::class, $image); + } +} diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php new file mode 100644 index 000000000..db5ddb781 --- /dev/null +++ b/tests/Drivers/Gd/ImageTest.php @@ -0,0 +1,46 @@ +image = new Image(new Collection([new Frame(imagecreatetruecolor(3, 2))])); + } + + public function testConstructor(): void + { + $this->assertInstanceOf(Image::class, $this->image); + } + + public function testIterator(): void + { + foreach ($this->image as $frame) { + $this->assertInstanceOf(Frame::class, $frame); + } + } + + public function testWidth(): void + { + $this->assertEquals(3, $this->image->width()); + } + + public function testHeight(): void + { + $this->assertEquals(2, $this->image->height()); + } + + public function testSize(): void + { + $this->assertInstanceOf(Size::class, $this->image->size()); + } +} diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php new file mode 100644 index 000000000..f4d2d2584 --- /dev/null +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -0,0 +1,62 @@ +expectException(DecoderException::class); + $result = $handler->handle(''); + } + + public function testHandleBinaryImage(): void + { + $handler = new InputHandler(); + $input = file_get_contents(__DIR__ . '/../../images/animation.gif'); + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleFilePathImage(): void + { + $handler = new InputHandler(); + $input = __DIR__ . '/../../images/animation.gif'; + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleBase64Image(): void + { + $handler = new InputHandler(); + $input = base64_encode(file_get_contents(__DIR__ . '/../../images/animation.gif')); + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleDataUriImage(): void + { + $handler = new InputHandler(); + $input = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleArrayColor(): void + { + $handler = new InputHandler(); + $input = [181, 55, 23, .5]; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + } +} diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php new file mode 100644 index 000000000..2768c7f65 --- /dev/null +++ b/tests/Drivers/Imagick/ColorTest.php @@ -0,0 +1,87 @@ +assertInstanceOf(Color::class, $this->getTestColor()); + } + + public function testRed(): void + { + $color = $this->getTestColor(255, 0, 0); + $this->assertEquals(255, $color->red()); + } + + public function testGreen(): void + { + $color = $this->getTestColor(0, 150, 0); + $this->assertEquals(150, $color->green()); + } + + public function testBlue(): void + { + $color = $this->getTestColor(0, 0, 120); + $this->assertEquals(120, $color->blue()); + } + + public function testAlpha(): void + { + $color = $this->getTestColor(0, 0, 120, 1); + $this->assertEquals(1, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, 0); + $this->assertEquals(0, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, .5); + $this->assertEquals(.5, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, .57); + $this->assertEquals(.57, $color->alpha()); + + $color = $this->getTestColor(0, 0, 120, .578); + $this->assertEquals(.58, $color->alpha()); + } + + public function testToArray(): void + { + $color = $this->getTestColor(0, 0, 120, 1); + $this->assertEquals([0, 0, 120, 1], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, 0); + $this->assertEquals([0, 0, 120, 0], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, .5); + $this->assertEquals([0, 0, 120, .5], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, .57); + $this->assertEquals([0, 0, 120, .57], $color->toArray()); + + $color = $this->getTestColor(0, 0, 120, .578); + $this->assertEquals([0, 0, 120, .58], $color->toArray()); + } + + public function testToHex(): void + { + $color = $this->getTestColor(181, 55, 23); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + + $color = $this->getTestColor(181, 55, 23, 127); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + } +} diff --git a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php new file mode 100644 index 000000000..e0e16800e --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php @@ -0,0 +1,21 @@ +decode([181, 55, 23, .5]); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(.5, $color->alpha()); + } +} diff --git a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php new file mode 100644 index 000000000..9e51820ed --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php @@ -0,0 +1,42 @@ +newImage(30, 20, new ImagickPixel('red'), 'png'); + $frame1 = new Frame($imagick1); + $frame1->setDelay(50); + $imagick2 = new Imagick(); + $imagick2->newImage(30, 20, new ImagickPixel('green'), 'png'); + $frame2 = new Frame($imagick2); + $frame2->setDelay(50); + $imagick3 = new Imagick(); + $imagick3->newImage(30, 20, new ImagickPixel('blue'), 'png'); + $frame3 = new Frame($imagick3); + $frame3->setDelay(50); + + return new Image(new Collection([$frame1, $frame2, $frame3])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new GifEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageGif())); + } +} diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php new file mode 100644 index 000000000..7a01f54a7 --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -0,0 +1,33 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + $frame = new Frame($imagick); + + return new Image(new Collection([$frame])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new JpegEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg)); + } +} \ No newline at end of file diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php new file mode 100644 index 000000000..c7c2a26de --- /dev/null +++ b/tests/Drivers/Imagick/FrameTest.php @@ -0,0 +1,87 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + $imagick->setImageDelay(4); + $imagick->setImageDispose(5); + $imagick->setImagePage(3, 2, 8, 9); + + return new Frame($imagick); + } + + public function testConstructor(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Frame::class, $frame); + } + + public function testSetGetDelay() + { + $frame = $this->getTestFrame(); + $this->assertEquals(4, $frame->getDelay()); + + $result = $frame->setDelay(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getDelay()); + } + + public function testSetGetDispose() + { + $frame = $this->getTestFrame(); + $this->assertEquals(5, $frame->getDispose()); + + $result = $frame->setDispose(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getDispose()); + } + + public function testSetGetOffsetLeft() + { + $frame = $this->getTestFrame(); + $this->assertEquals(8, $frame->getOffsetLeft()); + + $result = $frame->setOffsetLeft(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetLeft()); + } + + public function testSetGetOffsetTop() + { + $frame = $this->getTestFrame(); + $this->assertEquals(9, $frame->getOffsetTop()); + + $result = $frame->setOffsetTop(100); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetTop()); + } + + public function testSetGetOffset() + { + $frame = $this->getTestFrame(); + $this->assertEquals(8, $frame->getOffsetLeft()); + $this->assertEquals(9, $frame->getOffsetTop()); + + $result = $frame->setOffset(100, 200); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(100, $frame->getOffsetLeft()); + $this->assertEquals(200, $frame->getOffsetTop()); + } + + public function testToImage(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Image::class, $frame->toImage()); + } +} diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php new file mode 100644 index 000000000..611825d0f --- /dev/null +++ b/tests/Drivers/Imagick/ImageTest.php @@ -0,0 +1,51 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + + $this->image = new Image(new Collection([new Frame($imagick)])); + } + + public function testConstructor(): void + { + $this->assertInstanceOf(Image::class, $this->image); + } + + public function testIterator(): void + { + foreach ($this->image as $frame) { + $this->assertInstanceOf(Frame::class, $frame); + } + } + + public function testWidth(): void + { + $this->assertEquals(3, $this->image->width()); + } + + public function testHeight(): void + { + $this->assertEquals(2, $this->image->height()); + } + + public function testSize(): void + { + $this->assertInstanceOf(Size::class, $this->image->size()); + } +} diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php new file mode 100644 index 000000000..6bc689ba5 --- /dev/null +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -0,0 +1,62 @@ +expectException(DecoderException::class); + $result = $handler->handle(''); + } + + public function testHandleBinaryImage(): void + { + $handler = new InputHandler(); + $input = file_get_contents(__DIR__ . '/../../images/animation.gif'); + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleFilePathImage(): void + { + $handler = new InputHandler(); + $input = __DIR__ . '/../../images/animation.gif'; + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleBase64Image(): void + { + $handler = new InputHandler(); + $input = base64_encode(file_get_contents(__DIR__ . '/../../images/animation.gif')); + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleDataUriImage(): void + { + $handler = new InputHandler(); + $input = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + $result = $handler->handle($input); + $this->assertInstanceOf(Image::class, $result); + } + + public function testHandleArrayColor(): void + { + $handler = new InputHandler(); + $input = [181, 55, 23, .5]; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + } +} diff --git a/tests/EllipseCommandTest.php b/tests/EllipseCommandTest.php deleted file mode 100644 index d3b8b2b3d..000000000 --- a/tests/EllipseCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new EllipseCommand([250, 150, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new EllipseCommand([250, 150, 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/EllipseShapeTest.php b/tests/EllipseShapeTest.php deleted file mode 100644 index 9c8572755..000000000 --- a/tests/EllipseShapeTest.php +++ /dev/null @@ -1,55 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\EllipseShape', $ellipse); - $this->assertEquals(250, $ellipse->width); - $this->assertEquals(150, $ellipse->height); - - } - - public function testGdApplyToImage() - { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $ellipse = new EllipseGd(250, 150); - $result = $ellipse->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\EllipseShape', $ellipse); - $this->assertTrue($result); - } - - public function testImagickConstructor() - { - $ellipse = new EllipseImagick(250, 150); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\EllipseShape', $ellipse); - $this->assertEquals(250, $ellipse->width); - $this->assertEquals(150, $ellipse->height); - - } - - public function testImagickApplyToImage() - { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $ellipse = new EllipseImagick(250, 150); - $result = $ellipse->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\EllipseShape', $ellipse); - $this->assertTrue($result); - } - -} diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php deleted file mode 100644 index f68818df7..000000000 --- a/tests/EncoderTest.php +++ /dev/null @@ -1,339 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'jpg', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/jpeg; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessPngGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'png', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/png; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessGifGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'gif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/gif; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessWebpGd() - { - if (function_exists('imagewebp')) { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'webp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); - } - } - - public function testProcessWebpGdWithUnSupportedPalette() - { - if (function_exists('imagewebp')) { - $core = imagecreatefrompng(__DIR__.'/images/black-friday.png'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'webp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result)); - } - } - - public function testProcessAvifGd() - { - if (function_exists('imageavif')) { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'avif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/avif; charset=binary', $this->getMime($encoder->result)); - } - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessHeicGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'heic', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessTiffGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'tif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testProcessBmpGd() - { - if (function_exists('imagebmp')) { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'bmp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/x-ms-bmp; charset=binary', $this->getMime($encoder->result)); - } - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessIcoGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'ico', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessPsdGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'psd', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testProcessUnknownWithMimeGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->mime = 'image/jpeg'; - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/jpeg; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessUnknownGd() - { - $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $encoder = new GdEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('image/jpeg; charset=binary', $this->getMime($encoder->result)); - } - - public function testProcessJpegImagick() - { - $core = $this->getImagickMock('jpeg'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'jpg', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-jpeg', $encoder->result); - } - - public function testProcessPngImagick() - { - $core = $this->getImagickMock('png'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'png', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-png', $encoder->result); - } - - public function testProcessGifImagick() - { - $core = $this->getImagickMock('gif'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'gif', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-gif', $encoder->result); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessWebpImagick() - { - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'webp', 90); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessAvifImagick() - { - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'avif', 90); - } - - /** - * @expectedException \Intervention\Image\Exception\NotSupportedException - */ - public function testProcessHeicImagick() - { - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $img = $encoder->process($image, 'heic', 90); - } - - public function testProcessTiffImagick() - { - $core = $this->getImagickMock('tiff'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'tiff', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-tiff', $encoder->result); - } - - public function testProcessBmpImagick() - { - $core = $this->getImagickMock('bmp'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'bmp', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-bmp', $encoder->result); - } - - public function testProcessIcoImagick() - { - $core = $this->getImagickMock('ico'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'ico', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-ico', $encoder->result); - } - - public function testProcessPsdImagick() - { - $core = $this->getImagickMock('psd'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, 'psd', 90); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-psd', $encoder->result); - } - - public function testProcessUnknownWithMimeImagick() - { - $core = $this->getImagickMock('jpeg'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->mime = 'image/jpeg'; - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-jpeg', $encoder->result); - } - - public function testProcessUnknownImagick() - { - $core = $this->getImagickMock('jpeg'); - $encoder = new ImagickEncoder; - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $image->shouldReceive('setEncoded')->once()->andReturn($image); - $img = $encoder->process($image, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('mock-jpeg', $encoder->result); - } - - public function getImagickMock($type) - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('setformat')->with($type)->once(); - $imagick->shouldReceive('setimageformat')->once(); - $imagick->shouldReceive('setcompression')->once(); - $imagick->shouldReceive('setimagecompression')->once(); - $imagick->shouldReceive('setcompressionquality'); - $imagick->shouldReceive('setimagecompressionquality'); - $imagick->shouldReceive('setimagebackgroundcolor'); - $imagick->shouldReceive('setbackgroundcolor'); - $imagick->shouldReceive('mergeimagelayers')->andReturn($imagick); - $imagick->shouldReceive('getimagesblob')->once()->andReturn(sprintf('mock-%s', $type)); - return $imagick; - } - - public function getMime($data) - { - $finfo = new finfo(FILEINFO_MIME); - return $finfo->buffer($data); - } -} diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php deleted file mode 100644 index 8b051c95c..000000000 --- a/tests/ExifCommandTest.php +++ /dev/null @@ -1,112 +0,0 @@ -dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - } - - public function testFetchDefined() - { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand(['Artist']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - public function testFetchNonExisting() - { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand(['xxx']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testFetchFromPng() - { - $image = new Image; - $image->dirname = __DIR__.'/images'; - $image->basename = 'star.png'; - $command = new ExifCommand(['Orientation']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testImagickFetchAll() - { - $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand([]); - $command->dontPreferExtension(); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()['Artist']); - } - - public function testImagickFetchDefined() - { - $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['Artist']); - $command->dontPreferExtension(); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - public function testImagickNonExisting() - { - $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['xx']); - $command->dontPreferExtension(); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testImagickFallbackToExifExtenstion() - { - $image = $this->imagick()->make(__DIR__.'/images/exif.jpg'); - $command = new \Intervention\Image\Imagick\Commands\ExifCommand(['Artist']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - private function imagick() - { - return new \Intervention\Image\ImageManager([ - 'driver' => 'imagick' - ]); - } -} diff --git a/tests/FileTest.php b/tests/FileTest.php deleted file mode 100644 index e1c01fd24..000000000 --- a/tests/FileTest.php +++ /dev/null @@ -1,28 +0,0 @@ -setFileInfoFromPath('tests/images/test.jpg'); - $this->assertEquals('tests/images', $file->dirname); - $this->assertEquals('test.jpg', $file->basename); - $this->assertEquals('jpg', $file->extension); - $this->assertEquals('test', $file->filename); - $this->assertEquals('image/jpeg', $file->mime); - } - - public function testBasePath() - { - $file = new File; - $this->assertNull(null, $file->basePath() ?: ''); - - $file->dirname = 'foo'; - $file->basename = 'bar'; - $this->assertEquals('foo/bar', $file->basePath()); - } -} diff --git a/tests/FillCommandTest.php b/tests/FillCommandTest.php deleted file mode 100644 index cbd3f1ed6..000000000 --- a/tests/FillCommandTest.php +++ /dev/null @@ -1,93 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd(['666666']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillArray() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd([[50, 50, 50]]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillArrayWithAlpha() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd([[50, 50, 50, .50]]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillWithCoordinates() - { - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('setCore')->once(); - $driver->shouldReceive('newImage')->with(800, 600)->once()->andReturn($image); - $command = new FillGd(['#666666', 0, 0]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFill() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('drawimage')->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->andReturn($imagick); - $command = new FillImagick(['666666']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFillWithCoordinates() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->once()->andReturn('#000000'); - $imagick->shouldReceive('transparentpaintimage')->once()->andReturn(true); - $imagick->shouldReceive('compositeimage')->times(3)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->andReturn($imagick); - $image->shouldReceive('getWidth')->andReturn(800); - $image->shouldReceive('getHeight')->andReturn(600); - $command = new FillImagick(['666666', 0, 0]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/FitCommandTest.php b/tests/FitCommandTest.php deleted file mode 100644 index 23b1cb3bb..000000000 --- a/tests/FitCommandTest.php +++ /dev/null @@ -1,93 +0,0 @@ -shouldReceive('getWidth')->times(2)->andReturn(800); - $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); - $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new FitGd([200, 100]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFitWithPosition() - { - $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); - $cropped_size->shouldReceive('getWidth')->times(2)->andReturn(800); - $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); - $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new FitGd([200, 100, null, 'top-left']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFit() - { - $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); - $cropped_size->shouldReceive('getWidth')->once()->andReturn(200); - $cropped_size->shouldReceive('getHeight')->once()->andReturn(100); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); - $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $imagick->shouldReceive('scaleimage')->with(200, 100)->once()->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new FitImagick([200, 100]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFitWithPosition() - { - $cropped_size = Mockery::mock('\Intervention\Image\Size', [800, 400]); - $cropped_size->shouldReceive('getWidth')->once()->andReturn(200); - $cropped_size->shouldReceive('getHeight')->once()->andReturn(100); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', [0, 100]); - $original_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $imagick->shouldReceive('scaleimage')->with(200, 100)->once()->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new FitImagick([200, 100, null, 'top-left']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/FlipCommandTest.php b/tests/FlipCommandTest.php deleted file mode 100644 index 09370eb60..000000000 --- a/tests/FlipCommandTest.php +++ /dev/null @@ -1,45 +0,0 @@ -shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new FlipGd(['h']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('flopimage')->with()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new FlipImagick(['h']); - $result = $command->execute($image); - $this->assertTrue($result); - - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('flipimage')->with()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new FlipImagick(['v']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/GammaCommandTest.php b/tests/GammaCommandTest.php deleted file mode 100644 index 6430236d3..000000000 --- a/tests/GammaCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new GammaGd([1.4]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('gammaimage')->with(1.4)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GammaImagick([1.4]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/GdColorTest.php b/tests/GdColorTest.php deleted file mode 100644 index 7fa9f65fd..000000000 --- a/tests/GdColorTest.php +++ /dev/null @@ -1,296 +0,0 @@ -validateColor($c, 255, 255, 255, 127); - } - - public function testParseNull() - { - $c = new Color; - $c->parse(null); - $this->validateColor($c, 255, 255, 255, 127); - } - - public function testParseInteger() - { - $c = new Color; - $c->parse(850736919); - $this->validateColor($c, 181, 55, 23, 50); - } - - public function testParseArray() - { - $c = new Color; - $c->parse([181, 55, 23, 0.5]); - $this->validateColor($c, 181, 55, 23, 64); - } - - public function testParseHexString() - { - $c = new Color; - $c->parse('#b53717'); - $this->validateColor($c, 181, 55, 23, 0); - } - - public function testParseRgbaString() - { - $c = new Color; - $c->parse('rgba(181, 55, 23, 1)'); - $this->validateColor($c, 181, 55, 23, 0); - } - - public function testInitFromInteger() - { - $c = new Color; - $c->initFromInteger(0); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromInteger(2147483647); - $this->validateColor($c, 255, 255, 255, 127); - $c->initFromInteger(16777215); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromInteger(2130706432); - $this->validateColor($c, 0, 0, 0, 127); - $c->initFromInteger(850736919); - $this->validateColor($c, 181, 55, 23, 50); - } - - public function testInitFromArray() - { - $c = new Color; - $c->initFromArray([0, 0, 0, 0]); - $this->validateColor($c, 0, 0, 0, 127); - $c->initFromArray([0, 0, 0, 1]); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray([255, 255, 255, 1]); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray([255, 255, 255, 0]); - $this->validateColor($c, 255, 255, 255, 127); - $c->initFromArray([255, 255, 255, 0.5]); - $this->validateColor($c, 255, 255, 255, 64); - $c->initFromArray([0, 0, 0]); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray([255, 255, 255]); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray([181, 55, 23]); - $this->validateColor($c, 181, 55, 23, 0); - $c->initFromArray([181, 55, 23, 0.5]); - $this->validateColor($c, 181, 55, 23, 64); - } - - public function testInitFromHexString() - { - $c = new Color; - $c->initFromString('#cccccc'); - $this->validateColor($c, 204, 204, 204, 0); - $c->initFromString('#b53717'); - $this->validateColor($c, 181, 55, 23, 0); - $c->initFromString('ffffff'); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromString('ff00ff'); - $this->validateColor($c, 255, 0, 255, 0); - $c->initFromString('#000'); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromString('000'); - $this->validateColor($c, 0, 0, 0, 0); - } - - public function testInitFromRgbString() - { - $c = new Color; - $c->initFromString('rgb(1, 14, 144)'); - $this->validateColor($c, 1, 14, 144, 0); - $c->initFromString('rgb (255, 255, 255)'); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromString('rgb(0,0,0)'); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromString('rgba(0,0,0,0)'); - $this->validateColor($c, 0, 0, 0, 127); - $c->initFromString('rgba(0,0,0,0.5)'); - $this->validateColor($c, 0, 0, 0, 64); - $c->initFromString('rgba(255, 0, 0, 0.5)'); - $this->validateColor($c, 255, 0, 0, 64); - $c->initFromString('rgba(204, 204, 204, 0.9)'); - $this->validateColor($c, 204, 204, 204, 13); - } - - public function testInitFromRgb() - { - $c = new Color; - $c->initFromRgb(0, 0, 0); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromRgb(255, 255, 255); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromRgb(181, 55, 23); - $this->validateColor($c, 181, 55, 23, 0); - } - - public function testInitFromRgba() - { - $c = new Color; - $c->initFromRgba(0, 0, 0, 1); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromRgba(255, 255, 255, 1); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromRgba(181, 55, 23, 1); - $this->validateColor($c, 181, 55, 23, 0); - $c->initFromRgba(181, 55, 23, 0); - $this->validateColor($c, 181, 55, 23, 127); - $c->initFromRgba(181, 55, 23, 0.5); - $this->validateColor($c, 181, 55, 23, 64); - } - - public function testGetInt() - { - $c = new Color; - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals(2147483647, $i); - - $c = new Color([255, 255, 255]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 16777215); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 16777215); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 1085617943); - - $c = new Color([181, 55, 23, 1]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 11876119); - - $c = new Color([0, 0, 0, 0]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 2130706432); - } - - public function testGetHex() - { - $c = new Color; - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'ffffff'); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'ffffff'); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'b53717'); - - $c = new Color([0, 0, 0, 0]); - $i = $c->getHex('#'); - $this->assertInternalType('string', $i); - $this->assertEquals($i, '#000000'); - } - - public function testGetArray() - { - $c = new Color; - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [255, 255, 255, 0]); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [255, 255, 255, 1]); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [181, 55, 23, 0.5]); - - $c = new Color([0, 0, 0, 1]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [0, 0, 0, 1]); - } - - public function testGetRgba() - { - $c = new Color; - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 0.00)'); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 1.00)'); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(181, 55, 23, 0.50)'); - - $c = new Color([0, 0, 0, 1]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(0, 0, 0, 1.00)'); - } - - public function testDiffers() - { - $c1 = new Color([0, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2)); - - $c1 = new Color([1, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(true, $c1->differs($c2)); - - $c1 = new Color([1, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2, 10)); - - $c1 = new Color([127, 127, 127]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(true, $c1->differs($c2, 49)); - - $c1 = new Color([127, 127, 127]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2, 50)); - } - - /** - * @expectedException \Intervention\Image\Exception\NotReadableException - */ - public function testParseUnknown() - { - $c = new Color('xxxxxxxxxxxxxxxxxxxx'); - } - - private function validateColor($obj, $r, $g, $b, $a) - { - $this->assertInstanceOf('Intervention\Image\Gd\Color', $obj); - $this->assertInternalType('int', $r); - $this->assertInternalType('int', $g); - $this->assertInternalType('int', $b); - $this->assertInternalType('int', $a); - $this->assertEquals($obj->r, $r); - $this->assertEquals($obj->g, $g); - $this->assertEquals($obj->b, $b); - $this->assertEquals($obj->a, $a); - } -} diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php deleted file mode 100644 index 7d7d5ecdb..000000000 --- a/tests/GdSystemTest.php +++ /dev/null @@ -1,1703 +0,0 @@ -manager()->make('tests/images/circle.png'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - $this->assertEquals('tests/images', $img->dirname); - $this->assertEquals('circle.png', $img->basename); - $this->assertEquals('png', $img->extension); - $this->assertEquals('circle', $img->filename); - $this->assertEquals('image/png', $img->mime); - } - - /** - * @expectedException \Intervention\Image\Exception\NotReadableException - */ - public function testMakeFromPathBroken() - { - $this->manager()->make('tests/images/broken.png'); - } - - /** - * @expectedException \Intervention\Image\Exception\NotReadableException - */ - public function testMakeFromNotExisting() - { - $this->manager()->make('tests/images/not_existing.png'); - } - - public function testMakeFromString() - { - $str = file_get_contents('tests/images/circle.png'); - $img = $this->manager()->make($str); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromResource() - { - $resource = imagecreatefrompng('tests/images/circle.png'); - $img = $this->manager()->make($resource); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - } - - public function testMakeFromDataUrl() - { - $img = $this->manager()->make('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - } - - public function testMakeFromBase64() - { - $img = $this->manager()->make('iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - } - - public function testMakeFromBase64WithNewlines() - { - $data = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . - '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . - 'cRvs4UAAAAAElFTkSuQmCC'; - - $img = $this->manager()->make($data); - - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - } - - public function testMakeFromWebp() - { - if (function_exists('imagecreatefromwebp')) { - $img = $this->manager()->make('tests/images/test.webp'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertEquals('image/webp', $img->mime); - $this->assertEquals('tests/images', $img->dirname); - $this->assertEquals('test.webp', $img->basename); - $this->assertEquals('webp', $img->extension); - $this->assertEquals('test', $img->filename); - } - } - - public function testCanvas() - { - $img = $this->manager()->canvas(30, 20); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testCanvasWithSolidBackground() - { - $img = $this->manager()->canvas(30, 20, 'b53717'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertEquals('#b53717', $img->pickColor(15, 15, 'hex')); - } - - public function testGetSize() - { - $img = $this->manager()->make('tests/images/tile.png'); - $size = $img->getSize(); - $this->assertInstanceOf('Intervention\Image\Size', $size); - $this->assertInternalType('int', $size->width); - $this->assertInternalType('int', $size->height); - $this->assertEquals(16, $size->width); - $this->assertEquals(16, $size->height); - } - - public function testResizeImage() - { - $img = $this->manager()->make('tests/images/circle.png'); - $img->resize(120, 150); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(120, $img->getWidth()); - $this->assertEquals(150, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testResizeImageOnlyWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(120, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(120, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 15); - } - - public function testResizeImageOnlyHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(null, 150); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(150, $img->getHeight()); - $this->assertTransparentPosition($img, 15, 0); - } - - public function testResizeImageAutoHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(50, null, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertTransparentPosition($img, 30, 0); - } - - public function testResizeImageAutoWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(null, 50, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertTransparentPosition($img, 30, 0); - } - - public function testResizeDominantWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(100, 120, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testResizeImagePreserveSimpleUpsizing() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(100, 100, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 15, 0); - } - - public function testWidenImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->widen(100); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testWidenImageWithConstraint() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->widen(100, function ($constraint) {$constraint->upsize();}); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 8, 0); - } - - public function testHeightenImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->heighten(100); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testHeightenImageWithConstraint() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->heighten(100, function ($constraint) {$constraint->upsize();}); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('resource', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 8, 0); - } - - public function testResizeCanvasCenter() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 5, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 5, 4); - } - - public function testResizeCanvasTopLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top-left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 8, 7); - } - - public function testResizeCanvasTop() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 5, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 5, 7); - } - - public function testResizeCanvasTopRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top-right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 2, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 2, 7); - } - - public function testResizeCanvasLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 8, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 8, 4); - } - - public function testResizeCanvasRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 2, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 2, 4); - } - - public function testResizeCanvasBottomLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom-left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 8, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 8, 1); - } - - public function testResizeCanvasBottomRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom-right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 2, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 2, 1); - } - - public function testResizeCanvasBottom() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 5, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 5, 1); - } - - public function testResizeCanvasRelativeWithBackground() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(4, 4, 'center', true, '#ff00ff'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(20, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#ff00ff', $img, 0, 0); - $this->assertColorAtPosition('#ff00ff', $img, 19, 19); - $this->assertColorAtPosition('#b4e000', $img, 2, 9); - $this->assertColorAtPosition('#445160', $img, 10, 10); - $this->assertTransparentPosition($img, 2, 10); - $this->assertTransparentPosition($img, 10, 9); - } - - public function testResizeCanvasJustWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, null); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 5, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 5, 7); - } - - public function testResizeCanvasJustHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(null, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 8, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 8, 4); - } - - public function testResizeCanvasSmallerWidthLargerHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 20); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 9); - $this->assertColorAtPosition('#445160', $img, 5, 10); - $this->assertTransparentPosition($img, 0, 10); - $this->assertTransparentPosition($img, 5, 9); - } - - public function testResizeCanvasLargerWidthSmallerHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(20, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(20, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 2, 4); - $this->assertColorAtPosition('#445160', $img, 10, 5); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 2, 5); - $this->assertTransparentPosition($img, 10, 4); - } - - public function testResizeCanvasNegative() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(-4, -4); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(12, $img->getWidth()); - $this->assertEquals(12, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 5); - $this->assertColorAtPosition('#445160', $img, 6, 6); - $this->assertTransparentPosition($img, 0, 6); - $this->assertTransparentPosition($img, 6, 5); - } - - public function testResizeCanvasLargerHeightAutoWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(null, 20, 'bottom-left', false, '#ff00ff'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#ff00ff', $img, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#b4e000', $img, 0, 11); - $this->assertColorAtPosition('#445160', $img, 8, 12); - $this->assertTransparentPosition($img, 0, 12); - $this->assertTransparentPosition($img, 8, 11); - } - - public function testResizeCanvasBorderNonRelative() - { - $img = $this->manager()->canvas(1, 1, 'ff0000'); - $img->resizeCanvas(17, 17, 'center', false, '333333'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(17, $img->getWidth()); - $this->assertEquals(17, $img->getHeight()); - $this->assertColorAtPosition('#333333', $img, 0, 0); - $this->assertColorAtPosition('#333333', $img, 5, 5); - $this->assertColorAtPosition('#333333', $img, 7, 7); - $this->assertColorAtPosition('#ff0000', $img, 8, 8); - } - - public function testCropImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->crop(6, 6); // should be centered without pos. - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(6, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 3, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 3, 2); - } - - public function testCropImageWithPosition() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->crop(4, 4, 7, 7); // should be centered without pos. - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(4, $img->getWidth()); - $this->assertEquals(4, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 1, 1); - $this->assertTransparentPosition($img, 0, 1); - $this->assertTransparentPosition($img, 1, 0); - } - - public function testFitImageSquare() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fit(6); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(6, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445060', $img, 3, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 3, 2); - } - - public function testFitImageRectangle() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fit(12, 6); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(12, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 6, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 6, 2); - } - - public function testFitImageWithConstraintUpsize() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->fit(300, 150, function ($constraint) {$constraint->upsize();}); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(25, $img->getHeight()); - $this->assertColorAtPosition('#00aef0', $img, 0, 0); - $this->assertColorAtPosition('#afa94c', $img, 17, 0); - $this->assertColorAtPosition('#ffa601', $img, 24, 0); - } - - public function testFlipImageHorizontal() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->flip('h'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 8, 7); - $this->assertColorAtPosition('#445160', $img, 0, 8); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testFlipImageVertical() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->flip('v'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 7); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testRotateImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->rotate(90); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 7); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testInsertImage() - { - $watermark = $this->manager()->canvas(16, 16, '#0000ff'); // create watermark - - // top-left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(0, 0, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(16, 16, 'hex')); - - // top-left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(9, 9, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(10, 10, 'hex')); - - // top anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(0, 0, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 15, 'hex')); - - // top anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(18, 10, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(31, 26, 'hex')); - - // top-right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(15, 0, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(31, 0, 'hex')); - - // top-right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(6, 9, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(21, 25, 'hex')); - - // left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(15, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(0, 7, 'hex')); - - // left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(8, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(10, 7, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(25, 23, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(25, 8, 'hex')); - - // right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(31, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(15, 15, 'hex')); - - // right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(5, 8, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(22, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(21, 7, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(6, 8, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(21, 23, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(6, 23, 'hex')); - - // bottom-left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(15, 31, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(0, 15, 'hex')); - - // bottom-left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(10, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(9, 20, 'hex')); - - // bottom anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(8, 16, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 15, 'hex')); - - // bottom anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(5, 8, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(23, 22, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(24, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(7, 6, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(8, 6, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 21, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 6, 'hex')); - - // bottom-right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(16, 16, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(15, 16, 'hex')); - - // bottom-right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(21, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(22, 22, 'hex')); - - // center anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'center', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(23, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 7, 'hex')); - - // center anchor coordinates / coordinates will be ignored for center - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'center', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(23, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 7, 'hex')); - } - - public function testInsertWithAlphaChannel() - { - $img = $this->manager()->canvas(50, 50, 'ff0000'); - $img->insert('tests/images/circle.png'); - $this->assertColorAtPosition('#ff0000', $img, 0, 0); - $this->assertColorAtPosition('#320000', $img, 30, 30); - } - - public function testInsertAfterResize() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->resize(16, 16)->insert('tests/images/tile.png'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#ffa601', $img, 8, 7); - } - - public function testInsertResource() - { - $resource = imagecreatefrompng('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($resource); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testInsertBinary() - { - $data = file_get_contents('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($data); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testInsertInterventionImage() - { - $obj = $this->manager()->make('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($obj); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testOpacity() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->opacity(50); - $checkColor = $img->pickColor(7, 7, 'array'); - $this->assertEquals($checkColor[0], 180); - $this->assertEquals($checkColor[1], 224); - $this->assertEquals($checkColor[2], 0); - $this->assertEquals($checkColor[3], 0.5); - $checkColor = $img->pickColor(8, 8, 'array'); - $this->assertEquals($checkColor[0], 68); - $this->assertEquals($checkColor[1], 81); - $this->assertEquals($checkColor[2], 96); - $this->assertEquals($checkColor[3], 0.5); - $this->assertTransparentPosition($img, 0, 11); - } - - public function testMaskImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->mask('tests/images/gradient.png'); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 23, 23); - $checkColor = $img->pickColor(23, 24, 'array'); - $this->assertEquals($checkColor[0], 255); - $this->assertEquals($checkColor[1], 166); - $this->assertEquals($checkColor[2], 1); - $this->assertEquals($checkColor[3], 0.97); - $checkColor = $img->pickColor(39, 25, 'array'); - $this->assertEquals($checkColor[0], 0); - $this->assertEquals($checkColor[1], 174); - $this->assertEquals($checkColor[2], 240); - $this->assertEquals($checkColor[3], 0.32); - } - - public function testMaskImageWithAlpha() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->mask('tests/images/star.png', true); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 16, 16); - $checkColor = $img->pickColor(18, 18, 'array'); - $this->assertEquals($checkColor[0], 255); - $this->assertEquals($checkColor[1], 166); - $this->assertEquals($checkColor[2], 1); - $this->assertEquals($checkColor[3], 0.65); - $checkColor = $img->pickColor(24, 10, 'array'); - $this->assertEquals($checkColor[0], 0); - $this->assertEquals($checkColor[1], 174); - $this->assertEquals($checkColor[2], 240); - $this->assertEquals($checkColor[3], 0.80); - } - - public function testPixelateImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->pixelate(20); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testGreyscaleImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->greyscale(); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertTransparentPosition($img, 8, 0); - $this->assertColorAtPosition('#b9b9b9', $img, 0, 0); - } - - public function testInvertImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->invert(); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertTransparentPosition($img, 8, 0); - $this->assertColorAtPosition('#4b1fff', $img, 0, 0); - } - - public function testBlurImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->blur(1); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testFillImageWithColor() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fill('b53717'); - $this->assertColorAtPosition('#b53717', $img, 0, 0); - $this->assertColorAtPosition('#b53717', $img, 15, 15); - } - - public function testFillImageWithColorAtPosition() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fill('b53717', 0, 0); - $this->assertTransparentPosition($img, 0, 8); - $this->assertColorAtPosition('#b53717', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 15, 15); - } - - public function testFillImageWithResource() - { - $resource = imagecreatefrompng('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($resource, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testFillImageWithBinary() - { - $data = file_get_contents('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($data, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testFillImageWithInterventionImage() - { - $obj = $this->manager()->make('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($obj, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testPixelImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $coords = [[5, 5], [12, 12]]; - $img = $img->pixel('fdf5e4', $coords[0][0], $coords[0][1]); - $img = $img->pixel([255, 255, 255], $coords[1][0], $coords[1][1]); - $this->assertEquals('#fdf5e4', $img->pickColor($coords[0][0], $coords[0][1], 'hex')); - $this->assertEquals('#ffffff', $img->pickColor($coords[1][0], $coords[1][1], 'hex')); - } - - public function testTextImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img = $img->text('0', 3, 11); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('a9e2b15452b2a4637b65625188d206f6', $img->checksum()); - - - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img = $img->text('0', 8, 2, function($font) { - $font->align('center'); - $font->valign('top'); - $font->color('000000'); - }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('649f3f529d3931c56601155fd2680959', $img->checksum()); - - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img = $img->text('0', 8, 8, function($font) { - $font->align('right'); - $font->valign('middle'); - $font->file(2); - $font->color('000000'); - }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('c0dda67589c46a90d78a97b891a811ee', $img->checksum()); - } - - public function testRectangleImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->rectangle(5, 5, 11, 11, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e95487dcc29daf371a0e9190bff8dbfe', $img->checksum()); - } - - public function testLineImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->line(0, 0, 15, 15, function ($draw) { $draw->color('#ff0000'); }); - $this->assertEquals('a6237d34f6e95f30d2fc91a46bd058e6', $img->checksum()); - } - - public function testEllipseImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->ellipse(12, 8, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('080d9dd92ebe22f976c3c703cba33510', $img->checksum()); - } - - public function testCircleImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->circle(12, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('c3bff06c20244ba14e898e39ea0efd76', $img->checksum()); - } - - public function testPolygonImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $points = [3, 3, 11, 11, 7, 13]; - $img->polygon($points, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e534ff90c8026f9317b99071fda01ed4', $img->checksum()); - } - - public function testResetImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->backup(); - $img->resize(30, 20); - $img->reset(); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - } - - public function testResetEmptyImage() - { - $img = $this->manager()->canvas(16, 16, '#0000ff'); - $img->backup(); - $img->resize(30, 20); - $img->fill('#ff0000'); - $img->reset(); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#0000ff', $img, 0, 0); - } - - public function testResetKeepTransparency() - { - $img = $this->manager()->make('tests/images/circle.png'); - $img->backup(); - $img->reset(); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testResetToNamed() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->backup('original'); - $img->resize(30, 20); - $img->backup('30x20'); - - // reset to original - $img->reset('original'); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - - // reset to 30x20 - // $img->reset('30x20'); - // $this->assertEquals(30, $img->getWidth()); - // $this->assertEquals(20, $img->getHeight()); - - // reset to original again - $img->reset('original'); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - } - - public function testLimitColors() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->limitColors(4); - $this->assertLessThanOrEqual(5, imagecolorstotal($img->getCore())); - } - - public function testLimitColorsKeepTransparency() - { - $img = $this->manager()->make('tests/images/star.png'); - $img->limitColors(16); - $this->assertLessThanOrEqual(17, imagecolorstotal($img->getCore())); - $this->assertTransparentPosition($img, 0, 0); - $this->assertColorAtPosition('#0c02b4', $img, 6, 12); - $this->assertColorAtPosition('#fcbe04', $img, 22, 24); - } - - public function testLimitColorsKeepTransparencyWithMatte() - { - $img = $this->manager()->make('tests/images/star.png'); - $img->limitColors(64, '#00ff00'); - $this->assertLessThanOrEqual(65, imagecolorstotal($img->getCore())); - $this->assertTransparentPosition($img, 0, 0); - $this->assertColorAtPosition('#04f204', $img, 12, 10); - $this->assertColorAtPosition('#e40214', $img, 16, 21); - } - - public function testLimitColorsNullWithMatte() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->limitColors(null, '#ff00ff'); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#ff00ff', $img, 0, 8); - $this->assertColorAtPosition('#ff00ff', $img, 15, 0); - } - - public function testPickColorFromTrueColor() - { - $img = $this->manager()->make('tests/images/star.png'); - $c = $img->pickColor(0, 0); - $this->assertEquals(255, $c[0]); - $this->assertEquals(255, $c[1]); - $this->assertEquals(255, $c[2]); - $this->assertEquals(0, $c[3]); - - $c = $img->pickColor(11, 11); - $this->assertEquals(34, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(160, $c[2]); - $this->assertEquals(0.46, $c[3]); - - $c = $img->pickColor(16, 16); - $this->assertEquals(231, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(18, $c[2]); - $this->assertEquals(1, $c[3]); - } - - public function testPickColorFromIndexed() - { - $img = $this->manager()->make('tests/images/tile.png'); - $c = $img->pickColor(0, 0); - $this->assertEquals(180, $c[0]); - $this->assertEquals(224, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(8, 8); - $this->assertEquals(68, $c[0]); - $this->assertEquals(81, $c[1]); - $this->assertEquals(96, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(0, 15); - $this->assertEquals(255, $c[0]); - $this->assertEquals(255, $c[1]); - $this->assertEquals(255, $c[2]); - $this->assertEquals(0, $c[3]); - } - - public function testPickColorFromPalette() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->limitColors(200); - - $c = $img->pickColor(0, 0); - $this->assertEquals(180, $c[0]); - $this->assertEquals(226, $c[1]); - $this->assertEquals(4, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(8, 8); - $this->assertEquals(68, $c[0]); - $this->assertEquals(82, $c[1]); - $this->assertEquals(100, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(0, 15); - $this->assertEquals(255, $c[0]); - $this->assertEquals(255, $c[1]); - $this->assertEquals(255, $c[2]); - $this->assertEquals(0, $c[3]); - } - - public function testInterlaceImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->interlace(); - $img->encode('png'); - $this->assertTrue((ord($img->encoded[28]) != '0')); - $img->interlace(false); - $img->encode('png'); - $this->assertFalse((ord($img->encoded[28]) != '0')); - } - - public function testGammaImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->gamma(1.6); - $this->assertColorAtPosition('#00c9f6', $img, 0, 0); - $this->assertColorAtPosition('#ffc308', $img, 24, 24); - } - - public function testBrightnessImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->brightness(35); - $this->assertColorAtPosition('#59ffff', $img, 0, 0); - $this->assertColorAtPosition('#ffff5a', $img, 24, 24); - } - - public function testContrastImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->contrast(35); - $this->assertColorAtPosition('#00d4ff', $img, 0, 0); - $this->assertColorAtPosition('#ffc500', $img, 24, 24); - } - - public function testColorizeImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->colorize(40, 25, -50); - $this->assertColorAtPosition('#66ee70', $img, 0, 0); - $this->assertColorAtPosition('#ffe600', $img, 24, 24); - } - - public function testTrimGradient() - { - $canvas = $this->manager()->make('tests/images/gradient.png'); - - $img = clone $canvas; - $img->trim(); - $this->assertEquals($img->getWidth(), 46); - $this->assertEquals($img->getHeight(), 46); - - $img = clone $canvas; - $img->trim(null, null, 10); - $this->assertEquals($img->getWidth(), 38); - $this->assertEquals($img->getHeight(), 38); - - $img = clone $canvas; - $img->trim(null, null, 20); - $this->assertEquals($img->getWidth(), 34); - $this->assertEquals($img->getHeight(), 34); - - $img = clone $canvas; - $img->trim(null, null, 30); - $this->assertEquals($img->getWidth(), 30); - $this->assertEquals($img->getHeight(), 30); - - $img = clone $canvas; - $img->trim(null, null, 40); - $this->assertEquals($img->getWidth(), 26); - $this->assertEquals($img->getHeight(), 26); - - $img = clone $canvas; - $img->trim(null, null, 50); - $this->assertEquals($img->getWidth(), 22); - $this->assertEquals($img->getHeight(), 22); - - $img = clone $canvas; - $img->trim(null, null, 60); - $this->assertEquals($img->getWidth(), 20); - $this->assertEquals($img->getHeight(), 20); - - $img = clone $canvas; - $img->trim(null, null, 70); - $this->assertEquals($img->getWidth(), 16); - $this->assertEquals($img->getHeight(), 16); - - $img = clone $canvas; - $img->trim(null, null, 80); - $this->assertEquals($img->getWidth(), 12); - $this->assertEquals($img->getHeight(), 12); - - $img = clone $canvas; - $img->trim(null, null, 90); - $this->assertEquals($img->getWidth(), 8); - $this->assertEquals($img->getHeight(), 8); - } - - public function testTrimOnlyLeftAndRight() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, ['left', 'right'], 60); - $this->assertEquals($img->getWidth(), 20); - $this->assertEquals($img->getHeight(), 50); - } - - public function testTrimOnlyTopAndBottom() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, ['top', 'bottom'], 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 20); - } - - public function testTrimOnlyTop() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, 'top', 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 35); - } - - public function testTrimOnlyBottom() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, 'top', 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 35); - } - - public function testTrimWithFeather() - { - $canvas = $this->manager()->make('tests/images/trim.png'); - - $img = clone $canvas; - $feather = 5; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - $img = clone $canvas; - $feather = 10; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - $img = clone $canvas; - $feather = 20; // must respect original dimensions of image - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 50); - - $img = clone $canvas; - $feather = -5; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - $img = clone $canvas; - $feather = -10; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - // trim only left and right with feather - $img = clone $canvas; - $feather = 10; - $img->trim(null, ['left', 'right'], null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 50); - - // trim only top and bottom with feather - $img = clone $canvas; - $feather = 10; - $img->trim(null, ['top', 'bottom'], null, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - - // trim with tolerance and feather - $canvas = $this->manager()->make('tests/images/gradient.png'); - - $img = clone $canvas; - $feather = 2; - $img->trim(null, null, 10, $feather); - $this->assertEquals($img->getWidth(), 38 + $feather * 2); - $this->assertEquals($img->getHeight(), 38 + $feather * 2); - - $img = clone $canvas; - $feather = 5; - $img->trim(null, null, 10, $feather); - $this->assertEquals($img->getWidth(), 38 + $feather * 2); - $this->assertEquals($img->getHeight(), 38 + $feather * 2); - - $img = clone $canvas; - $feather = 10; // should respect original dimensions - $img->trim(null, null, 20, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 50); - } - - public function testEncodeDefault() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode(); - $this->assertInternalType('resource', imagecreatefromstring($img->encoded)); - } - - public function testEncodeJpeg() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('jpg'); - $this->assertInternalType('resource', imagecreatefromstring($img->encoded)); - } - - public function testEncodeGif() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('gif'); - $this->assertInternalType('resource', imagecreatefromstring($img->encoded)); - } - - public function testEncodeWebp() - { - if (function_exists('imagewebp')) { - $img = $this->manager()->make('tests/images/trim.png'); - $data = (string) $img->encode('webp'); - $this->assertEquals('image/webp; charset=binary', $this->getMime($data)); - } - } - - public function testEncodeDataUrl() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('data-url'); - $this->assertEquals('data:image/png;base64', substr($img->encoded, 0, 21)); - } - - public function testExifReadAll() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif(); - $this->assertInternalType('array', $data); - $this->assertEquals(19, count($data)); - } - - public function testExifReadKey() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif('Artist'); - $this->assertInternalType('string', $data); - $this->assertEquals('Oliver Vogel', $data); - } - - public function testExifReadNotExistingKey() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif('xxx'); - $this->assertEquals(null, $data); - } - - public function testSaveImage() - { - $save_as = 'tests/tmp/foo.jpg'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as, 80); - $this->assertFileExists($save_as); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.jpg'); - $this->assertEquals($img->extension, 'jpg'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/jpeg'); - @unlink($save_as); - - $save_as = 'tests/tmp/foo.png'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.png'); - $this->assertEquals($img->extension, 'png'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/png'); - $this->assertFileExists($save_as); - @unlink($save_as); - - $save_as = 'tests/tmp/foo.jpg'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as, 0); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.jpg'); - $this->assertEquals($img->extension, 'jpg'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/jpeg'); - $this->assertFileExists($save_as); - @unlink($save_as); - } - - public function testSaveImageWithoutParameter() - { - $path = 'tests/tmp/bar.png'; - - // create temp. test image (red) - $img = $this->manager()->canvas(16, 16, '#ff0000'); - $img->save($path); - $img->destroy(); - - // open test image again - $img = $this->manager()->make($path); - $this->assertColorAtPosition('#ff0000', $img, 0, 0); - - // fill with green and save wthout paramater - $img->fill('#00ff00'); - $img->save(); - $img->destroy(); - - // re-open test image (should be green) - $img = $this->manager()->make($path); - $this->assertColorAtPosition('#00ff00', $img, 0, 0); - $img->destroy(); - - @unlink($path); - } - - public function testDestroy() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->backup(); - $img->destroy(); - // destroy should affect core - $this->assertEquals(get_resource_type($img->getCore()), 'Unknown'); - // destroy should affect backup - $this->assertEquals(get_resource_type($img->getBackup()), 'Unknown'); - } - - public function testStringConversion() - { - $img = $this->manager()->make('tests/images/trim.png'); - $value = strval($img); - $this->assertInternalType('string', $value); - } - - public function testFilter() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->filter(new \Intervention\Image\Filters\DemoFilter(10)); - $this->assertColorAtPosition('#818181', $img, 0, 0); - $this->assertColorAtPosition('#939393', $img, 18, 18); - $this->assertColorAtPosition('#939393', $img, 18, 18); - $this->assertColorAtPosition('#adadad', $img, 25, 25); - $this->assertColorAtPosition('#939393', $img, 35, 35); - } - - public function testCloneImageObject() - { - $img = $this->manager()->make('tests/images/trim.png'); - $cln = clone $img; - - // destroy original - $img->destroy(); - unset($img); - - // clone should be still intact - $this->assertInstanceOf('Intervention\Image\Image', $cln); - $this->assertInternalType('resource', $cln->getCore()); - } - - public function testGifConversionKeepsTransparency() - { - $save_as = 'tests/tmp/foo.gif'; - - // create gif image from transparent png - $img = $this->manager()->make('tests/images/star.png'); - $img->save($save_as); - - // new gif image should be transparent - $img = $this->manager()->make($save_as); - $this->assertTransparentPosition($img, 0, 0); - @unlink($save_as); - } - - private function assertColorAtPosition($color, $img, $x, $y) - { - $pick = $img->pickColor($x, $y, 'hex'); - $this->assertEquals($color, $pick); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - private function assertTransparentPosition($img, $x, $y, $transparent = 0) - { - // background should be transparent - $color = $img->pickColor($x, $y, 'array'); - $this->assertEquals($transparent, $color[3]); // alpha channel - } - - private function manager() - { - return new \Intervention\Image\ImageManager([ - 'driver' => 'gd' - ]); - } - - private function getMime($data) - { - $finfo = new finfo(FILEINFO_MIME); - return $finfo->buffer($data); - } -} diff --git a/tests/Geometry/PointTest.php b/tests/Geometry/PointTest.php new file mode 100644 index 000000000..f2d15b2e1 --- /dev/null +++ b/tests/Geometry/PointTest.php @@ -0,0 +1,82 @@ +assertInstanceOf(Point::class, $point); + $this->assertEquals(0, $point->getX()); + $this->assertEquals(0, $point->getY()); + } + + public function testConstructorWithParameters() + { + $point = new Point(40, 50); + $this->assertInstanceOf(Point::class, $point); + $this->assertEquals(40, $point->getX()); + $this->assertEquals(50, $point->getY()); + } + + public function testSetX() + { + $point = new Point(0, 0); + $point->setX(100); + $this->assertEquals(100, $point->getX()); + $this->assertEquals(0, $point->getY()); + } + + public function testSetY() + { + $point = new Point(0, 0); + $point->setY(100); + $this->assertEquals(0, $point->getX()); + $this->assertEquals(100, $point->getY()); + } + + public function testmoveX() + { + $point = new Point(50, 50); + $point->moveX(100); + $this->assertEquals(150, $point->getX()); + $this->assertEquals(50, $point->getY()); + } + + public function testmoveY() + { + $point = new Point(50, 50); + $point->moveY(100); + $this->assertEquals(50, $point->getX()); + $this->assertEquals(150, $point->getY()); + } + + public function testSetPosition() + { + $point = new Point(0, 0); + $point->setPosition(100, 200); + $this->assertEquals(100, $point->getX()); + $this->assertEquals(200, $point->getY()); + } + + public function testRotate() + { + $point = new Point(30, 0); + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(0, $point->getX()); + $this->assertEquals(30, $point->getY()); + + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(-30, $point->getX()); + $this->assertEquals(0, $point->getY()); + + $point = new Point(300, 200); + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(-200, $point->getX()); + $this->assertEquals(300, $point->getY()); + } +} diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php new file mode 100644 index 000000000..e3e9c348f --- /dev/null +++ b/tests/Geometry/SizeTest.php @@ -0,0 +1,101 @@ +assertInstanceOf(Size::class, $size); + $this->assertInstanceOf(Point::class, $size->getPivot()); + $this->assertEquals(300, $size->getWidth()); + $this->assertEquals(200, $size->getHeight()); + } + + public function testGetWidth() + { + $size = new Size(800, 600); + $this->assertEquals(800, $size->getWidth()); + } + + public function testGetHeight() + { + $size = new Size(800, 600); + $this->assertEquals(600, $size->getHeight()); + } + + public function testGetAspectRatio() + { + $size = new Size(800, 600); + $this->assertEquals(1.33333333333, $size->getAspectRatio()); + + $size = new Size(100, 100); + $this->assertEquals(1, $size->getAspectRatio()); + + $size = new Size(1920, 1080); + $this->assertEquals(1.777777777778, $size->getAspectRatio()); + } + + public function testFitsInto() + { + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(100, 100)); + $this->assertFalse($fits); + + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(1000, 100)); + $this->assertFalse($fits); + + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(100, 1000)); + $this->assertFalse($fits); + + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(800, 600)); + $this->assertTrue($fits); + + $box = new Size(800, 600); + $fits = $box->fitsInto(new Size(1000, 1000)); + $this->assertTrue($fits); + + $box = new Size(100, 100); + $fits = $box->fitsInto(new Size(800, 600)); + $this->assertTrue($fits); + + $box = new Size(100, 100); + $fits = $box->fitsInto(new Size(80, 60)); + $this->assertFalse($fits); + } + + public function testIsLandscape() + { + $box = new Size(100, 100); + $this->assertFalse($box->isLandscape()); + + $box = new Size(100, 200); + $this->assertFalse($box->isLandscape()); + + $box = new Size(300, 200); + $this->assertTrue($box->isLandscape()); + } + + public function testIsPortrait() + { + $box = new Size(100, 100); + $this->assertFalse($box->isPortrait()); + + $box = new Size(200, 100); + $this->assertFalse($box->isPortrait()); + + $box = new Size(200, 300); + $this->assertTrue($box->isPortrait()); + } +} diff --git a/tests/GetsizeCommandTest.php b/tests/GetsizeCommandTest.php deleted file mode 100644 index d67f3b7f5..000000000 --- a/tests/GetsizeCommandTest.php +++ /dev/null @@ -1,39 +0,0 @@ -shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new GetSizeGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInstanceOf('Intervention\Image\Size', $command->getOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagewidth')->with(); - $imagick->shouldReceive('getimageheight')->with(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GetSizeImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInstanceOf('Intervention\Image\Size', $command->getOutput()); - } -} diff --git a/tests/GreyscaleCommandTest.php b/tests/GreyscaleCommandTest.php deleted file mode 100644 index 84298199e..000000000 --- a/tests/GreyscaleCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new GreyscaleGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('modulateimage')->with(100, 0, 100)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GreyscaleImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/HeightenCommandTest.php b/tests/HeightenCommandTest.php deleted file mode 100644 index 4016ff7d5..000000000 --- a/tests/HeightenCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -aspectRatio(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new HeightenGd([200]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new HeightenImagick([200]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ImageManagerStaticTest.php b/tests/ImageManagerStaticTest.php deleted file mode 100644 index b4bb3f5da..000000000 --- a/tests/ImageManagerStaticTest.php +++ /dev/null @@ -1,36 +0,0 @@ -getManager(); - $this->assertInstanceOf('Intervention\Image\ImageManager', $m); - } - - public function testMake() - { - $manager = Mockery::mock('Intervention\Image\ImageManager'); - $manager->shouldReceive('make')->with('foo')->once(); - $managerStatic = new ImageManagerStatic($manager); - $managerStatic->make('foo'); - } - - public function testCanvas() - { - $manager = Mockery::mock('Intervention\Image\ImageManager'); - $manager->shouldReceive('canvas')->with(100, 100, null)->once(); - $managerStatic = new ImageManagerStatic($manager); - $managerStatic->canvas(100, 100); - } -} diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index 9312208a7..2bdd2c6e0 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -1,40 +1,46 @@ 'bar']); + $this->assertInstanceOf(ImageManager::class, $manager); + $this->assertEquals('gd', $manager->getConfig('driver')); + $this->assertEquals('bar', $manager->getConfig('foo')); } - public function testConstructor() + public function testConfigure() { - $config = ['driver' => 'foo', 'bar' => 'baz']; - $manager = new ImageManager($config); - $this->assertEquals('foo', $manager->config['driver']); - $this->assertEquals('baz', $manager->config['bar']); + $manager = new ImageManager(['foo' => 'bar']); + $manager->configure(['foo' => 'baz', 'driver' => 'foo']); + $this->assertEquals('foo', $manager->getConfig('driver')); + $this->assertEquals('baz', $manager->getConfig('foo')); } - public function testConfigure() + public function testGetConfig() { - $overwrite = ['driver' => 'none', 'bar' => 'none']; - $config = ['driver' => 'foo', 'bar' => 'baz']; - $manager = new ImageManager($overwrite); - $manager->configure($config); - $this->assertEquals('foo', $manager->config['driver']); - $this->assertEquals('baz', $manager->config['bar']); + $manager = new ImageManager(['foo' => 'bar']); + $this->assertEquals('gd', $manager->getConfig('driver')); + $this->assertEquals('bar', $manager->getConfig('foo')); } - public function testConfigureObject() + public function testCreateGd() { - $config = ['driver' => new Intervention\Image\Imagick\Driver()]; - $manager = new ImageManager($config); + $manager = new ImageManager(['driver' => 'gd']); + $image = $manager->create(5, 4); + $this->assertInstanceOf(ImageInterface::class, $image); + } - $image = $manager->make('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); - $this->assertInstanceOf('Intervention\Image\Image', $image); - $this->assertInstanceOf('Imagick', $image->getCore()); + public function testMakeGd() + { + $manager = new ImageManager(['driver' => 'gd']); + $image = $manager->make(__DIR__ . '/images/red.gif'); + $this->assertInstanceOf(ImageInterface::class, $image); } } diff --git a/tests/ImageTest.php b/tests/ImageTest.php deleted file mode 100644 index 870658cff..000000000 --- a/tests/ImageTest.php +++ /dev/null @@ -1,143 +0,0 @@ -getTestImage(); - $this->assertEquals('mock', $image->getCore()); - } - - public function testCommandCall() - { - $image = $this->getTestImage(); - $result = $image->test(1, 2, 3); - $this->assertEquals('mock', $result); - } - - public function testEncode() - { - $image = $this->getTestImage(); - $image->getDriver()->shouldReceive('encode')->with($image, 'png', 90)->once(); - $image->encode('png', 90); - } - - public function testSave() - { - $save_as = __DIR__.'/tmp/test.jpg'; - $image = $this->getTestImage(); - $image->getDriver()->shouldReceive('encode')->with($image, 'jpg', 85)->once()->andReturn('mock'); - $image = $image->save($save_as, 85); - $this->assertInstanceOf('\Intervention\Image\Image', $image); - $this->assertFileExists($save_as); - $this->assertEquals($image->basename, 'test.jpg'); - $this->assertEquals($image->extension, 'jpg'); - $this->assertEquals($image->filename, 'test'); - @unlink($save_as); - } - - public function testFormatSave() - { - $save_as = __DIR__.'/tmp/test'; - - $config = ['driver' => new Intervention\Image\Imagick\Driver()]; - $manager = new ImageManager($config); - - $image = $manager->make('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); - $this->assertInstanceOf('Intervention\Image\Image', $image); - $this->assertInstanceOf('Imagick', $image->getCore()); - - $gifCore = $image->getCore(); - $this->assertEquals($gifCore->getImageMimeType(), 'image/gif'); - $image->save($save_as, null, 'jpg'); - - $this->assertEquals(\mime_content_type($save_as), 'image/jpeg'); - @unlink($save_as); - } - - public function testIsEncoded() - { - $image = $this->getTestImage(); - $this->assertFalse($image->isEncoded()); - - $image->setEncoded('foo'); - $this->assertTrue($image->isEncoded()); - } - - public function testFilter() - { - $demoFilter = Mockery::mock('\Intervention\Image\Filters\DemoFilter', [15]); - $image = $this->getTestImage(); - $demoFilter->shouldReceive('applyFilter')->with($image)->once()->andReturn($image); - $image->filter($demoFilter); - } - - public function testMime() - { - $image = $this->getTestImage(); - $this->assertEquals('image/png', $image->mime()); - } - - /** - * @expectedException \Intervention\Image\Exception\RuntimeException - */ - public function testGetBackupWithoutBackuo() - { - $image = $this->getTestImage(); - $image->getBackup(); - } - - public function testSetGetBackup() - { - $image = $this->getTestImage(); - $image->setBackup('foo'); - $backup = $image->getBackup(); - $this->assertEquals('foo', $backup); - } - - public function testGetBackups() - { - $image = $this->getTestImage(); - $backups = $image->getBackups(); - $this->assertEquals([], $backups); - - $image = $this->getTestImage(); - $image->setBackup('foo'); - $image->setBackup('bar'); - $image->setBackup('baz'); - $backups = $image->getBackups(); - $this->assertEquals(['default' => 'baz'], $backups); - - $image = $this->getTestImage(); - $image->setBackup('foo', 'a'); - $image->setBackup('bar', 'b'); - $image->setBackup('baz', 'c'); - $backups = $image->getBackups(); - $this->assertEquals(['a' => 'foo', 'b' => 'bar', 'c' => 'baz'], $backups); - } - - private function getTestImage() - { - $size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $driver = Mockery::mock('\Intervention\Image\AbstractDriver'); - $command = Mockery::mock('\Intervention\Image\Commands\AbstractCommand'); - $command->shouldReceive('hasOutput')->andReturn(true); - $command->shouldReceive('getOutput')->andReturn('mock'); - $driver->shouldReceive('executeCommand')->andReturn($command); - $image = new Image($driver, 'mock'); - $image->mime = 'image/png'; - $image->dirname = './tmp'; - $image->basename = 'foo.png'; - - return $image; - } -} diff --git a/tests/ImagickColorTest.php b/tests/ImagickColorTest.php deleted file mode 100644 index c51ec4daa..000000000 --- a/tests/ImagickColorTest.php +++ /dev/null @@ -1,338 +0,0 @@ -pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_RED)->andReturn(0.956862745098); - $this->assertEquals(244, $c->getRedValue()); - - $c = new Color; - $c->pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_GREEN)->andReturn(0.0470588235294); - $this->assertEquals(12, $c->getGreenValue()); - - $c = new Color; - $c->pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_BLUE)->andReturn(0.0862745098039); - $this->assertEquals(22, $c->getBlueValue()); - - $c = new Color; - $c->pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_ALPHA)->andReturn(1); - $this->assertEquals(1, $c->getAlphaValue()); - - $c = new Color; - $c->pixel = Mockery::mock('ImagickPixel'); - $c->pixel->shouldReceive('getcolorvalue')->with(Imagick::COLOR_ALPHA)->andReturn(1); - $this->assertEquals(1, $c->getAlphaValue()); - } - - public function testConstructor() - { - $c = new Color; - $this->validateColor($c, 255, 255, 255, 0); - } - - public function testParseNull() - { - $c = new Color; - $c->parse(null); - $this->validateColor($c, 255, 255, 255, 0); - } - - public function testParseInteger() - { - $c = new Color; - $c->parse(16777215); - $this->validateColor($c, 255, 255, 255, 0); - - $c = new Color; - $c->parse(4294967295); - $this->validateColor($c, 255, 255, 255, 1); - } - - public function testParseArray() - { - $c = new Color; - $c->parse([181, 55, 23, 0.5]); - $this->validateColor($c, 181, 55, 23, 0.5); - } - - public function testParseHexString() - { - $c = new Color; - $c->parse('#b53717'); - $this->validateColor($c, 181, 55, 23, 1); - } - - public function testParseRgbaString() - { - $c = new Color; - $c->parse('rgba(181, 55, 23, 1)'); - $this->validateColor($c, 181, 55, 23, 1); - } - - public function testInitFromInteger() - { - $c = new Color; - $c->initFromInteger(0); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromInteger(2147483647); - $this->validateColor($c, 255, 255, 255, 0.5); - $c->initFromInteger(16777215); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromInteger(2130706432); - $this->validateColor($c, 0, 0, 0, 0.5); - $c->initFromInteger(867514135); - $this->validateColor($c, 181, 55, 23, 0.2); - } - - public function testInitFromArray() - { - $c = new Color; - $c->initFromArray([0, 0, 0, 0]); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromArray([0, 0, 0, 1]); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromArray([255, 255, 255, 1]); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromArray([255, 255, 255, 0]); - $this->validateColor($c, 255, 255, 255, 0); - $c->initFromArray([255, 255, 255, 0.5]); - $this->validateColor($c, 255, 255, 255, 0.5); - $c->initFromArray([0, 0, 0]); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromArray([255, 255, 255]); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromArray([181, 55, 23]); - $this->validateColor($c, 181, 55, 23, 1); - $c->initFromArray([181, 55, 23, 0.5]); - $this->validateColor($c, 181, 55, 23, 0.5); - } - - public function testInitFromHexString() - { - $c = new Color; - $c->initFromString('#cccccc'); - $this->validateColor($c, 204, 204, 204, 1); - $c->initFromString('#b53717'); - $this->validateColor($c, 181, 55, 23, 1); - $c->initFromString('ffffff'); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromString('ff00ff'); - $this->validateColor($c, 255, 0, 255, 1); - $c->initFromString('#000'); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromString('000'); - $this->validateColor($c, 0, 0, 0, 1); - } - - public function testInitFromRgbString() - { - $c = new Color; - $c->initFromString('rgb(1, 14, 144)'); - $this->validateColor($c, 1, 14, 144, 1); - $c->initFromString('rgb (255, 255, 255)'); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromString('rgb(0,0,0)'); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromString('rgba(0,0,0,0)'); - $this->validateColor($c, 0, 0, 0, 0); - $c->initFromString('rgba(0,0,0,0.5)'); - $this->validateColor($c, 0, 0, 0, 0.5); - $c->initFromString('rgba(255, 0, 0, 0.5)'); - $this->validateColor($c, 255, 0, 0, 0.5); - $c->initFromString('rgba(204, 204, 204, 0.9)'); - $this->validateColor($c, 204, 204, 204, 0.9); - } - - public function testInitFromRgb() - { - $c = new Color; - $c->initFromRgb(0, 0, 0); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromRgb(255, 255, 255); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromRgb(181, 55, 23); - $this->validateColor($c, 181, 55, 23, 1); - } - - public function testInitFromRgba() - { - $c = new Color; - $c->initFromRgba(0, 0, 0, 1); - $this->validateColor($c, 0, 0, 0, 1); - $c->initFromRgba(255, 255, 255, 1); - $this->validateColor($c, 255, 255, 255, 1); - $c->initFromRgba(181, 55, 23, 1); - $this->validateColor($c, 181, 55, 23, 1); - $c->initFromRgba(181, 55, 23, 0); - $this->validateColor($c, 181, 55, 23, 0); - $c->initFromRgba(181, 55, 23, 0.5); - $this->validateColor($c, 181, 55, 23, 0.5); - } - - public function testGetInt() - { - $c = new Color; - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 16777215); - - $c = new Color([255, 255, 255]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 4294967295); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 4294967295); - - $c = new Color([181, 55, 23, 0.2]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 867514135); - - $c = new Color([255, 255, 255, 0.5]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 2164260863); - - $c = new Color([181, 55, 23, 1]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 4290066199); - - $c = new Color([0, 0, 0, 0]); - $i = $c->getInt(); - $this->assertInternalType('int', $i); - $this->assertEquals($i, 0); - } - - public function testGetHex() - { - $c = new Color; - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'ffffff'); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'ffffff'); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getHex(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'b53717'); - - $c = new Color([0, 0, 0, 0]); - $i = $c->getHex('#'); - $this->assertInternalType('string', $i); - $this->assertEquals($i, '#000000'); - } - - public function testGetArray() - { - $c = new Color; - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [255, 255, 255, 0]); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [255, 255, 255, 1]); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [181, 55, 23, 0.5]); - - $c = new Color([0, 0, 0, 1]); - $i = $c->getArray(); - $this->assertInternalType('array', $i); - $this->assertEquals($i, [0, 0, 0, 1]); - } - - public function testGetRgba() - { - $c = new Color; - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 0.00)'); - - $c = new Color([255, 255, 255, 1]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 1.00)'); - - $c = new Color([181, 55, 23, 0.5]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(181, 55, 23, 0.50)'); - - $c = new Color([0, 0, 0, 1]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(0, 0, 0, 1.00)'); - - $c = new Color([255, 255, 255, 0.5]); - $i = $c->getRgba(); - $this->assertInternalType('string', $i); - $this->assertEquals($i, 'rgba(255, 255, 255, 0.50)'); - } - - public function testDiffers() - { - $c1 = new Color([0, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2)); - - $c1 = new Color([1, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(true, $c1->differs($c2)); - - $c1 = new Color([1, 0, 0]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2, 10)); - - $c1 = new Color([127, 127, 127]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(true, $c1->differs($c2, 49)); - - $c1 = new Color([127, 127, 127]); - $c2 = new Color([0, 0, 0]); - $this->assertEquals(false, $c1->differs($c2, 50)); - } - - /** - * @expectedException \Intervention\Image\Exception\NotReadableException - */ - public function testParseUnknown() - { - $c = new Color('xxxxxxxxxxxxxxxxxxxx'); - } - - private function validateColor($obj, $r, $g, $b, $a) - { - $this->assertInstanceOf('Intervention\Image\Imagick\Color', $obj); - $this->assertInstanceOf('ImagickPixel', $obj->pixel); - $this->assertEquals($r, round($obj->getRedValue(), 2)); - $this->assertEquals($g, round($obj->getGreenValue(), 2)); - $this->assertEquals($b, round($obj->getBlueValue(), 2)); - $this->assertEquals($a, round($obj->getAlphaValue(), 2)); - } -} diff --git a/tests/ImagickSystemTest.php b/tests/ImagickSystemTest.php deleted file mode 100644 index 25e961c6f..000000000 --- a/tests/ImagickSystemTest.php +++ /dev/null @@ -1,1650 +0,0 @@ -manager()->make('tests/images/circle.png'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - $this->assertEquals('tests/images', $img->dirname); - $this->assertEquals('circle.png', $img->basename); - $this->assertEquals('png', $img->extension); - $this->assertEquals('circle', $img->filename); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromString() - { - $str = file_get_contents('tests/images/circle.png'); - $img = $this->manager()->make($str); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromImagick() - { - $imagick = new \Imagick; - $imagick->readImage('tests/images/circle.png'); - $img = $this->manager()->make($imagick); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - } - - public function testMakeFromDataUrl() - { - $str = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'; - $img = $this->manager()->make($str); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromBase64() - { - $str = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3cRvs4UAAAAAElFTkSuQmCC'; - $img = $this->manager()->make($str); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - public function testMakeFromBase64WithNewlines() - { - $data = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+' . "\n" . - '9AAAAGElEQVQYlWM8c+bMfwYiABMxikYVUk8hAHWzA3' . "\n" . - 'cRvs4UAAAAAElFTkSuQmCC'; - - $img = $this->manager()->make($data); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertEquals('image/png', $img->mime); - } - - - public function testCanvas() - { - $img = $this->manager()->canvas(30, 20); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testCanvasWithSolidBackground() - { - $img = $this->manager()->canvas(30, 20, 'b53717'); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertEquals('#b53717', $img->pickColor(15, 15, 'hex')); - } - - public function testGetSize() - { - $img = $this->manager()->make('tests/images/tile.png'); - $size = $img->getSize(); - $this->assertInstanceOf('Intervention\Image\Size', $size); - $this->assertInternalType('int', $size->width); - $this->assertInternalType('int', $size->height); - $this->assertEquals(16, $size->width); - $this->assertEquals(16, $size->height); - } - - public function testResizeImage() - { - $img = $this->manager()->make('tests/images/circle.png'); - $img->resize(120, 150); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(120, $img->getWidth()); - $this->assertEquals(150, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testResizeImageOnlyWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(120, null); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(120, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 0, 15); - } - - public function testResizeImageOnlyHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(null, 150); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(150, $img->getHeight()); - $this->assertTransparentPosition($img, 15, 0); - } - - public function testResizeImageAutoHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(50, null, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertTransparentPosition($img, 30, 0); - } - - public function testResizeImageAutoWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(null, 50, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(50, $img->getHeight()); - $this->assertTransparentPosition($img, 30, 0); - } - - public function testResizeDominantWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(100, 120, function ($constraint) { $constraint->aspectRatio(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testResizeImagePreserveSimpleUpsizing() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resize(100, 100, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 15, 0); - } - - public function testWidenImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->widen(100); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testWidenImageWithConstraint() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->widen(100, function ($constraint) {$constraint->upsize();}); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 8, 0); - } - - public function testHeightenImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->heighten(100); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(100, $img->getWidth()); - $this->assertEquals(100, $img->getHeight()); - $this->assertTransparentPosition($img, 60, 0); - } - - public function testHeightenImageWithConstraint() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->heighten(100, function ($constraint) {$constraint->upsize();}); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInstanceOf('Imagick', $img->getCore()); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertTransparentPosition($img, 8, 0); - } - - public function testResizeCanvasCenter() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 5, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 5, 4); - } - - public function testResizeCanvasTopLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top-left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 8, 7); - } - - public function testResizeCanvasTop() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 5, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 5, 7); - } - - public function testResizeCanvasTopRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'top-right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 2, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 2, 7); - } - - public function testResizeCanvasLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 8, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 8, 4); - } - - public function testResizeCanvasRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 2, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 2, 4); - } - - public function testResizeCanvasBottomLeft() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom-left'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 8, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 8, 1); - } - - public function testResizeCanvasBottomRight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom-right'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 2, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 2, 1); - } - - public function testResizeCanvasBottom() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 10, 'bottom'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 1); - $this->assertColorAtPosition('#445160', $img, 5, 2); - $this->assertTransparentPosition($img, 0, 2); - $this->assertTransparentPosition($img, 5, 1); - } - - public function testResizeCanvasRelativeWithBackground() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(4, 4, 'center', true, '#ff00ff'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(20, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#ff00ff', $img, 0, 0); - $this->assertColorAtPosition('#ff00ff', $img, 19, 19); - $this->assertColorAtPosition('#b4e000', $img, 2, 9); - $this->assertColorAtPosition('#445160', $img, 10, 10); - $this->assertTransparentPosition($img, 2, 10); - $this->assertTransparentPosition($img, 10, 9); - } - - public function testResizeCanvasJustWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, null); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#445160', $img, 5, 8); - $this->assertTransparentPosition($img, 0, 8); - $this->assertTransparentPosition($img, 5, 7); - } - - public function testResizeCanvasJustHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(null, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#445160', $img, 8, 5); - $this->assertTransparentPosition($img, 0, 5); - $this->assertTransparentPosition($img, 8, 4); - } - - public function testResizeCanvasSmallerWidthLargerHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(10, 20); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(10, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 9); - $this->assertColorAtPosition('#445160', $img, 5, 10); - $this->assertTransparentPosition($img, 0, 10); - $this->assertTransparentPosition($img, 5, 9); - } - - public function testResizeCanvasLargerWidthSmallerHeight() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(20, 10); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(20, $img->getWidth()); - $this->assertEquals(10, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 2, 4); - $this->assertColorAtPosition('#445160', $img, 10, 5); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 2, 5); - $this->assertTransparentPosition($img, 10, 4); - } - - public function testResizeCanvasNegative() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(-4, -4); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(12, $img->getWidth()); - $this->assertEquals(12, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 5); - $this->assertColorAtPosition('#445160', $img, 6, 6); - $this->assertTransparentPosition($img, 0, 6); - $this->assertTransparentPosition($img, 6, 5); - } - - public function testResizeCanvasLargerHeightAutoWidth() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->resizeCanvas(null, 20, 'bottom-left', false, '#ff00ff'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - $this->assertColorAtPosition('#ff00ff', $img, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 4); - $this->assertColorAtPosition('#b4e000', $img, 0, 11); - $this->assertColorAtPosition('#445160', $img, 8, 12); - $this->assertTransparentPosition($img, 0, 12); - $this->assertTransparentPosition($img, 8, 11); - } - - public function testResizeCanvasBorderNonRelative() - { - $img = $this->manager()->canvas(1, 1, 'ff0000'); - $img->resizeCanvas(17, 17, 'center', false, '333333'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(17, $img->getWidth()); - $this->assertEquals(17, $img->getHeight()); - $this->assertColorAtPosition('#333333', $img, 0, 0); - $this->assertColorAtPosition('#333333', $img, 5, 5); - $this->assertColorAtPosition('#333333', $img, 7, 7); - $this->assertColorAtPosition('#ff0000', $img, 8, 8); - } - - public function testCropImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->crop(6, 6); // should be centered without pos. - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(6, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 3, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 3, 2); - } - - public function testCropImageWithPosition() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->crop(4, 4, 7, 7); // should be centered without pos. - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(4, $img->getWidth()); - $this->assertEquals(4, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 1, 1); - $this->assertTransparentPosition($img, 0, 1); - $this->assertTransparentPosition($img, 1, 0); - } - - public function testFitImageSquare() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fit(6); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(6, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 3, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 3, 2); - } - - public function testFitImageRectangle() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fit(12, 6); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(12, $img->getWidth()); - $this->assertEquals(6, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 2); - $this->assertColorAtPosition('#445160', $img, 6, 3); - $this->assertTransparentPosition($img, 0, 3); - $this->assertTransparentPosition($img, 6, 2); - } - - public function testFitImageWithConstraintUpsize() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->fit(300, 150, function ($constraint) {$constraint->upsize();}); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(50, $img->getWidth()); - $this->assertEquals(25, $img->getHeight()); - $this->assertColorAtPosition('#00aef0', $img, 0, 0); - $this->assertColorAtPosition('#afa94c', $img, 17, 0); - $this->assertColorAtPosition('#ffa601', $img, 24, 0); - } - - public function testFlipImageHorizontal() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->flip('h'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 8, 7); - $this->assertColorAtPosition('#445160', $img, 0, 8); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testFlipImageVertical() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->flip('v'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 7); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testRotateImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->rotate(90); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 7); - $this->assertTransparentPosition($img, 0, 7); - $this->assertTransparentPosition($img, 8, 8); - } - - public function testInsertImage() - { - $watermark = $this->manager()->canvas(16, 16, '#0000ff'); // create watermark - - // top-left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(0, 0, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(16, 16, 'hex')); - - // top-left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(9, 9, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(10, 10, 'hex')); - - // top anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(0, 0, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 15, 'hex')); - - // top anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(18, 10, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(31, 26, 'hex')); - - // top-right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(15, 0, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(31, 0, 'hex')); - - // top-right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'top-right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(6, 9, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(21, 25, 'hex')); - - // left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(15, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(0, 7, 'hex')); - - // left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(8, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(10, 7, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(25, 23, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(25, 8, 'hex')); - - // right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(31, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(15, 15, 'hex')); - - // right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(5, 8, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(22, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(21, 7, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(6, 8, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(21, 23, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(6, 23, 'hex')); - - // bottom-left anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-left', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(15, 31, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(0, 15, 'hex')); - - // bottom-left anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-left', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(10, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(9, 20, 'hex')); - - // bottom anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(8, 16, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 15, 'hex')); - - // bottom anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#ff0000', $img->pickColor(5, 8, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(23, 22, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(24, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(7, 6, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(8, 6, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 21, 'hex')); - $this->assertEquals('#0000ff', $img->pickColor(23, 6, 'hex')); - - // bottom-right anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-right', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(16, 16, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(15, 16, 'hex')); - - // bottom-right anchor coordinates - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'bottom-right', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(21, 21, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(22, 22, 'hex')); - - // center anchor - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'center', 0, 0); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(23, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 7, 'hex')); - - // center anchor coordinates / coordinates will be ignored for center - $img = $this->manager()->canvas(32, 32, '#ff0000'); // create canvas - $img->insert($watermark, 'center', 10, 10); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals($img->getWidth(), 32); - $this->assertEquals($img->getHeight(), 32); - $this->assertEquals('#0000ff', $img->pickColor(23, 23, 'hex')); - $this->assertEquals('#ff0000', $img->pickColor(8, 7, 'hex')); - } - - public function testInsertWithAlphaChannel() - { - $img = $this->manager()->canvas(50, 50, 'ff0000'); - $img->insert('tests/images/circle.png'); - $this->assertColorAtPosition('#ff0000', $img, 0, 0); - $this->assertColorAtPosition('#330000', $img, 30, 30); - } - - public function testInsertAfterResize() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->resize(16, 16)->insert('tests/images/tile.png'); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#ffa601', $img, 8, 7); - } - - public function testInsertImagick() - { - $imagick = new \Imagick; - $imagick->readImage('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($imagick); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testInsertBinary() - { - $data = file_get_contents('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($data); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testInsertInterventionImage() - { - $obj = $this->manager()->make('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->insert($obj); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertColorAtPosition('#b4e000', $img, 0, 7); - $this->assertColorAtPosition('#00aef0', $img, 0, 8); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 24, 24); - } - - public function testOpacity() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->opacity(50); - $checkColor = $img->pickColor(7, 7, 'array'); - $this->assertEquals($checkColor[0], 180); - $this->assertEquals($checkColor[1], 224); - $this->assertEquals($checkColor[2], 0); - $this->assertEquals($checkColor[3], 0.5); - $checkColor = $img->pickColor(8, 8, 'array'); - $this->assertEquals($checkColor[0], 68); - $this->assertEquals($checkColor[1], 81); - $this->assertEquals($checkColor[2], 96); - $this->assertEquals($checkColor[3], 0.5); - $this->assertTransparentPosition($img, 0, 11); - } - - public function testMaskImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->mask('tests/images/gradient.png'); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 18, 18); - $this->assertTransparentPosition($img, 23, 23); - $this->assertTransparentPosition($img, 30, 30); - $alpha = $img->pickColor(23, 24, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - $alpha = $img->pickColor(35, 25, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - $alpha = $img->pickColor(25, 42, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - } - - public function testMaskImageWithAlpha() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->mask('tests/images/star.png', true); - $this->assertTransparentPosition($img, 0, 0); - $this->assertTransparentPosition($img, 16, 16); - $this->assertTransparentPosition($img, 36, 36); - $this->assertTransparentPosition($img, 47, 47); - $alpha = $img->pickColor(18, 18, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - $alpha = $img->pickColor(22, 35, 'array'); - $this->assertLessThan(1, $alpha[3]); - $this->assertGreaterThanOrEqual(0, $alpha[3]); - } - - public function testPixelateImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->pixelate(20); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testGreyscaleImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->greyscale(); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertTransparentPosition($img, 8, 0); - $this->assertColorAtPosition('#707070', $img, 0, 0); - } - - public function testInvertImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->invert(); - $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertTransparentPosition($img, 8, 0); - $this->assertColorAtPosition('#4b1fff', $img, 0, 0); - } - - public function testBlurImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->blur(1); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testFillImageWithColor() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fill('b53717'); - $this->assertColorAtPosition('#b53717', $img, 0, 0); - $this->assertColorAtPosition('#b53717', $img, 15, 15); - } - - public function testFillImageWithColorAtPosition() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->fill('b53717', 0, 0); - $this->assertTransparentPosition($img, 0, 8); - $this->assertColorAtPosition('#b53717', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 15, 15); - } - - public function testFillImageWithImagick() - { - $imagick = new \Imagick; - $imagick->readImage('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($imagick, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testFillImageWithBinary() - { - $data = file_get_contents('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($data, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testFillImageWithInterventionImage() - { - $obj = $this->manager()->make('tests/images/tile.png'); - $img = $this->manager()->make('tests/images/trim.png'); - $img->fill($obj, 0, 0); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#00aef0', $img, 8, 7); - $this->assertColorAtPosition('#ffa601', $img, 20, 20); - } - - public function testPixelImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $coords = [[5, 5], [12, 12]]; - $img = $img->pixel('fdf5e4', $coords[0][0], $coords[0][1]); - $img = $img->pixel([255, 255, 255], $coords[1][0], $coords[1][1]); - $this->assertEquals('#fdf5e4', $img->pickColor($coords[0][0], $coords[0][1], 'hex')); - $this->assertEquals('#ffffff', $img->pickColor($coords[1][0], $coords[1][1], 'hex')); - } - - public function testRectangleImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->rectangle(5, 5, 11, 11, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('32ceca9759d1973dd461b39664df604d', $img->checksum()); - } - - public function testLineImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->line(0, 0, 15, 15, function ($draw) { $draw->color('#ff0000'); }); - $this->assertEquals('f5c585019bff361d91e2928b2ac2286b', $img->checksum()); - } - - public function testEllipseImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->ellipse(12, 8, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('9dc5bbec6d45868610c082a1d67640b5', $img->checksum()); - } - - public function testCircleImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img->circle(12, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('a433c7c1a842ef83e1cb45875371358c', $img->checksum()); - } - - public function testPolygonImage() - { - $img = $this->manager()->canvas(16, 16, 'ffffff'); - $points = [3, 3, 11, 11, 7, 13]; - $img->polygon($points, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e301afe179da858d441ad8fc0eb5490a', $img->checksum()); - } - - public function testResetImage() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->backup(); - $img->resize(30, 20); - $img->reset(); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - } - - public function testResetEmptyImage() - { - $img = $this->manager()->canvas(16, 16, '#0000ff'); - $img->backup(); - $img->resize(30, 20); - $img->fill('#ff0000'); - $img->reset(); - $this->assertInternalType('int', $img->getWidth()); - $this->assertInternalType('int', $img->getHeight()); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - $this->assertColorAtPosition('#0000ff', $img, 0, 0); - } - - public function testResetKeepTransparency() - { - $img = $this->manager()->make('tests/images/circle.png'); - $img->backup(); - $img->reset(); - $this->assertTransparentPosition($img, 0, 0); - } - - public function testResetToNamed() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->backup('original'); - $img->resize(30, 20); - $img->backup('30x20'); - - // reset to original - $img->reset('original'); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - - // reset to 30x20 - $img->reset('30x20'); - $this->assertEquals(30, $img->getWidth()); - $this->assertEquals(20, $img->getHeight()); - - // reset to original again - $img->reset('original'); - $this->assertEquals(16, $img->getWidth()); - $this->assertEquals(16, $img->getHeight()); - } - - public function testLimitColors() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->limitColors(4); - $this->assertLessThanOrEqual(5, $img->getCore()->getImageColors()); - } - - public function testLimitColorsKeepTransparency() - { - $img = $this->manager()->make('tests/images/star.png'); - $img->limitColors(16); - $this->assertLessThanOrEqual(17, $img->getCore()->getImageColors()); - $this->assertTransparentPosition($img, 0, 0); - $this->assertColorAtPosition('#680098', $img, 6, 12); - $this->assertColorAtPosition('#c2596a', $img, 22, 24); - } - - public function testLimitColorsKeepTransparencyWithMatte() - { - $img = $this->manager()->make('tests/images/star.png'); - $img->limitColors(32, '#00ff00'); - $this->assertLessThanOrEqual(33, $img->getCore()->getImageColors()); - $this->assertTransparentPosition($img, 0, 0); - $this->assertColorAtPosition('#00ff00', $img, 12, 10); - $this->assertColorAtPosition('#00ff00', $img, 22, 17); - $this->assertColorAtPosition('#e70012', $img, 16, 21); - } - - public function testLimitColorsNullWithMatte() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->limitColors(null, '#ff00ff'); - $this->assertColorAtPosition('#b4e000', $img, 0, 0); - $this->assertColorAtPosition('#445160', $img, 8, 8); - $this->assertColorAtPosition('#ff00ff', $img, 0, 8); - $this->assertColorAtPosition('#ff00ff', $img, 15, 0); - } - - public function testPickColorFromTrueColor() - { - $img = $this->manager()->make('tests/images/star.png'); - $c = $img->pickColor(0, 0); - $this->assertEquals(255, $c[0]); - $this->assertEquals(255, $c[1]); - $this->assertEquals(255, $c[2]); - $this->assertEquals(0, $c[3]); - - $c = $img->pickColor(11, 11); - $this->assertEquals(34, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(160, $c[2]); - $this->assertEquals(0.47, $c[3]); - - $c = $img->pickColor(16, 16); - $this->assertEquals(231, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(18, $c[2]); - $this->assertEquals(1, $c[3]); - } - - public function testPickColorFromIndexed() - { - $img = $this->manager()->make('tests/images/tile.png'); - $c = $img->pickColor(0, 0); - $this->assertEquals(180, $c[0]); - $this->assertEquals(224, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(8, 8); - $this->assertEquals(68, $c[0]); - $this->assertEquals(81, $c[1]); - $this->assertEquals(96, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(0, 15); - $this->assertEquals(0, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(0, $c[3]); - } - - public function testPickColorFromPalette() - { - $img = $this->manager()->make('tests/images/tile.png'); - $img->getCore()->quantizeImage(200, \Imagick::COLORSPACE_RGB, 0, false, false); - - $c = $img->pickColor(0, 0); - $this->assertEquals(180, $c[0]); - $this->assertEquals(224, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(8, 8); - $this->assertEquals(68, $c[0]); - $this->assertEquals(81, $c[1]); - $this->assertEquals(96, $c[2]); - $this->assertEquals(1, $c[3]); - - $c = $img->pickColor(0, 15); - $this->assertEquals(0, $c[0]); - $this->assertEquals(0, $c[1]); - $this->assertEquals(0, $c[2]); - $this->assertEquals(0, $c[3]); - } - - public function testInterlaceImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->interlace(); - $img->encode('png'); - $this->assertTrue((ord($img->encoded[28]) != '0')); - $img->interlace(false); - $img->encode('png'); - $this->assertFalse((ord($img->encoded[28]) != '0')); - } - - public function testGammaImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->gamma(1.6); - $this->assertColorAtPosition('#00c9f6', $img, 0, 0); - $this->assertColorAtPosition('#ffc308', $img, 24, 24); - } - - public function testBrightnessImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->brightness(35); - $this->assertColorAtPosition('#45ccff', $img, 0, 0); - $this->assertColorAtPosition('#ffc55b', $img, 24, 24); - } - - public function testContrastImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->contrast(35); - $this->assertColorAtPosition('#00feff', $img, 0, 0); - $this->assertColorAtPosition('#fffd04', $img, 24, 24); - } - - public function testColorizeImage() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->colorize(40, 25, -50); - $this->assertColorAtPosition('#00ece2', $img, 0, 0); - $this->assertColorAtPosition('#ffea00', $img, 24, 24); - } - - public function testTrimGradient() - { - $canvas = $this->manager()->make('tests/images/gradient.png'); - - $img = clone $canvas; - $img->trim(); - $this->assertEquals($img->getWidth(), 46); - $this->assertEquals($img->getHeight(), 46); - - $img = clone $canvas; - $img->trim(null, null, 10); - $this->assertEquals($img->getWidth(), 38); - $this->assertEquals($img->getHeight(), 38); - - $img = clone $canvas; - $img->trim(null, null, 20); - $this->assertEquals($img->getWidth(), 34); - $this->assertEquals($img->getHeight(), 34); - - $img = clone $canvas; - $img->trim(null, null, 30); - $this->assertEquals($img->getWidth(), 30); - $this->assertEquals($img->getHeight(), 30); - - $img = clone $canvas; - $img->trim(null, null, 40); - $this->assertEquals($img->getWidth(), 26); - $this->assertEquals($img->getHeight(), 26); - - $img = clone $canvas; - $img->trim(null, null, 50); - $this->assertEquals($img->getWidth(), 22); - $this->assertEquals($img->getHeight(), 22); - - $img = clone $canvas; - $img->trim(null, null, 60); - $this->assertEquals($img->getWidth(), 20); - $this->assertEquals($img->getHeight(), 20); - - $img = clone $canvas; - $img->trim(null, null, 70); - $this->assertEquals($img->getWidth(), 16); - $this->assertEquals($img->getHeight(), 16); - - $img = clone $canvas; - $img->trim(null, null, 80); - $this->assertEquals($img->getWidth(), 12); - $this->assertEquals($img->getHeight(), 12); - - $img = clone $canvas; - $img->trim(null, null, 90); - $this->assertEquals($img->getWidth(), 8); - $this->assertEquals($img->getHeight(), 8); - } - - public function testTrimOnlyLeftAndRight() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, ['left', 'right'], 60); - $this->assertEquals($img->getWidth(), 20); - $this->assertEquals($img->getHeight(), 50); - } - - public function testTrimOnlyTopAndBottom() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, ['top', 'bottom'], 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 20); - } - - public function testTrimOnlyTop() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, 'top', 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 35); - } - - public function testTrimOnlyBottom() - { - $img = $this->manager()->make('tests/images/gradient.png'); - $img->trim(null, 'top', 60); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 35); - } - - public function testTrimWithFeather() - { - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 5; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 10; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 20; // must respect original dimensions of image - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 50); - $img->destroy(); - - $img = $this->manager()->make('tests/images/trim.png'); - $feather = -5; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/trim.png'); - $feather = -10; - $img->trim(null, null, null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - // trim only left and right with feather - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 10; - $img->trim(null, ['left', 'right'], null, $feather); - $this->assertEquals($img->getWidth(), 28 + $feather * 2); - $this->assertEquals($img->getHeight(), 50); - $img->destroy(); - - // trim only top and bottom with feather - $img = $this->manager()->make('tests/images/trim.png'); - $feather = 10; - $img->trim(null, ['top', 'bottom'], null, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 28 + $feather * 2); - $img->destroy(); - - // trim with tolerance and feather - $img = $this->manager()->make('tests/images/gradient.png'); - $feather = 2; - $img->trim(null, null, 10, $feather); - $this->assertEquals($img->getWidth(), 38 + $feather * 2); - $this->assertEquals($img->getHeight(), 38 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/gradient.png'); - $feather = 5; - $img->trim(null, null, 10, $feather); - $this->assertEquals($img->getWidth(), 38 + $feather * 2); - $this->assertEquals($img->getHeight(), 38 + $feather * 2); - $img->destroy(); - - $img = $this->manager()->make('tests/images/gradient.png'); - $feather = 10; // should respect original dimensions - $img->trim(null, null, 20, $feather); - $this->assertEquals($img->getWidth(), 50); - $this->assertEquals($img->getHeight(), 50); - $img->destroy(); - } - - public function testEncodeDefault() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode(); - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $img->encoded); - $this->assertEquals('image/png', $mime); - } - - public function testEncodeJpeg() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('jpg'); - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $img->encoded); - $this->assertEquals('image/jpeg', $mime); - } - - public function testEncodeGif() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('gif'); - $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $img->encoded); - $this->assertEquals('image/gif', $mime); - } - - public function testEncodeDataUrl() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->encode('data-url'); - $this->assertEquals('data:image/png;base64', substr($img->encoded, 0, 21)); - } - - public function testExifReadAll() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif(); - $this->assertInternalType('array', $data); - $this->assertGreaterThanOrEqual(13, count($data)); - } - - public function testExifReadKey() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif('Artist'); - $this->assertInternalType('string', $data); - $this->assertEquals('Oliver Vogel', $data); - } - - public function testExifReadNotExistingKey() - { - $img = $this->manager()->make('tests/images/exif.jpg'); - $data = $img->exif('xxx'); - $this->assertEquals(null, $data); - } - - public function testSaveImage() - { - $save_as = 'tests/tmp/foo.jpg'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as, 80); - $this->assertFileExists($save_as); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.jpg'); - $this->assertEquals($img->extension, 'jpg'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/jpeg'); - @unlink($save_as); - - $save_as = 'tests/tmp/foo.png'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.png'); - $this->assertEquals($img->extension, 'png'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/png'); - $this->assertFileExists($save_as); - @unlink($save_as); - - $save_as = 'tests/tmp/foo.jpg'; - $img = $this->manager()->make('tests/images/trim.png'); - $img->save($save_as, 0); - $this->assertEquals($img->dirname, 'tests/tmp'); - $this->assertEquals($img->basename, 'foo.jpg'); - $this->assertEquals($img->extension, 'jpg'); - $this->assertEquals($img->filename, 'foo'); - $this->assertEquals($img->mime, 'image/jpeg'); - $this->assertFileExists($save_as); - @unlink($save_as); - } - - public function testSaveImageWithoutParameter() - { - $path = 'tests/tmp/bar.png'; - - // create temp. test image (red) - $img = $this->manager()->canvas(16, 16, '#ff0000'); - $img->save($path); - $img->destroy(); - - // open test image again - $img = $this->manager()->make($path); - $this->assertColorAtPosition('#ff0000', $img, 0, 0); - - // fill with green and save wthout paramater - $img->fill('#00ff00'); - $img->save(); - $img->destroy(); - - // re-open test image (should be green) - $img = $this->manager()->make($path); - $this->assertColorAtPosition('#00ff00', $img, 0, 0); - $img->destroy(); - - @unlink($path); - } - - /** - * @expectedException ImagickException - */ - public function testDestroy() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->destroy(); - $img->getCore()->getImageWidth(); // try to get width (should throw exception) - } - - /** - * @expectedException Exception - */ - public function testDestroyWithBackup() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->backup(); - $img->destroy(); - $img->getBackup()->getImageWidth(); // try to get width (should throw exception) - } - - public function testStringConversion() - { - $img = $this->manager()->make('tests/images/trim.png'); - $value = strval($img); - $this->assertInternalType('string', $value); - } - - public function testFilter() - { - $img = $this->manager()->make('tests/images/trim.png'); - $img->filter(new \Intervention\Image\Filters\DemoFilter(10)); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - public function testCloneImageObject() - { - $img = $this->manager()->make('tests/images/trim.png'); - $cln = clone $img; - - // destroy original - $img->destroy(); - unset($img); - - // clone should be still intact - $this->assertInstanceOf('Intervention\Image\Image', $cln); - $this->assertInstanceOf('Imagick', $cln->getCore()); - } - - public function testGifConversionKeepsTransparency() - { - $save_as = 'tests/tmp/foo.gif'; - - // create gif image from transparent png - $img = $this->manager()->make('tests/images/star.png'); - $img->save($save_as); - - // new gif image should be transparent - $img = $this->manager()->make($save_as); - $this->assertTransparentPosition($img, 0, 0); - @unlink($save_as); - } - - private function assertColorAtPosition($color, $img, $x, $y) - { - $pick = $img->pickColor($x, $y, 'hex'); - $this->assertEquals($color, $pick); - $this->assertInstanceOf('Intervention\Image\Image', $img); - } - - private function assertTransparentPosition($img, $x, $y, $transparent = 0) - { - // background should be transparent - $color = $img->pickColor($x, $y, 'array'); - $this->assertEquals($transparent, $color[3]); // alpha channel - } - - private function manager() - { - return new \Intervention\Image\ImageManager([ - 'driver' => 'imagick' - ]); - } -} diff --git a/tests/InsertCommandTest.php b/tests/InsertCommandTest.php deleted file mode 100644 index fa0b521c4..000000000 --- a/tests/InsertCommandTest.php +++ /dev/null @@ -1,67 +0,0 @@ -shouldReceive('align')->with('center', 10, 20)->once()->andReturn($image_size); - $watermark_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $watermark_size->shouldReceive('align')->with('center')->once()->andReturn($watermark_size); - $image_size->shouldReceive('relativePosition')->with($watermark_size)->once()->andReturn($position); - - $path = __DIR__.'/images/test.jpg'; - $resource = imagecreatefromjpeg($path); - $watermark = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('init')->with($path)->once()->andReturn($watermark); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); - $watermark->shouldReceive('getCore')->once()->andReturn($resource); - - $command = new InsertGd([$path, 'center', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $position = Mockery::mock('\Intervention\Image\Point', [10, 20]); - - $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $image_size->shouldReceive('align')->with('center', 10, 20)->once()->andReturn($image_size); - $watermark_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $watermark_size->shouldReceive('align')->with('center')->once()->andReturn($watermark_size); - $image_size->shouldReceive('relativePosition')->with($watermark_size)->once()->andReturn($position); - - $path = __DIR__.'/images/test.jpg'; - $watermark = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('init')->with($path)->once()->andReturn($watermark); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('compositeimage')->with($imagick, \Imagick::COMPOSITE_DEFAULT, 10, 20)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); - $watermark->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InsertImagick([$path, 'center', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/InterlaceCommandTest.php b/tests/InterlaceCommandTest.php deleted file mode 100644 index 907bc2cbe..000000000 --- a/tests/InterlaceCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new InterlaceGd([true]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('setinterlacescheme')->with(\Imagick::INTERLACE_LINE)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InterlaceImagick([true]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/InvertCommandTest.php b/tests/InvertCommandTest.php deleted file mode 100644 index 3cad4ddaa..000000000 --- a/tests/InvertCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new InvertGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('negateimage')->with(false)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InvertImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/IptcCommandTest.php b/tests/IptcCommandTest.php deleted file mode 100644 index 27c1ef34d..000000000 --- a/tests/IptcCommandTest.php +++ /dev/null @@ -1,72 +0,0 @@ -dirname = __DIR__.'/images'; - $image->basename = 'iptc.jpg'; - $command = new IptcCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - } - - public function testFetchDefined() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new IptcCommand(['AuthorByline']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - - public function testFetchNonExisting() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new IptcCommand(['xxx']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - - public function testFetchFromPng() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'star.png'; - $command = new IptcCommand(['Orientation']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testReturnNullOnIptcReadFail() - { - $image = Mockery::mock('Intervention\Image\Image'); - $command = new IptcCommand(['Orientation']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } -} diff --git a/tests/LimitColorsCommandTest.php b/tests/LimitColorsCommandTest.php deleted file mode 100644 index e76d57a4c..000000000 --- a/tests/LimitColorsCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new LimitColorsGd([16]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $size = Mockery::mock('\Intervention\Image\Size', [32, 32]); - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('separateimagechannel')->with(\Imagick::CHANNEL_ALPHA)->times(2); - $imagick->shouldReceive('transparentpaintimage')->with('#ffffff', 0, 0, false)->once(); - $imagick->shouldReceive('negateimage')->with(false)->once(); - $imagick->shouldReceive('quantizeimage')->with(16, \Imagick::COLORSPACE_RGB, 0, false, false)->once(); - $imagick->shouldReceive('compositeimage')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new LimitColorsImagick([16]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/LineCommandTest.php b/tests/LineCommandTest.php deleted file mode 100644 index 2dadb2a00..000000000 --- a/tests/LineCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new LineCommand([10, 15, 100, 150]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new LineCommand([10, 15, 100, 150]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/LineShapeTest.php b/tests/LineShapeTest.php deleted file mode 100644 index b95f71bf1..000000000 --- a/tests/LineShapeTest.php +++ /dev/null @@ -1,50 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\LineShape', $line); - $this->assertEquals(10, $line->x); - $this->assertEquals(15, $line->y); - - // imagick - $line = new LineImagick(10, 15); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\LineShape', $line); - $this->assertEquals(10, $line->x); - $this->assertEquals(15, $line->y); - } - - public function testApplyToImage() - { - // gd - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $line = new LineGd(10, 15); - $result = $line->applyToImage($image, 100, 200); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\LineShape', $line); - $this->assertTrue($result); - - // imagick - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $line = new LineImagick(10, 15); - $result = $line->applyToImage($image, 100, 200); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\LineShape', $line); - $this->assertTrue($result); - } -} diff --git a/tests/MaskCommandTest.php b/tests/MaskCommandTest.php deleted file mode 100644 index 6330670a6..000000000 --- a/tests/MaskCommandTest.php +++ /dev/null @@ -1,68 +0,0 @@ -shouldReceive('getSize')->once()->andReturn($mask_size); - $mask_image->shouldReceive('pickColor')->andReturn([0,0,0,0]); - - $canvas_image = Mockery::mock('Intervention\Image\Image'); - $canvas_core = imagecreatetruecolor(32, 32); - $canvas_image->shouldReceive('getCore')->times(2)->andReturn($canvas_core); - $canvas_image->shouldReceive('pixel'); - - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(32, 32, [0,0,0,0])->once()->andReturn($canvas_image); - $driver->shouldReceive('init')->with($mask_path)->once()->andReturn($mask_image); - - $image_size = Mockery::mock('Intervention\Image\Size', [32, 32]); - $image_core = imagecreatefrompng(__DIR__.'/images/trim.png'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getDriver')->times(2)->andReturn($driver); - $image->shouldReceive('pickColor')->andReturn([0,0,0,0]); - $image->shouldReceive('setCore')->with($canvas_core)->once(); - - $command = new MaskGd([$mask_path, true]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $mask_core = Mockery::mock('Imagick'); - $mask_path = __DIR__.'images/star.png'; - $mask_image = Mockery::mock('Intervention\Image\Image'); - $mask_image->shouldReceive('getCore')->once()->andReturn($mask_core); - $mask_size = Mockery::mock('Intervention\Image\Size', [32, 32]); - $mask_image->shouldReceive('getSize')->once()->andReturn($mask_size); - - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('init')->with($mask_path)->once()->andReturn($mask_image); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('setimagematte')->with(true)->once(); - $imagick->shouldReceive('compositeimage')->with($mask_core, \Imagick::COMPOSITE_DSTIN, 0, 0)->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image_size = Mockery::mock('Intervention\Image\Size', [32, 32]); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new MaskImagick([$mask_path, true]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/OpacityCommandTest.php b/tests/OpacityCommandTest.php deleted file mode 100644 index aac049714..000000000 --- a/tests/OpacityCommandTest.php +++ /dev/null @@ -1,44 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($mask_core); - - $resource = imagecreatefrompng(__DIR__.'/images/trim.png'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(32, 32, 'rgba(0, 0, 0, 0.5)')->andReturn($mask); - - $size = Mockery::mock('\Intervention\Image\Size', [32, 32]); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('mask')->with($mask_core, true)->once(); - $command = new OpacityGd([50]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('evaluateimage')->with(\Imagick::EVALUATE_DIVIDE, 2, \Imagick::CHANNEL_ALPHA)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new OpacityImagick([50]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/OrientateCommandTest.php b/tests/OrientateCommandTest.php deleted file mode 100644 index 884fcbb1c..000000000 --- a/tests/OrientateCommandTest.php +++ /dev/null @@ -1,94 +0,0 @@ -shouldReceive('exif')->with('Orientation')->once()->andReturn(2); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationThree() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(3); - $image->shouldReceive('rotate')->with(180)->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationFour() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(4); - $image->shouldReceive('rotate')->with(180)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationFive() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(5); - $image->shouldReceive('rotate')->with(270)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationSix() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(6); - $image->shouldReceive('rotate')->with(270)->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationSeven() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(7); - $image->shouldReceive('rotate')->with(90)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationEight() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(8); - $image->shouldReceive('rotate')->with(90)->once(); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationNoExifData() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(null); - $command = new OrientateCommand([]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PickColorCommandTest.php b/tests/PickColorCommandTest.php deleted file mode 100644 index fecc676f7..000000000 --- a/tests/PickColorCommandTest.php +++ /dev/null @@ -1,67 +0,0 @@ -shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new PickColorGd([1, 2]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - $this->assertEquals(4, count($command->getOutput())); - } - - public function testGdWithFormat() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new PickColorGd([1, 2, 'hex']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('string', $command->getOutput()); - $this->assertEquals('#ffffff', $command->getOutput()); - } - - public function testImagickWithCoordinates() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->with(1, 2)->andReturn(new ImagickPixel); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PickColorImagick([1, 2]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - $this->assertEquals(4, count($command->getOutput())); - } - - public function testImagickWithFormat() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->with(1, 2)->andReturn(new ImagickPixel('#ff0000')); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PickColorImagick([1, 2, 'hex']); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('string', $command->getOutput()); - $this->assertEquals('#ff0000', $command->getOutput()); - } -} diff --git a/tests/PixelCommandTest.php b/tests/PixelCommandTest.php deleted file mode 100644 index ad1681d72..000000000 --- a/tests/PixelCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new PixelGd(['#b53717', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('drawimage')->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PixelImagick(['#b53717', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PixelateCommandTest.php b/tests/PixelateCommandTest.php deleted file mode 100644 index 29eba75f5..000000000 --- a/tests/PixelateCommandTest.php +++ /dev/null @@ -1,37 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new PixelateGd([10]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(80, 60)->once()->andReturn(true); - $imagick->shouldReceive('scaleimage')->with(800, 600)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($imagick); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new PixelateImagick([10]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PointTest.php b/tests/PointTest.php deleted file mode 100644 index 816c1b134..000000000 --- a/tests/PointTest.php +++ /dev/null @@ -1,47 +0,0 @@ -assertInstanceOf('Intervention\Image\Point', $point); - $this->assertEquals(0, $point->x); - $this->assertEquals(0, $point->y); - } - - public function testConstructorWithParameters() - { - $point = new Point(40, 50); - $this->assertInstanceOf('Intervention\Image\Point', $point); - $this->assertEquals(40, $point->x); - $this->assertEquals(50, $point->y); - } - - public function testSetX() - { - $point = new Point(0, 0); - $point->setX(100); - $this->assertEquals(100, $point->x); - $this->assertEquals(0, $point->y); - } - - public function testSetY() - { - $point = new Point(0, 0); - $point->setY(100); - $this->assertEquals(0, $point->x); - $this->assertEquals(100, $point->y); - } - - public function testSetPosition() - { - $point = new Point(0, 0); - $point->setPosition(100, 200); - $this->assertEquals(100, $point->x); - $this->assertEquals(200, $point->y); - } -} diff --git a/tests/PolygonCommandTest.php b/tests/PolygonCommandTest.php deleted file mode 100644 index 1580d8bca..000000000 --- a/tests/PolygonCommandTest.php +++ /dev/null @@ -1,45 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new PolygonCommand([$points]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $points = [1, 2, 3, 4, 5, 6]; - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new PolygonCommand([$points]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/PolygonShapeTest.php b/tests/PolygonShapeTest.php deleted file mode 100644 index be0001c46..000000000 --- a/tests/PolygonShapeTest.php +++ /dev/null @@ -1,57 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); - $this->assertEquals([1, 2, 3, 4, 5, 6], $polygon->points); - - } - - public function testGdApplyToImage() - { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $polygon = new PolygonGd([1, 2, 3, 4, 5, 6]); - $result = $polygon->applyToImage($image); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); - $this->assertTrue($result); - } - - public function testImagickConstructor() - { - $polygon = new PolygonImagick([1, 2, 3, 4, 5, 6]); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\PolygonShape', $polygon); - $this->assertEquals([ - ['x' => 1, 'y' => 2], - ['x' => 3, 'y' => 4], - ['x' => 5, 'y' => 6]], - $polygon->points); - - } - - public function testImagickApplyToImage() - { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $polygon = new PolygonImagick([1, 2, 3, 4, 5, 6]); - $result = $polygon->applyToImage($image); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\PolygonShape', $polygon); - $this->assertTrue($result); - } - -} diff --git a/tests/PsrResponseCommandTest.php b/tests/PsrResponseCommandTest.php deleted file mode 100644 index f7753f04a..000000000 --- a/tests/PsrResponseCommandTest.php +++ /dev/null @@ -1,58 +0,0 @@ -'; - - $image = Mockery::mock('Intervention\Image\Image'); - $stream = \GuzzleHttp\Psr7\stream_for($encodedContent); - - $image->shouldReceive('stream') - ->with('jpg', 87) - ->once() - ->andReturn($stream); - - $image->shouldReceive('getEncoded') - ->twice() - ->andReturn($encodedContent); - - $command = new PsrResponseCommand(['jpg', 87]); - $result = $command->execute($image); - - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - - $output = $command->getOutput(); - - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $output); - - /** - * @var \Psr\Http\Message\ResponseInterface $output - */ - $this->assertEquals($stream, $output->getBody()); - $this->assertEquals($encodedContent, (string)$output->getBody()); - - $this->assertTrue($output->hasHeader('Content-Type')); - $this->assertTrue($output->hasHeader('Content-Length')); - - $this->assertTrue(in_array($output->getHeaderLine('Content-Type'), [ - 'application/xml', - 'text/xml', - ])); - - $this->assertEquals( - strlen($encodedContent), - $output->getHeaderLine('Content-Length') - ); - } -} diff --git a/tests/RectangleCommandTest.php b/tests/RectangleCommandTest.php deleted file mode 100644 index 3def6859c..000000000 --- a/tests/RectangleCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new RectangleCommand([10, 15, 100, 150]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - - $command = new RectangleCommand([10, 15, 100, 150]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/RectangleShapeTest.php b/tests/RectangleShapeTest.php deleted file mode 100644 index d4d7b2a0e..000000000 --- a/tests/RectangleShapeTest.php +++ /dev/null @@ -1,54 +0,0 @@ -assertInstanceOf('Intervention\Image\Gd\Shapes\RectangleShape', $rectangle); - $this->assertEquals(10, $rectangle->x1); - $this->assertEquals(15, $rectangle->y1); - $this->assertEquals(100, $rectangle->x2); - $this->assertEquals(150, $rectangle->y2); - - // imagick - $rectangle = new RectangleImagick(10, 15, 100, 150); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\RectangleShape', $rectangle); - $this->assertEquals(10, $rectangle->x1); - $this->assertEquals(15, $rectangle->y1); - $this->assertEquals(100, $rectangle->x2); - $this->assertEquals(150, $rectangle->y2); - } - - public function testApplyToImage() - { - // gd - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $rectangle = new RectangleGd(10, 15, 100, 150); - $result = $rectangle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\RectangleShape', $rectangle); - $this->assertTrue($result); - - // imagick - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); - $rectangle = new RectangleImagick(10, 15, 100, 150); - $result = $rectangle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\RectangleShape', $rectangle); - $this->assertTrue($result); - } -} diff --git a/tests/ResetCommandTest.php b/tests/ResetCommandTest.php deleted file mode 100644 index 29a545a23..000000000 --- a/tests/ResetCommandTest.php +++ /dev/null @@ -1,71 +0,0 @@ -shouldReceive('cloneCore')->with($resource)->once()->andReturn($resource); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->andReturn($resource); - $command = new ResetGd([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdWithName() - { - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('cloneCore')->with($resource)->once()->andReturn($resource); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->withArgs(['fooBackup'])->andReturn($resource); - $command = new ResetGd(['fooBackup']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithoutName() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->andReturn($imagick); - $command = new ResetImagick([]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithName() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->withArgs(['fooBackup'])->andReturn($imagick); - $command = new ResetImagick(['fooBackup']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ResizeCanvasCommandTest.php b/tests/ResizeCanvasCommandTest.php deleted file mode 100644 index fb6480a1e..000000000 --- a/tests/ResizeCanvasCommandTest.php +++ /dev/null @@ -1,80 +0,0 @@ -shouldReceive('align')->with('center')->andReturn($canvas_size); - $canvas_size->shouldReceive('relativePosition')->andReturn($canvas_pos); - $image_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); - $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $image_size->shouldReceive('align')->with('center')->andReturn($image_size); - $image_size->shouldReceive('relativePosition')->andReturn($image_pos); - $canvas = Mockery::mock('\Intervention\Image\Image'); - $canvas->shouldReceive('getCore')->times(5)->andReturn($resource); - $canvas->shouldReceive('getSize')->andReturn($canvas_size); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(820, 640, '#b53717')->once()->andReturn($canvas); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new ResizeCanvasGd([20, 40, 'center', true, '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $canvas_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); - $canvas_size = Mockery::mock('\Intervention\Image\Size', [820, 640]); - $canvas_size->shouldReceive('align')->with('center')->andReturn($canvas_size); - $canvas_size->shouldReceive('relativePosition')->andReturn($canvas_pos); - $image_pos = Mockery::mock('\Intervention\Image\Point', [0, 0]); - $image_size = Mockery::mock('\Intervention\Image\Size', [800, 600]); - $image_size->shouldReceive('align')->with('center')->andReturn($image_size); - $image_size->shouldReceive('relativePosition')->andReturn($image_pos); - $canvas = Mockery::mock('\Intervention\Image\Image'); - - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 600, 0, 0)->once(); - $imagick->shouldReceive('compositeimage')->with($imagick, 40, 0, 0)->once(); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); - $imagick->shouldReceive('drawimage')->once(); - $imagick->shouldReceive('transparentpaintimage')->once(); - $imagick->shouldReceive('getimagecolorspace')->once(); - $imagick->shouldReceive('setimagecolorspace')->once(); - - $canvas->shouldReceive('getCore')->times(6)->andReturn($imagick); - $canvas->shouldReceive('getSize')->andReturn($canvas_size); - $canvas->shouldReceive('pickColor')->with(0, 0, 'hex')->once()->andReturn('#000000'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(820, 640, '#b53717')->once()->andReturn($canvas); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $image->shouldReceive('setCore')->once(); - $command = new ResizeCanvasImagick([20, 40, 'center', true, '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } - -} diff --git a/tests/ResizeCommandTest.php b/tests/ResizeCommandTest.php deleted file mode 100644 index 130ea4b2a..000000000 --- a/tests/ResizeCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -upsize(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new ResizeCommandGd([300, 200, $callback]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new ResizeCommandImagick([300, 200, $callback]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php deleted file mode 100644 index 6a10b2c63..000000000 --- a/tests/ResponseTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertInstanceOf('\Intervention\Image\Response', $response); - $this->assertInstanceOf('\Intervention\Image\Image', $response->image); - } - - public function testConstructorWithParameters() - { - $image = Mockery::mock('\Intervention\Image\Image'); - $response = new Response($image, 'jpg', 75); - $this->assertInstanceOf('\Intervention\Image\Response', $response); - $this->assertInstanceOf('\Intervention\Image\Image', $response->image); - $this->assertEquals('jpg', $response->format); - $this->assertEquals(75, $response->quality); - } -} diff --git a/tests/RotateCommandTest.php b/tests/RotateCommandTest.php deleted file mode 100644 index 48facd163..000000000 --- a/tests/RotateCommandTest.php +++ /dev/null @@ -1,48 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once()->andReturn($resource); - $command = new RotateGd([45, '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $pixel = Mockery::mock('ImagickPixel', ['#b53717']); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('rotateimage')->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new RotateImagick([45, '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithLargeRotation() - { - $rotation = 45; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('rotateimage')->with(Mockery::type('object'), -$rotation)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new RotateImagick([$rotation + (360 * 1000), '#b53717']); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/SharpenCommandTest.php b/tests/SharpenCommandTest.php deleted file mode 100644 index ed187d879..000000000 --- a/tests/SharpenCommandTest.php +++ /dev/null @@ -1,34 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new SharpenGd([50]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('unsharpmaskimage')->with(1, 1, 8, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new SharpenImagick([50]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/SizeTest.php b/tests/SizeTest.php deleted file mode 100644 index 35ced2329..000000000 --- a/tests/SizeTest.php +++ /dev/null @@ -1,436 +0,0 @@ -assertInstanceOf('Intervention\Image\Size', $size); - $this->assertInstanceOf('Intervention\Image\Point', $size->pivot); - $this->assertEquals(1, $size->width); - $this->assertEquals(1, $size->height); - } - - public function testConstructorWithCoordinates() - { - $pivot = Mockery::mock('Intervention\Image\Point'); - $size = new Size(300, 200, $pivot); - $this->assertInstanceOf('Intervention\Image\Size', $size); - $this->assertInstanceOf('Intervention\Image\Point', $size->pivot); - $this->assertEquals(300, $size->width); - $this->assertEquals(200, $size->height); - } - - public function testGetWidth() - { - $size = new Size(800, 600); - $this->assertEquals(800, $size->getWidth()); - } - - public function testGetHeight() - { - $size = new Size(800, 600); - $this->assertEquals(600, $size->getHeight()); - } - - public function testGetRatio() - { - $size = new Size(800, 600); - $this->assertEquals(1.33333333333, $size->getRatio()); - - $size = new Size(100, 100); - $this->assertEquals(1, $size->getRatio()); - - $size = new Size(1920, 1080); - $this->assertEquals(1.777777777778, $size->getRatio()); - } - - public function testResize() - { - $size = new Size(800, 600); - $size->resize(1000, 2000); - $this->assertEquals(1000, $size->width); - $this->assertEquals(2000, $size->height); - - $size = new Size(800, 600); - $size->resize(2000, null); - $this->assertEquals(2000, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 1000); - $this->assertEquals(800, $size->width); - $this->assertEquals(1000, $size->height); - } - - public function testResizeWithCallbackAspectRatio() - { - $size = new Size(800, 600); - $size->resize(1000, 2000, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(1000, $size->width); - $this->assertEquals(750, $size->height); - - $size = new Size(800, 600); - $size->resize(2000, 1000, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(1333, $size->width); - $this->assertEquals(1000, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 3000, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(4000, $size->width); - $this->assertEquals(3000, $size->height); - - $size = new Size(800, 600); - $size->resize(8000, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(8000, $size->width); - $this->assertEquals(6000, $size->height); - - $size = new Size(800, 600); - $size->resize(100, 400, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(100, $size->width); - $this->assertEquals(75, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 100, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(133, $size->width); - $this->assertEquals(100, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 300, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(80, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(80, $size->width); - $this->assertEquals(60, $size->height); - - $size = new Size(640, 480); - $size->resize(225, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(169, $size->height); - - $size = new Size(640, 480); - $size->resize(223, null, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(223, $size->width); - $this->assertEquals(167, $size->height); - - $size = new Size(600, 800); - $size->resize(300, 300, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 10, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(13, $size->width); - $this->assertEquals(10, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, 1200, function ($c) { $c->aspectRatio(); }); - $this->assertEquals(1000, $size->width); - $this->assertEquals(750, $size->height); - } - - public function testResizeWithCallbackUpsize() - { - $size = new Size(800, 600); - $size->resize(1000, 2000, function ($c) { $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 1000, function ($c) { $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, 400, function ($c) { $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(400, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 300, function ($c) { $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, null, function ($c) { $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 1000, function ($c) { $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - } - - public function testResizeWithCallbackAspectRatioAndUpsize() - { - $size = new Size(800, 600); - $size->resize(1000, 2000, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, 600, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, 300, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 1000, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(400, null, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 300, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(400, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(1000, null, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(null, 1000, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(800, $size->width); - $this->assertEquals(600, $size->height); - - $size = new Size(800, 600); - $size->resize(100, 100, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(100, $size->width); - $this->assertEquals(75, $size->height); - - $size = new Size(800, 600); - $size->resize(300, 200, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(267, $size->width); - $this->assertEquals(200, $size->height); - - $size = new Size(600, 800); - $size->resize(300, 300, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(225, $size->width); - $this->assertEquals(300, $size->height); - - $size = new Size(800, 600); - $size->resize(400, 10, function ($c) { $c->aspectRatio(); $c->upsize(); }); - $this->assertEquals(13, $size->width); - $this->assertEquals(10, $size->height); - } - - public function testRelativePosition() - { - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->align('top-left'); - $input->align('top-left'); - $pos = $container->relativePosition($input); - $this->assertEquals(0, $pos->x); - $this->assertEquals(0, $pos->y); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->align('center'); - $input->align('top-left'); - $pos = $container->relativePosition($input); - $this->assertEquals(400, $pos->x); - $this->assertEquals(300, $pos->y); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->align('bottom-right'); - $input->align('top-right'); - $pos = $container->relativePosition($input); - $this->assertEquals(600, $pos->x); - $this->assertEquals(600, $pos->y); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->align('center'); - $input->align('center'); - $pos = $container->relativePosition($input); - $this->assertEquals(300, $pos->x); - $this->assertEquals(250, $pos->y); - } - - public function testAlign() - { - $width = 640; - $height = 480; - $pivot = Mockery::mock('Intervention\Image\Point'); - $pivot->shouldReceive('setPosition')->with(0, 0)->once(); - $pivot->shouldReceive('setPosition')->with(intval($width/2), 0)->once(); - $pivot->shouldReceive('setPosition')->with($width, 0)->once(); - $pivot->shouldReceive('setPosition')->with(0, intval($height/2))->once(); - $pivot->shouldReceive('setPosition')->with(intval($width/2), intval($height/2))->once(); - $pivot->shouldReceive('setPosition')->with($width, intval($height/2))->once(); - $pivot->shouldReceive('setPosition')->with(0, $height)->once(); - $pivot->shouldReceive('setPosition')->with(intval($width/2), $height)->once(); - $pivot->shouldReceive('setPosition')->with($width, $height)->once(); - - $box = new Size($width, $height, $pivot); - $box->align('top-left'); - $box->align('top'); - $box->align('top-right'); - $box->align('left'); - $box->align('center'); - $box->align('right'); - $box->align('bottom-left'); - $box->align('bottom'); - $b = $box->align('bottom-right'); - $this->assertInstanceOf('Intervention\Image\Size', $b); - } - - public function testFit() - { - $box = new Size(800, 600); - $fitted = $box->fit(new Size(100, 100)); - $this->assertEquals(600, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(100, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(200, 100)); - $this->assertEquals(800, $fitted->width); - $this->assertEquals(400, $fitted->height); - $this->assertEquals(0, $fitted->pivot->x); - $this->assertEquals(100, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(100, 200)); - $this->assertEquals(300, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(250, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(2000, 10)); - $this->assertEquals(800, $fitted->width); - $this->assertEquals(4, $fitted->height); - $this->assertEquals(0, $fitted->pivot->x); - $this->assertEquals(298, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(10, 2000)); - $this->assertEquals(3, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(399, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(800, 600); - $fitted = $box->fit(new Size(800, 600)); - $this->assertEquals(800, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(0, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(400, 300); - $fitted = $box->fit(new Size(120, 120)); - $this->assertEquals(300, $fitted->width); - $this->assertEquals(300, $fitted->height); - $this->assertEquals(50, $fitted->pivot->x); - $this->assertEquals(0, $fitted->pivot->y); - - $box = new Size(600, 800); - $fitted = $box->fit(new Size(100, 100)); - $this->assertEquals(600, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals(0, $fitted->pivot->x); - $this->assertEquals(100, $fitted->pivot->y); - } - - /** - * @dataProvider providerFitWithPosition - */ - public function testFitWithPosition(Size $box, $position, $x, $y) - { - $fitted = $box->fit(new Size(100, 100), $position); - $this->assertEquals(600, $fitted->width); - $this->assertEquals(600, $fitted->height); - $this->assertEquals($x, $fitted->pivot->x); - $this->assertEquals($y, $fitted->pivot->y); - } - - public function providerFitWithPosition() - { - return [ - [new Size(800, 600), 'top-left', 0, 0], - [new Size(800, 600), 'top', 100, 0], - [new Size(800, 600), 'top-right', 200, 0], - [new Size(800, 600), 'left', 0, 0], - [new Size(800, 600), 'center', 100, 0], - [new Size(800, 600), 'right', 200, 0], - [new Size(800, 600), 'bottom-left', 0, 0], - [new Size(800, 600), 'bottom', 100, 0], - [new Size(800, 600), 'bottom-right', 200, 0], - - [new Size(600, 800), 'top-left', 0, 0], - [new Size(600, 800), 'top', 0, 0], - [new Size(600, 800), 'top-right', 0, 0], - [new Size(600, 800), 'left', 0, 100], - [new Size(600, 800), 'center', 0, 100], - [new Size(600, 800), 'right', 0, 100], - [new Size(600, 800), 'bottom-left', 0, 200], - [new Size(600, 800), 'bottom', 0, 200], - [new Size(600, 800), 'bottom-right', 0, 200], - ]; - } - - public function testFitsInto() - { - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(100, 100)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(1000, 100)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(100, 1000)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(800, 600)); - $this->assertTrue($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(1000, 1000)); - $this->assertTrue($fits); - - $box = new Size(100, 100); - $fits = $box->fitsInto(new Size(800, 600)); - $this->assertTrue($fits); - - $box = new Size(100, 100); - $fits = $box->fitsInto(new Size(80, 60)); - $this->assertFalse($fits); - } - - /** - * @expectedException \Intervention\Image\Exception\InvalidArgumentException - */ - public function testInvalidResize() - { - $size = new Size(800, 600); - $size->resize(null, null); - } -} diff --git a/tests/StreamCommandTest.php b/tests/StreamCommandTest.php deleted file mode 100644 index 1af12dbd4..000000000 --- a/tests/StreamCommandTest.php +++ /dev/null @@ -1,36 +0,0 @@ -shouldReceive('encode') - ->with('jpg', 87) - ->once() - ->andReturnSelf(); - - $image->shouldReceive('getEncoded') - ->once() - ->andReturn($encodedContent); - - $command = new StreamCommand(['jpg', 87]); - $result = $command->execute($image); - - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - - $output = $command->getOutput(); - $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $output); - $this->assertEquals($encodedContent, (string)$output); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 000000000..f229c084f --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,17 @@ +assertEquals($r, $color->getRgbRed()); + $this->assertEquals($g, $color->getRgbGreen()); + $this->assertEquals($b, $color->getRgbBlue()); + $this->assertEquals($a, $color->getOpacity()); + } +} diff --git a/tests/TextCommandTest.php b/tests/TextCommandTest.php deleted file mode 100644 index 8b19cbf0e..000000000 --- a/tests/TextCommandTest.php +++ /dev/null @@ -1,32 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $command = new TextCommand(['test', 10, 20]); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - # code... - } - -} diff --git a/tests/TrimCommandTest.php b/tests/TrimCommandTest.php deleted file mode 100644 index b7f5c7706..000000000 --- a/tests/TrimCommandTest.php +++ /dev/null @@ -1,54 +0,0 @@ -shouldReceive('differs')->with($baseColor, 45)->andReturn(true); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('pickColor')->with(0, 0, 'object')->times(2)->andReturn($baseColor); - $image->shouldReceive('pickColor')->with(799, 0, 'object')->once()->andReturn($baseColor); - $image->shouldReceive('setCore')->once(); - $command = new TrimGd(['top-left', ['left', 'right'], 45, 2]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $baseColorPixel = new \ImagickPixel; - $baseColor = Mockery::mock('Intervention\Image\Gd\Color'); - $baseColor->shouldReceive('getPixel')->once()->andReturn($baseColorPixel); - $imagick = Mockery::mock('Imagick'); - $imagick->width = 100; - $imagick->height = 100; - $imagick->shouldReceive('borderimage')->with($baseColorPixel, 1, 1)->once()->andReturn(true); - $imagick->shouldReceive('trimimage')->with(29632.5)->once()->andReturn(true); - $imagick->shouldReceive('getimagepage')->once()->andReturn(['x' => 50, 'y' => 50]); - $imagick->shouldReceive('cropimage')->with(104, 202, 47, 0)->once()->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once()->andReturn(true); - $imagick->shouldReceive('destroy')->with()->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('pickColor')->with(0, 0, 'object')->once()->andReturn($baseColor); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new TrimImagick(['top-left', ['left', 'right'], 45, 2]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/WidenCommandTest.php b/tests/WidenCommandTest.php deleted file mode 100644 index 3d8622d04..000000000 --- a/tests/WidenCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -aspectRatio(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new WidenGd([200]); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(300, 200)->once()->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', [800, 600]); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new WidenImagick([200]); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/images/animation.gif b/tests/images/animation.gif new file mode 100644 index 0000000000000000000000000000000000000000..c45bb7771e1e8d034cb0e17b5f73709407104851 GIT binary patch literal 592 zcmZ?wbhEHb6k*_J_`=R$>7D$48KaYbylY@=ZnEdI*}Pea-aFe=YEgQMnE8qO#; zSy89+Z4v9|1?)j#;ivng&rOi3&2sv)l({L_zQ5S=(*lmS^SFL2W+~5bxiCSZDAnD; zFJW?t*}+bQV?DBp|GE8KLxPsJYn8J)y_3d`<@*ND%i^x#vAXn^t$K?el|_sz}Az3R}J)-SsxlReDu}cq0a9R zz0PIb!k(k0i$c~2AWVe$gn?Bg0qm2ERqJv~Kd%uDMs*sUEhl7C?3>jGY6B0RE zIOZHHU~1kdr4-qMP=oNDMuI@i5d(qw5;_jA^%|d_Ye>Ja?%+e+?VsmJpI3NX$s`-& zo+`}Iu?Xf8pan>NOHh#GOql0T)bXZi-?Bq;1$(WWdDVFrU*|c*@20^U$bUpo)j*Gt b|DmwM2Vcn@>iXFH1~d`LZ;)6*^P4pQdA7Tj literal 0 HcmV?d00001 diff --git a/tests/images/black-friday.png b/tests/images/black-friday.png deleted file mode 100644 index 129c9e9459f0dcee89f1a9f4a25e89b3e9d50a10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4964 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 Ab^rhX literal 0 HcmV?d00001 diff --git a/tests/images/broken.png b/tests/images/broken.png deleted file mode 100644 index eaecd5c64023152ca7c424acf044cff4fe93b068..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42 vcmeAS@N?(olHy`uVBq!ia0voZz6=a3NgQkp42-e*>=zjr6c{{R97DJOiY*3R diff --git a/tests/images/cats.gif b/tests/images/cats.gif new file mode 100644 index 0000000000000000000000000000000000000000..307d4f0e52fb0d170511f4d5d472d66194307522 GIT binary patch literal 1721 zcmeIx`&ZI;7{Ku_pbixix#txE5fux(jY>UVLKKvlBHmJ)x6E73Qk$7B5ET(`k@s7Y znrJs~U7VIOb%Lj+T~@AnoThWtX1PwS)7I9soil&iU$D>5&-26kd7Y0h--op`)*o;N zWB_pGV>|!=rf3w(%Fe0a zQja^nfQ~J#ByBfG|30Iv6o6r5jCI*fUBd3eF^SXZToeYi%MzXAfL-m+sVpKYw&OCQ zV?A@5T`kZWp;LE&`3*j`--A$5LVAAXM4ltIu!TGt=s1(%+ni=qwgVS(IRDv&Ol3Z? zW1mI43qE^@YA@?wtW8L#VO=GiYhQ{l@wXb+-EZ=78TKLbvnphbmmJ-SM%u*w^F5oa~xM#o-P`z5Yau zD%L74G2=34Yquv^94c&N;*izf)bMu>f;JaDh=j_Cx3|k9Q%*?KlO7lw_p9ViIGgax{^J6q_r?KSQ80%c$aUV%fI-$rNpJP^odF0y-&ouDCIEZ|fWR$z zJ*^rk9PJRL?NN=%kYv9mUhmln1;Hg{PTSiySwZBKSn~VYb*E{;-BG$eTRuY?m68^p zEEkl9!c7Rw<#SvFEI)4TamM40R^AY%T5a$YF!Lv)o-T@lMY1m%SSH<195{|XR}J!( zvSMv}*@tSlJiS$5cPhFZwCFfECq>S<4=Na!XDxC~pMl1t9yTutVnHRupD(vQnoGBS zg10yDfQbdTByUa~!+b7jEmk$12#D!TKD0e3BqQ2;96qowhR7i^R)eC4uD$qSRBCJ! zFns;jCGBanDSR{C1lR)L0o;1}Z2$(hq#*$yACBj-c{2;_B51HcOc+(Mi-{h#IgmS# zx%pd+is=lPYO(@mR!T=sw)+;u-zrVebT0DqjYZ`G=2|;PLZi-xM~J!XGd5W$Yz8J7 z@H8zhS(d+AB6aCo*QvwZfC{zH|LAM`K*$5A@#H)r2Ip9IF7=b95a7)L023p>88US2 z$rMo^)w=LU->TM)z6-m+o{a;L@Y&()#c{dRC2~-Rs3dAvzBYx?8DuJCtFxi`V5R`6 z-)IO2S>YgqxLHV8FC=^@)NK~R(!i?9R!tG~B+CK96l-LLruRw2W$!Ts9_>y9``C&C zB6EueyYGNOfqjV4+>BCeT*ov^R0rOR*VH+TxYh|zixj&G`^=w0r;7{oTm?={AH?BdLzRzqf(jE&ZF6C_K&m)0C7DkwA2}@k3vv*F`(|i TK8aaq5;Hw^f|?J30K5MNs4O4@ literal 0 HcmV?d00001 diff --git a/tests/images/circle.png b/tests/images/circle.png deleted file mode 100644 index 0ad4e6bdafdb58ed53f52ea8b123c55a63406d07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 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 VHj8nfw%10P~g% Ab^rhX literal 0 HcmV?d00001 diff --git a/tests/images/iptc.jpg b/tests/images/iptc.jpg deleted file mode 100644 index 849dd0a3f9bf8420a4cd8501fac3935f3ebe6e5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10526 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 diff --git a/tests/images/red.gif b/tests/images/red.gif new file mode 100644 index 0000000000000000000000000000000000000000..408dff18335cddb345f42b408c795decce3c7f6d GIT binary patch literal 47 zcmZ?wbh9u|6krfwXkcLYpOV7Bz@P)fK)wJ20~23M|H{*E`4`XGa;tl@9V>%10Q4~o Ab^rhX literal 0 HcmV?d00001 diff --git a/tests/images/star.png b/tests/images/star.png deleted file mode 100644 index def6448d8fd404d07058f5b250dd3302b50ce3f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 463 zcmV;=0WkiFP)5yNie))P-b|}pvCZrL7U+Lh;{+-f#Q1@B!LQ4fLI%db)YneFA3GmjNKj<3LMV_ z#7$84ejxr!t|8FQ*iLC+ReL#Exh%W;13rZZ22DQuth?R+Ss2dc+z+E6d z19j9aQlsD=5C@WAIX0jSHRB#RXGz5iQQC?f@Z{7@h^f~u7qZ559?VAKIa!T|sQ1^^I4V?IVO6o>!-002ovPDHLk FV1g$Yr*Z%Q diff --git a/tests/images/test.jpg b/tests/images/test.jpg deleted file mode 100644 index 4bb6549141d4179948fe680537487b31f1652f68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9183 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 CK%zqc diff --git a/tests/images/test.webp b/tests/images/test.webp deleted file mode 100755 index ecd28246eacd3d168cc855baf289aab6a7effe30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82 zcmWIYbaV4!U|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` diff --git a/tests/tmp/.gitkeep b/tests/tmp/.gitkeep deleted file mode 100644 index e69de29bb..000000000 From 8405300cc7e3663cebc57e3d54d9c7aa94ce053b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 15:07:56 +0200 Subject: [PATCH 0144/1667] EncodedImage class --- .gitignore | 1 + src/Drivers/Abstract/AbstractImage.php | 30 ++++++-------------------- src/EncodedImage.php | 30 ++++++++++++++++++++++++++ src/Interfaces/ImageInterface.php | 3 +++ 4 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 src/EncodedImage.php diff --git a/.gitignore b/.gitignore index daaed11d9..799dd9418 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ composer.lock vendor/ dev/ .idea/ +.phpunit.result.cache \ No newline at end of file diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 780b734b7..4c48a7817 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Collection; +use Intervention\Image\EncodedImage; use Intervention\Image\Exceptions\NotWritableException; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; @@ -47,35 +48,18 @@ public function modify(ModifierInterface $modifier): ImageInterface return $modifier->apply($this); } - public function encode(EncoderInterface $encoder, ?string $path = null): string + public function encode(EncoderInterface $encoder): EncodedImage { - $encoded = $encoder->encode($this); - - if ($path) { - $saved = @file_put_contents($path, $encoded); - if ($saved === false) { - throw new NotWritableException( - "Can't write image data to path ({$path})." - ); - } - } - - return $encoded; + return new EncodedImage($encoder->encode($this)); } - public function toJpeg(?int $quality = null, ?string $path = null): string + public function toJpeg(?int $quality = null): string { - return $this->encode( - $this->resolveDriverClass('Encoders\JpegEncoder', $quality), - $path - ); + return $this->encode($this->resolveDriverClass('Encoders\JpegEncoder', $quality)); } - public function toGif(?string $path = null): string + public function toGif(): string { - return $this->encode( - $this->resolveDriverClass('Encoders\GifEncoder'), - $path - ); + return $this->encode($this->resolveDriverClass('Encoders\GifEncoder')); } } diff --git a/src/EncodedImage.php b/src/EncodedImage.php new file mode 100644 index 000000000..b7691860b --- /dev/null +++ b/src/EncodedImage.php @@ -0,0 +1,30 @@ +data = $data; + } + + public function save(string $filepath): void + { + $saved = @file_put_contents($filepath, (string) $this); + if ($saved === false) { + throw new NotWritableException( + "Can't write image data to path ({$filepath})." + ); + } + } + + public function __toString(): string + { + return $this->data; + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index ed54858ad..d4bca5897 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Interfaces; +use Intervention\Image\EncodedImage; + interface ImageInterface { public function size(): SizeInterface; @@ -9,4 +11,5 @@ public function width(): int; public function height(): int; public function isAnimated(): bool; public function greyscale(): ImageInterface; + public function encode(EncoderInterface $encoder): EncodedImage } From 0cf62e5f1d5f952b790d9a912f788aa9ea109ec9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 15:10:44 +0200 Subject: [PATCH 0145/1667] Bugfix --- src/Interfaces/ImageInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index d4bca5897..735f046fb 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -11,5 +11,5 @@ public function width(): int; public function height(): int; public function isAnimated(): bool; public function greyscale(): ImageInterface; - public function encode(EncoderInterface $encoder): EncodedImage + public function encode(EncoderInterface $encoder): EncodedImage; } From 2927abf1a2091bb3b78022b16b38efaa9b988fe7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 16:27:27 +0200 Subject: [PATCH 0146/1667] Normalized delay --- src/Drivers/Gd/Frame.php | 8 ++++---- src/Drivers/Imagick/Frame.php | 8 ++++---- src/Interfaces/FrameInterface.php | 4 ++-- tests/Drivers/Gd/FrameTest.php | 4 ++-- tests/Drivers/Imagick/FrameTest.php | 9 +++++---- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index e5c9e0d15..2c7d18e19 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -11,9 +11,9 @@ class Frame extends AbstractFrame implements FrameInterface { /** - * Delay time in miliseconds after next frame is shown + * Delay time in seconds after next frame is shown * - * @var integer + * @var float */ protected $delay = 0; @@ -48,12 +48,12 @@ public function getCore(): GdImage return $this->core; } - public function getDelay(): int + public function getDelay(): float { return $this->delay; } - public function setDelay(int $delay): FrameInterface + public function setDelay(float $delay): FrameInterface { $this->delay = $delay; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index c5c5652a9..448732993 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -20,14 +20,14 @@ public function getCore(): Imagick return $this->core; } - public function getDelay(): int + public function getDelay(): float { - return $this->core->getImageDelay(); + return $this->core->getImageDelay() / 100; } - public function setDelay(int $delay): FrameInterface + public function setDelay(float $delay): FrameInterface { - $this->core->setImageDelay($delay); + $this->core->setImageDelay(round($delay * 100)); return $this; } diff --git a/src/Interfaces/FrameInterface.php b/src/Interfaces/FrameInterface.php index 2af3408cd..4febaea95 100644 --- a/src/Interfaces/FrameInterface.php +++ b/src/Interfaces/FrameInterface.php @@ -6,8 +6,8 @@ interface FrameInterface { public function toImage(): ImageInterface; public function getCore(); - public function getDelay(): int; - public function setDelay(int $delay): FrameInterface; + public function getDelay(): float; + public function setDelay(float $delay): FrameInterface; public function getDispose(): int; public function setDispose(int $dispose): FrameInterface; public function setOffset(int $left, int $top): FrameInterface; diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index b17feb516..bb72f17b3 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -24,9 +24,9 @@ public function testSetGetDelay() $frame = $this->getTestFrame(); $this->assertEquals(0, $frame->getDelay()); - $result = $frame->setDelay(100); + $result = $frame->setDelay(1.5); $this->assertInstanceOf(Frame::class, $result); - $this->assertEquals(100, $frame->getDelay()); + $this->assertEquals(1.5, $frame->getDelay()); } public function testSetGetDispose() diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index c7c2a26de..30ae14676 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -14,7 +14,7 @@ protected function getTestFrame(): Frame { $imagick = new Imagick(); $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $imagick->setImageDelay(4); + $imagick->setImageDelay(125); // 1.25 seconds $imagick->setImageDispose(5); $imagick->setImagePage(3, 2, 8, 9); @@ -30,11 +30,12 @@ public function testConstructor(): void public function testSetGetDelay() { $frame = $this->getTestFrame(); - $this->assertEquals(4, $frame->getDelay()); + $this->assertEquals(1.25, $frame->getDelay()); - $result = $frame->setDelay(100); + $result = $frame->setDelay(2.5); $this->assertInstanceOf(Frame::class, $result); - $this->assertEquals(100, $frame->getDelay()); + $this->assertEquals(2.5, $frame->getDelay()); + $this->assertEquals(250, $frame->getCore()->getImageDelay()); } public function testSetGetDispose() From 26a35946f10f9730161b190b37b45369cc7e3e75 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 16:33:25 +0200 Subject: [PATCH 0147/1667] Normalized delay --- src/Drivers/Gd/Decoders/BinaryImageDecoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index a5e69c0b4..0269da2d5 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -47,7 +47,7 @@ protected function decodeGif($input): ImageInterface $splitter = GifSplitter::create($gif)->split(); $delays = $splitter->getDelays(); foreach ($splitter->coalesceToResources() as $key => $gd) { - $image->addFrame((new Frame($gd))->setDelay($delays[$key])); + $image->addFrame((new Frame($gd))->setDelay($delays[$key] / 100)); } return $image; From 60f7b300c48d494b277f585c0bde7ed42a683171 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 16:36:57 +0200 Subject: [PATCH 0148/1667] Fix --- src/Drivers/Abstract/AbstractImage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 4c48a7817..84ae28ab5 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -9,6 +9,7 @@ use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanResolveDriverClass; From 588178954949fb43a266c94e592ff557110cc2f7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 16:55:03 +0200 Subject: [PATCH 0149/1667] Animaton loops --- src/Drivers/Abstract/AbstractImage.php | 14 ++++++++++++++ src/Drivers/Gd/Decoders/BinaryImageDecoder.php | 2 ++ src/Drivers/Gd/Image.php | 2 ++ .../Imagick/Decoders/BinaryImageDecoder.php | 1 + src/Interfaces/ImageInterface.php | 2 ++ 5 files changed, 21 insertions(+) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 84ae28ab5..9467c67d8 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -17,6 +17,8 @@ abstract class AbstractImage { use CanResolveDriverClass; + protected $loops = 0; + public function getIterator(): Collection { return $this->frames; @@ -34,6 +36,18 @@ public function addFrame(FrameInterface $frame): ImageInterface return $this; } + public function setLoops(int $count): ImageInterface + { + $this->loops = $count; + + return $this; + } + + public function loops(): int + { + return $this->loops; + } + public function size(): SizeInterface { return new Size($this->width(), $this->height()); diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 0269da2d5..fcc2c992b 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -40,6 +40,8 @@ protected function decodeGif($input): ImageInterface $image = new Image(new Collection()); $gif = GifDecoder::decode($input); + $image->setLoops($gif->getMainApplicationExtension()?->getLoops()); + if (!$gif->isAnimated()) { return $image->addFrame(new Frame(@imagecreatefromstring($input))); } diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 40742d244..5f3315695 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -14,6 +14,8 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { + protected $loops = 0; + public function __construct(protected Collection $frames) { // diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 96cc9a982..72cdc7188 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -24,6 +24,7 @@ public function decode($input): ImageInterface|ColorInterface $imagick = $imagick->coalesceImages(); $image = new Image(new Collection()); + $image->setLoops($imagick->getNumberImages()); foreach ($imagick as $frame_content) { $image->addFrame(new Frame($frame_content)); diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 735f046fb..a7609d05b 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -12,4 +12,6 @@ public function height(): int; public function isAnimated(): bool; public function greyscale(): ImageInterface; public function encode(EncoderInterface $encoder): EncodedImage; + public function setLoops(int $count): ImageInterface; + public function loops(): int; } From f540e07ed31c52abe48d78ed2c19054b46679a9a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 17:01:27 +0200 Subject: [PATCH 0150/1667] Fixes --- src/Drivers/Gd/Encoders/GifEncoder.php | 2 +- src/Drivers/Imagick/Decoders/BinaryImageDecoder.php | 2 +- src/Drivers/Imagick/Encoders/GifEncoder.php | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index d4a99a021..927fc6708 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -22,7 +22,7 @@ public function encode(ImageInterface $image): string protected function encodeAnimated($image): string { - $builder = GifBuilder::canvas($image->width(), $image->height(), 2); + $builder = GifBuilder::canvas($image->width(), $image->height(), $image->getLoops()); foreach ($image as $key => $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 72cdc7188..76145fbaa 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -24,7 +24,7 @@ public function decode($input): ImageInterface|ColorInterface $imagick = $imagick->coalesceImages(); $image = new Image(new Collection()); - $image->setLoops($imagick->getNumberImages()); + $image->setLoops($imagick->getImageIterations()); foreach ($imagick as $frame_content) { $image->addFrame(new Frame($frame_content)); diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index 4b62dae2e..a2eed02bd 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -19,6 +19,7 @@ public function encode(ImageInterface $image): string $gif->addImage($frame->getCore()); } + $gif->setImageIterations($image->getLoops()); $gif->setFormat($format); $gif->setImageFormat($format); $gif->setCompression($compression); From f7d6e26e4cc61d9cf5715fb9392c3201df6aec4e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 21 Oct 2021 17:05:39 +0200 Subject: [PATCH 0151/1667] Fixes --- src/Drivers/Gd/Encoders/GifEncoder.php | 2 +- src/Drivers/Imagick/Encoders/GifEncoder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 927fc6708..47dbb7cb5 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -22,7 +22,7 @@ public function encode(ImageInterface $image): string protected function encodeAnimated($image): string { - $builder = GifBuilder::canvas($image->width(), $image->height(), $image->getLoops()); + $builder = GifBuilder::canvas($image->width(), $image->height(), $image->loops()); foreach ($image as $key => $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index a2eed02bd..1998edfbb 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -19,7 +19,7 @@ public function encode(ImageInterface $image): string $gif->addImage($frame->getCore()); } - $gif->setImageIterations($image->getLoops()); + $gif->setImageIterations($image->loops()); $gif->setFormat($format); $gif->setImageFormat($format); $gif->setCompression($compression); From 99038d3a75d6cf1460b5175a8228e1b57bab3109 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 22 Oct 2021 16:15:49 +0200 Subject: [PATCH 0152/1667] Refactoring --- src/Drivers/Abstract/AbstractImage.php | 21 +++++++++++++++++-- .../Gd/Decoders/BinaryImageDecoder.php | 3 ++- src/Drivers/Gd/Image.php | 12 ----------- src/Drivers/Imagick/Image.php | 10 --------- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 9467c67d8..00f970a78 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -18,6 +18,12 @@ abstract class AbstractImage use CanResolveDriverClass; protected $loops = 0; + protected $frames; + + public function __construct(Collection $frames) + { + $this->frames = $frames; + } public function getIterator(): Collection { @@ -70,11 +76,22 @@ public function encode(EncoderInterface $encoder): EncodedImage public function toJpeg(?int $quality = null): string { - return $this->encode($this->resolveDriverClass('Encoders\JpegEncoder', $quality)); + return $this->encode( + $this->resolveDriverClass('Encoders\JpegEncoder', $quality) + ); } public function toGif(): string { - return $this->encode($this->resolveDriverClass('Encoders\GifEncoder')); + return $this->encode( + $this->resolveDriverClass('Encoders\GifEncoder') + ); + } + + public function greyscale(): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\GreyscaleModifier') + ); } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index fcc2c992b..478ae7f68 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -40,12 +40,13 @@ protected function decodeGif($input): ImageInterface $image = new Image(new Collection()); $gif = GifDecoder::decode($input); - $image->setLoops($gif->getMainApplicationExtension()?->getLoops()); if (!$gif->isAnimated()) { return $image->addFrame(new Frame(@imagecreatefromstring($input))); } + $image->setLoops($gif->getMainApplicationExtension()?->getLoops()); + $splitter = GifSplitter::create($gif)->split(); $delays = $splitter->getDelays(); foreach ($splitter->coalesceToResources() as $key => $gd) { diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 5f3315695..751f4d4bf 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -14,13 +14,6 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { - protected $loops = 0; - - public function __construct(protected Collection $frames) - { - // - } - public function width(): int { return imagesx($this->frames->first()->getCore()); @@ -30,9 +23,4 @@ public function height(): int { return imagesy($this->frames->first()->getCore()); } - - public function greyscale(): ImageInterface - { - return $this->modify(new Modifiers\GreyscaleModifier()); - } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index be0a3afca..47073a259 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -13,11 +13,6 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { - public function __construct(protected Collection $frames) - { - // - } - public function width(): int { return $this->frames->first()->getCore()->getImageWidth(); @@ -27,9 +22,4 @@ public function height(): int { return $this->frames->first()->getCore()->getImageHeight(); } - - public function greyscale(): ImageInterface - { - return $this->modify(new Modifiers\GreyscaleModifier()); - } } From a149d87b332d69a7f48ba4371d4a07ce8010878e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 18:10:34 +0200 Subject: [PATCH 0153/1667] Methods --- src/EncodedImage.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/EncodedImage.php b/src/EncodedImage.php index b7691860b..fa01f6564 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -7,6 +7,7 @@ class EncodedImage { protected $data; + protected $mimetype; public function __construct(string $data) { @@ -23,6 +24,11 @@ public function save(string $filepath): void } } + public function toDataUrl(): string + { + return ''; + } + public function __toString(): string { return $this->data; From e475b28f013c166a5a4f406fb5abe3016460a237 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 18:10:42 +0200 Subject: [PATCH 0154/1667] Added BlurModifiers --- src/Drivers/Gd/Modifiers/BlurModifier.php | 36 +++++++++++++++++++ .../Imagick/Modifiers/BlurModifier.php | 28 +++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/BlurModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/BlurModifier.php diff --git a/src/Drivers/Gd/Modifiers/BlurModifier.php b/src/Drivers/Gd/Modifiers/BlurModifier.php new file mode 100644 index 000000000..376b364f4 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/BlurModifier.php @@ -0,0 +1,36 @@ +amount = $amount; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->blurFrame($frame); + } + + return $image; + } + + protected function blurFrame(FrameInterface $frame): void + { + for ($i = 0; $i < $this->amount; $i++) { + imagefilter($frame->getCore(), IMG_FILTER_GAUSSIAN_BLUR); + } + } +} diff --git a/src/Drivers/Imagick/Modifiers/BlurModifier.php b/src/Drivers/Imagick/Modifiers/BlurModifier.php new file mode 100644 index 000000000..f22b5f019 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/BlurModifier.php @@ -0,0 +1,28 @@ +amount = $amount; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($this->image as $frame) { + $frame->getCore()->blurImage(1 * $this->amount, 0.5 * $this->amount); + } + + return $this->image; + } +} From 088f92147db4debe68409cb8bd5e21b49173e37c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 20:10:27 +0200 Subject: [PATCH 0155/1667] Added Frame::getSize() --- src/Drivers/Gd/Frame.php | 7 +++++++ src/Drivers/Imagick/Frame.php | 5 +++++ src/Interfaces/FrameInterface.php | 1 + 3 files changed, 13 insertions(+) diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 2c7d18e19..0d3e0c645 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -5,8 +5,10 @@ use GdImage; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractFrame; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\SizeInterface; class Frame extends AbstractFrame implements FrameInterface { @@ -48,6 +50,11 @@ public function getCore(): GdImage return $this->core; } + public function getSize(): SizeInterface + { + return new Size(imagesx($this->core), imagesy($this->core)); + } + public function getDelay(): float { return $this->delay; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 448732993..97e02065e 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -20,6 +20,11 @@ public function getCore(): Imagick return $this->core; } + public function getSize(): SizeInterface + { + return new Size($this->core->getImageWidth(), $this->core->getImageHeight()); + } + public function getDelay(): float { return $this->core->getImageDelay() / 100; diff --git a/src/Interfaces/FrameInterface.php b/src/Interfaces/FrameInterface.php index 4febaea95..3c27aea9c 100644 --- a/src/Interfaces/FrameInterface.php +++ b/src/Interfaces/FrameInterface.php @@ -6,6 +6,7 @@ interface FrameInterface { public function toImage(): ImageInterface; public function getCore(); + public function getSize(): SizeInterface; public function getDelay(): float; public function setDelay(float $delay): FrameInterface; public function getDispose(): int; From be5301dd8972cdc654113cf88e1e8292f6c9772b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 20:10:40 +0200 Subject: [PATCH 0156/1667] Added blur() method --- src/Drivers/Abstract/AbstractImage.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 00f970a78..a01593ef9 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -94,4 +94,11 @@ public function greyscale(): ImageInterface $this->resolveDriverClass('Modifiers\GreyscaleModifier') ); } + + public function blur(int $amount): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\BlurModifier', $amount) + ); + } } From da92a66926de5ad5475b1006372dec6039bb403b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 20:12:55 +0200 Subject: [PATCH 0157/1667] Added tests --- src/Drivers/Imagick/Frame.php | 2 ++ tests/Drivers/Gd/FrameTest.php | 7 +++++++ tests/Drivers/Imagick/FrameTest.php | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 97e02065e..94cc9c680 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -5,8 +5,10 @@ use Imagick; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractFrame; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\SizeInterface; class Frame extends AbstractFrame implements FrameInterface { diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index bb72f17b3..3c0331038 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -4,6 +4,7 @@ use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; +use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; class FrameTest extends TestCase @@ -19,6 +20,12 @@ public function testConstructor(): void $this->assertInstanceOf(Frame::class, $frame); } + public function testGetSize(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Size::class, $frame->getSize()); + } + public function testSetGetDelay() { $frame = $this->getTestFrame(); diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index 30ae14676..e57642766 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -6,6 +6,7 @@ use ImagickPixel; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; +use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; class FrameTest extends TestCase @@ -27,6 +28,12 @@ public function testConstructor(): void $this->assertInstanceOf(Frame::class, $frame); } + public function testGetSize(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(Size::class, $frame->getSize()); + } + public function testSetGetDelay() { $frame = $this->getTestFrame(); From 72f4e2e6ead05950b936b1157382f1f7f9d7c997 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Oct 2021 20:42:48 +0200 Subject: [PATCH 0158/1667] Resizer --- src/Geometry/Resizer.php | 194 +++++++++++++++++ tests/Geometry/ResizerTest.php | 370 +++++++++++++++++++++++++++++++++ 2 files changed, 564 insertions(+) create mode 100644 src/Geometry/Resizer.php create mode 100644 tests/Geometry/ResizerTest.php diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php new file mode 100644 index 000000000..d804194c9 --- /dev/null +++ b/src/Geometry/Resizer.php @@ -0,0 +1,194 @@ +size = $size; + $this->target = new Size(0, 0); + } + + public static function fromSize(SizeInterface $size): self + { + return new self($size); + } + + public function getSize(): SizeInterface + { + return $this->size; + } + + protected function hasTargetWidth(): bool + { + return $this->target->getWidth() > 0; + } + + protected function hasTargetHeight(): bool + { + return $this->target->getHeight() > 0; + } + + public function width(int $width): self + { + $this->target->setWidth($width); + + return $this; + } + + public function height(int $height): self + { + $this->target->setHeight($height); + + return $this; + } + + public function setTargetSizeByArray(array $arguments): self + { + if (isset($arguments[0]) && is_callable($arguments[0])) { + $arguments[0]($this); + + return $this; + } + + if (isset($arguments[0]) && is_numeric($arguments[0])) { + $this->width($arguments[0]); + } + + if (isset($arguments[1]) && is_numeric($arguments[1])) { + $this->height($arguments[1]); + } + + return $this; + } + + public function setTargetSize(SizeInterface $size): self + { + $this->target = $size; + + return $this; + } + + public function resize(): SizeInterface + { + $resized = clone $this->size; + + if ($this->hasTargetWidth()) { + $resized->setWidth($this->target->getWidth()); + } + + if ($this->hasTargetHeight()) { + $resized->setHeight($this->target->getHeight()); + } + + return $resized; + } + + public function resizeDown(): SizeInterface + { + if ($this->hasTargetWidth()) { + $this->target->setWidth( + min($this->target->getWidth(), $this->size->getWidth()) + ); + } + + if ($this->hasTargetHeight()) { + $this->target->setHeight( + min($this->target->getHeight(), $this->size->getHeight()) + ); + } + + return $this->resize(); + } + + public function scale(): SizeInterface + { + if ($this->hasTargetWidth() && $this->hasTargetHeight()) { + $this->target->setWidth(min( + $this->getProportionalWidth(), + $this->target->getWidth() + )); + $this->target->setHeight(min( + $this->getProportionalHeight(), + $this->target->getHeight() + )); + } elseif ($this->hasTargetWidth()) { + $this->target->setHeight($this->getProportionalHeight()); + } elseif ($this->hasTargetHeight()) { + $this->target->setWidth($this->getProportionalWidth()); + } + + return $this->resize(); + } + + public function scaleDown(): SizeInterface + { + if ($this->hasTargetWidth() && $this->hasTargetHeight()) { + $this->target->setWidth(min( + $this->getProportionalWidth(), + $this->target->getWidth(), + $this->size->getWidth() + )); + $this->target->setHeight(min( + $this->getProportionalHeight(), + $this->target->getHeight(), + $this->size->getHeight() + )); + } elseif ($this->hasTargetWidth()) { + $this->target->setWidth(min( + $this->target->getWidth(), + $this->size->getWidth() + )); + $this->target->setHeight(min( + $this->getProportionalHeight(), + $this->size->getHeight() + )); + } elseif ($this->hasTargetHeight()) { + $this->target->setWidth(min( + $this->getProportionalWidth(), + $this->size->getWidth() + )); + $this->target->setHeight(min( + $this->target->getHeight(), + $this->size->getHeight() + )); + } + + return $this->resize(); + } + + protected function getProportionalWidth(): int + { + if (! $this->hasTargetHeight()) { + return $this->size->getWidth(); + } + + return (int) round($this->target->getHeight() * $this->size->getAspectRatio()); + } + + protected function getProportionalHeight(): int + { + if (! $this->hasTargetWidth()) { + return $this->size->getHeight(); + } + + return (int) round($this->target->getWidth() / $this->size->getAspectRatio()); + } +} diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php new file mode 100644 index 000000000..5395aafff --- /dev/null +++ b/tests/Geometry/ResizerTest.php @@ -0,0 +1,370 @@ +setTargetSizeByArray([800, 600]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(800, $result->resize()->getWidth()); + $this->assertEquals(600, $result->resize()->getHeight()); + + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSizeByArray([800]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(800, $result->resize()->getWidth()); + $this->assertEquals(200, $result->resize()->getHeight()); + + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSizeByArray([function ($size) { + $size->width(80); + $size->height(40); + }]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(80, $result->resize()->getWidth()); + $this->assertEquals(40, $result->resize()->getHeight()); + + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSizeByArray([function ($size) { + $size->width(80); + }]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(80, $result->resize()->getWidth()); + $this->assertEquals(200, $result->resize()->getHeight()); + + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSizeByArray([function ($size) { + $size->height(10); + }]); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(300, $result->resize()->getWidth()); + $this->assertEquals(10, $result->resize()->getHeight()); + } + + public function testSetTargetSize(): void + { + $resizer = new Resizer(new Size(300, 200)); + $result = $resizer->setTargetSize(new Size(200, 100)); + $this->assertInstanceOf(Resizer::class, $result); + $this->assertEquals(200, $result->resize()->getWidth()); + $this->assertEquals(100, $result->resize()->getHeight()); + } + + public function testResize() + { + $size = new Size(300, 200); + $resizer = new Resizer($size); + $resizer->width(150); + $result = $resizer->resize(); + $original = $resizer->getSize(); + $this->assertEquals(150, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); + $this->assertEquals(300, $original->getWidth()); + $this->assertEquals(200, $original->getHeight()); + + $size = new Size(300, 200); + $resizer = new Resizer($size); + $resizer->width(20); + $resizer->height(10); + $result = $resizer->resize(); + $original = $resizer->getSize(); + $this->assertEquals(20, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(300, $original->getWidth()); + $this->assertEquals(200, $original->getHeight()); + } + + public function testResizeDown() + { + // 800x600 > 1000x2000 = 800x600 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(2000); + $result = $resizer->resizeDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + // 800x600 > 400x1000 = 400x600 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(1000); + $result = $resizer->resizeDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + // 800x600 > 1000x400 = 800x400 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(400); + $result = $resizer->resizeDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(400, $result->getHeight()); + + // 800x600 > 400x300 = 400x300 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(300); + $result = $resizer->resizeDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + // 800x600 > 1000xnull = 800x600 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $result = $resizer->resizeDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + // 800x600 > nullx1000 = 800x600 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(1000); + $result = $resizer->resizeDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + } + + public function testScale() + { + // 800x600 > 1000x2000 = 1000x750 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(2000); + $result = $resizer->scale(); + $this->assertEquals(1000, $result->getWidth()); + $this->assertEquals(750, $result->getHeight()); + + // 800x600 > 2000x1000 = 1333x1000 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(2000); + $resizer->height(1000); + $result = $resizer->scale(); + $this->assertEquals(1333, $result->getWidth()); + $this->assertEquals(1000, $result->getHeight()); + + // // 800x600 > nullx3000 = 4000x3000 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(3000); + $result = $resizer->scale(); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(3000, $result->getHeight()); + + // // 800x600 > 8000xnull = 8000x6000 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(8000); + $result = $resizer->scale(); + $this->assertEquals(8000, $result->getWidth()); + $this->assertEquals(6000, $result->getHeight()); + + // // 800x600 > 100x400 = 100x75 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(100); + $resizer->height(400); + $result = $resizer->scale(); + $this->assertEquals(100, $result->getWidth()); + $this->assertEquals(75, $result->getHeight()); + + // // 800x600 > 400x100 = 133x100 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(100); + $result = $resizer->scale(); + $this->assertEquals(133, $result->getWidth()); + $this->assertEquals(100, $result->getHeight()); + + // // 800x600 > nullx300 = 400x300 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(300); + $result = $resizer->scale(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + // // 800x600 > 80xnull = 80x60 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(80); + $result = $resizer->scale(); + $this->assertEquals(80, $result->getWidth()); + $this->assertEquals(60, $result->getHeight()); + + // // 640x480 > 225xnull = 225x169 + $size = new Size(640, 480); + $resizer = new Resizer($size); + $resizer->width(225); + $result = $resizer->scale(); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(169, $result->getHeight()); + + // // 640x480 > 223xnull = 223x167 + $size = new Size(640, 480); + $resizer = new Resizer($size); + $resizer->width(223); + $result = $resizer->scale(); + $this->assertEquals(223, $result->getWidth()); + $this->assertEquals(167, $result->getHeight()); + + // // 600x800 > 300x300 = 225x300 + $size = new Size(600, 800); + $resizer = new Resizer($size); + $resizer->width(300); + $resizer->height(300); + $result = $resizer->scale(); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + // // 800x600 > 400x10 = 13x10 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(10); + $result = $resizer->scale(); + $this->assertEquals(13, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); + + // // 800x600 > 1000x1200 = 1000x750 + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(1200); + $result = $resizer->scale(); + $this->assertEquals(1000, $result->getWidth()); + $this->assertEquals(750, $result->getHeight()); + + $size = new Size(12000, 12); + $resizer = new Resizer($size); + $resizer->width(4000); + $resizer->height(3000); + $result = $resizer->scale(); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(4, $result->getHeight()); + + $size = new Size(12, 12000); + $resizer = new Resizer($size); + $resizer->width(4000); + $resizer->height(3000); + $result = $resizer->scale(); + $this->assertEquals(3, $result->getWidth()); + $this->assertEquals(3000, $result->getHeight()); + + $size = new Size(12000, 6000); + $resizer = new Resizer($size); + $resizer->width(4000); + $resizer->height(3000); + $result = $resizer->scale(); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(2000, $result->getHeight()); + } + + public function testScaleDown() + { + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(2000); + $result = $resizer->scaleDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(600); + $result = $resizer->scaleDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $resizer->height(300); + $result = $resizer->scaleDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(1000); + $result = $resizer->scaleDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $result = $resizer->scaleDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(300); + $result = $resizer->scaleDown(); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(1000); + $result = $resizer->scaleDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->height(1000); + $result = $resizer->scaleDown(); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(100); + $result = $resizer->scaleDown(); + $this->assertEquals(100, $result->getWidth()); + $this->assertEquals(75, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(300); + $resizer->height(200); + $result = $resizer->scaleDown(); + $this->assertEquals(267, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); + + $size = new Size(600, 800); + $resizer = new Resizer($size); + $resizer->width(300); + $resizer->height(300); + $result = $resizer->scaleDown(); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); + + $size = new Size(800, 600); + $resizer = new Resizer($size); + $resizer->width(400); + $resizer->height(10); + $result = $resizer->scaleDown(); + $this->assertEquals(13, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); + } +} From e351b6868326138b2735d62b4a69cc0b0b60c289 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 12:16:23 +0200 Subject: [PATCH 0159/1667] Bugfix --- src/Drivers/Gd/Image.php | 9 +++++++++ src/Drivers/Imagick/Modifiers/GreyscaleModifier.php | 4 ++-- tests/Drivers/Gd/ImageTest.php | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 751f4d4bf..c3fde6eb5 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -6,6 +6,7 @@ use Intervention\Image\Collection; 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\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -23,4 +24,12 @@ public function height(): int { return imagesy($this->frames->first()->getCore()); } + + public function resize(...$arguments): self + { + $resizer = new Resizer($this->size()); + $resizer->setTargetSizeByArray($arguments)->resize(); + + return $this; + } } diff --git a/src/Drivers/Imagick/Modifiers/GreyscaleModifier.php b/src/Drivers/Imagick/Modifiers/GreyscaleModifier.php index d34529558..f5e652824 100644 --- a/src/Drivers/Imagick/Modifiers/GreyscaleModifier.php +++ b/src/Drivers/Imagick/Modifiers/GreyscaleModifier.php @@ -9,10 +9,10 @@ class GreyscaleModifier implements ModifierInterface { public function apply(ImageInterface $image): ImageInterface { - foreach ($this->image as $frame) { + foreach ($image as $frame) { $frame->getCore()->modulateImage(100, 0, 100); } - return $this->image; + return $image; } } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index db5ddb781..b22756806 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -43,4 +43,11 @@ public function testSize(): void { $this->assertInstanceOf(Size::class, $this->image->size()); } + + public function testResize(): void + { + $this->assertInstanceOf(Image::class, $this->image->resize(function ($size) { + $size->width(300); + })); + } } From c71256a0a695a87ee5789f31fde0394b4e33e2a3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:06:19 +0200 Subject: [PATCH 0160/1667] Fixed Imagick Frame content --- src/Drivers/Imagick/Decoders/BinaryImageDecoder.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 76145fbaa..1b931e539 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -27,7 +27,9 @@ public function decode($input): ImageInterface|ColorInterface $image->setLoops($imagick->getImageIterations()); foreach ($imagick as $frame_content) { - $image->addFrame(new Frame($frame_content)); + $image->addFrame( + new Frame($frame_content->getImage()) + ); } return $image; From b0731c89d7a74def0491bfec3c1bbc255fb1f2eb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:14:13 +0200 Subject: [PATCH 0161/1667] ResizeModifier --- src/Drivers/Abstract/AbstractImage.php | 7 ++ src/Drivers/Gd/Modifiers/ResizeModifier.php | 80 +++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index a01593ef9..de1464f3f 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -101,4 +101,11 @@ public function blur(int $amount): ImageInterface $this->resolveDriverClass('Modifiers\BlurModifier', $amount) ); } + + public function resize(int $width, int $height): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $width, $height) + ); + } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php new file mode 100644 index 000000000..01ee3b96b --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -0,0 +1,80 @@ +width = $width; + $this->height = $height; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->modify($frame->getCore(), 0, 0, 0, 0, $this->width, $this->height, $image->getWidth(), $image->getHeight()); + } + + return $image; + } + + /** + * Wrapper function for 'imagecopyresampled' + * + * @param Image $image + * @param int $dst_x + * @param int $dst_y + * @param int $src_x + * @param int $src_y + * @param int $dst_w + * @param int $dst_h + * @param int $src_w + * @param int $src_h + * @return boolean + */ + protected function modify($gd, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) + { + // create new image + $modified = imagecreatetruecolor($dst_w, $dst_h); + + // preserve transparency + $transIndex = imagecolortransparent($gd); + + 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); + } + + // copy content from gd + $result = imagecopyresampled( + $modified, + $gd, + $dst_x, + $dst_y, + $src_x, + $src_y, + $dst_w, + $dst_h, + $src_w, + $src_h + ); + + // set new content as recource + $image->setCore($modified); + + return $result; + } +} From d98f9069292980ad5f8104b7e6903baa0b091bbe Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:30:23 +0200 Subject: [PATCH 0162/1667] ResizeModifier --- src/Drivers/Gd/Frame.php | 7 +++++++ src/Drivers/Gd/Image.php | 8 -------- src/Drivers/Gd/Modifiers/ResizeModifier.php | 14 ++++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 0d3e0c645..76803cb8f 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -50,6 +50,13 @@ public function getCore(): GdImage return $this->core; } + public function setCore(GdImage $core): self + { + $this->core = $core; + + return $this; + } + public function getSize(): SizeInterface { return new Size(imagesx($this->core), imagesy($this->core)); diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index c3fde6eb5..be11857ee 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -24,12 +24,4 @@ public function height(): int { return imagesy($this->frames->first()->getCore()); } - - public function resize(...$arguments): self - { - $resizer = new Resizer($this->size()); - $resizer->setTargetSizeByArray($arguments)->resize(); - - return $this; - } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 01ee3b96b..6805129c3 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -20,7 +20,8 @@ public function __construct(int $width, int $height) public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - $this->modify($frame->getCore(), 0, 0, 0, 0, $this->width, $this->height, $image->getWidth(), $image->getHeight()); + $framesize = $frame->getSize(); + $this->modify($frame, 0, 0, 0, 0, $this->width, $this->height, $framesize->getWidth(), $framesize->getHeight()); } return $image; @@ -40,11 +41,14 @@ public function apply(ImageInterface $image): ImageInterface * @param int $src_h * @return boolean */ - protected function modify($gd, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) + protected function modify(Frame $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) { // create new image $modified = imagecreatetruecolor($dst_w, $dst_h); + // get current image + $gd = $frame->getCore(); + // preserve transparency $transIndex = imagecolortransparent($gd); @@ -58,7 +62,7 @@ protected function modify($gd, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $ imagesavealpha($modified, true); } - // copy content from gd + // copy content from resource $result = imagecopyresampled( $modified, $gd, @@ -73,8 +77,6 @@ protected function modify($gd, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $ ); // set new content as recource - $image->setCore($modified); - - return $result; + $frame->setCore($modified); } } From bab6cef4ae8f21f7538da77a3d078b748f98a6ef Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:48:34 +0200 Subject: [PATCH 0163/1667] ResizeModifier --- src/Drivers/Gd/Modifiers/ResizeModifier.php | 4 ++- .../Imagick/Modifiers/ResizeModifier.php | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/Drivers/Imagick/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 6805129c3..ea84aca41 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -41,7 +41,7 @@ public function apply(ImageInterface $image): ImageInterface * @param int $src_h * @return boolean */ - protected function modify(Frame $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) + protected function modify(FrameInterface $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) { // create new image $modified = imagecreatetruecolor($dst_w, $dst_h); @@ -76,6 +76,8 @@ protected function modify(Frame $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $src_h ); + imagedestroy($gd); + // set new content as recource $frame->setCore($modified); } diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php new file mode 100644 index 000000000..22e7f2f1a --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -0,0 +1,28 @@ +width = $width; + $this->height = $height; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $frame->getCore()->scaleImage($this->width, $this->height); + } + + return $image; + } +} From f4b2f3eeb2ff72027712fc3f92ae38f4ed912fe2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:51:16 +0200 Subject: [PATCH 0164/1667] Bugfix --- src/Drivers/Imagick/Modifiers/BlurModifier.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/BlurModifier.php b/src/Drivers/Imagick/Modifiers/BlurModifier.php index f22b5f019..eb4fefa1d 100644 --- a/src/Drivers/Imagick/Modifiers/BlurModifier.php +++ b/src/Drivers/Imagick/Modifiers/BlurModifier.php @@ -19,10 +19,10 @@ public function __construct(int $amount) public function apply(ImageInterface $image): ImageInterface { - foreach ($this->image as $frame) { + foreach ($image as $frame) { $frame->getCore()->blurImage(1 * $this->amount, 0.5 * $this->amount); } - return $this->image; + return $image; } } From a2da08af9b4529f04c73fe92808c75be808bd074 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:53:44 +0200 Subject: [PATCH 0165/1667] DeconstructImages for Imagick gif encoding --- src/Drivers/Imagick/Encoders/GifEncoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index 1998edfbb..fa846238f 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -24,6 +24,7 @@ public function encode(ImageInterface $image): string $gif->setImageFormat($format); $gif->setCompression($compression); $gif->setImageCompression($compression); + $gif = $gif->deconstructImages(); return $gif->getImagesBlob(); } From f48a206fdfedab1173db6fc99408eed42708465c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 18:10:49 +0200 Subject: [PATCH 0166/1667] resize methods --- src/Drivers/Abstract/AbstractImage.php | 39 ++++++++++++++++++- src/Drivers/Gd/Modifiers/ResizeModifier.php | 26 ++++++++++--- .../Imagick/Modifiers/ResizeModifier.php | 19 ++++++--- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index de1464f3f..c643c5e5f 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -5,6 +5,7 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Intervention\Image\Exceptions\NotWritableException; +use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; @@ -59,6 +60,11 @@ public function size(): SizeInterface return new Size($this->width(), $this->height()); } + public function getResizer(): Resizer + { + return new Resizer($this->size()); + } + public function isAnimated(): bool { return $this->getFrames()->count() > 1; @@ -102,10 +108,39 @@ public function blur(int $amount): ImageInterface ); } - public function resize(int $width, int $height): ImageInterface + public function resize(...$arguments): ImageInterface + { + $size = $this->getResizer()->setTargetSizeByArray($arguments)->resize(); + + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + ); + } + + public function resizeDown(...$arguments): ImageInterface + { + $size = $this->getResizer()->setTargetSizeByArray($arguments)->resizeDown(); + + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + ); + } + + public function scale(...$arguments): ImageInterface { + $size = $this->getResizer()->setTargetSizeByArray($arguments)->scale(); + + return $this->modify( + $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + ); + } + + public function scaleDown(...$arguments): ImageInterface + { + $size = $this->getResizer()->setTargetSizeByArray($arguments)->scaleDown(); + return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $width, $height) + $this->resolveDriverClass('Modifiers\ResizeModifier', $size) ); } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index ea84aca41..673fe9fca 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -5,23 +5,37 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\SizeInterface; class ResizeModifier implements ModifierInterface { - protected $width; - protected $height; + /** + * Target size + * + * @var SizeInterface + */ + protected $target; - public function __construct(int $width, int $height) + public function __construct(SizeInterface $target) { - $this->width = $width; - $this->height = $height; + $this->target = $target; } public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { $framesize = $frame->getSize(); - $this->modify($frame, 0, 0, 0, 0, $this->width, $this->height, $framesize->getWidth(), $framesize->getHeight()); + $this->modify( + $frame, + 0, + 0, + 0, + 0, + $this->target->getWidth(), + $this->target->getHeight(), + $framesize->getWidth(), + $framesize->getHeight() + ); } return $image; diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index 22e7f2f1a..ee9de7557 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -5,22 +5,29 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\SizeInterface; class ResizeModifier implements ModifierInterface { - protected $width; - protected $height; + /** + * Target size + * + * @var SizeInterface + */ + protected $target; - public function __construct(int $width, int $height) + public function __construct(SizeInterface $target) { - $this->width = $width; - $this->height = $height; + $this->target = $target; } public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - $frame->getCore()->scaleImage($this->width, $this->height); + $frame->getCore()->scaleImage( + $this->target->getWidth(), + $this->target->getHeight() + ); } return $image; From 8c2766bcba3c861dbede9afbfef2260b93329f05 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 16:29:05 +0000 Subject: [PATCH 0167/1667] Encoding --- src/Drivers/Abstract/AbstractImage.php | 10 +++++----- src/Drivers/Gd/Encoders/GifEncoder.php | 13 ++++++++----- src/Drivers/Gd/Encoders/JpegEncoder.php | 7 +++++-- src/Drivers/Imagick/Encoders/GifEncoder.php | 5 +++-- src/Drivers/Imagick/Encoders/JpegEncoder.php | 5 +++-- src/EncodedImage.php | 5 +++-- src/Interfaces/EncoderInterface.php | 4 +++- src/Interfaces/ImageInterface.php | 2 +- tests/Drivers/Gd/ImageTest.php | 11 ++--------- tests/Drivers/Imagick/ImageTest.php | 4 ++-- 10 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index c643c5e5f..3d682bf78 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -55,14 +55,14 @@ public function loops(): int return $this->loops; } - public function size(): SizeInterface + public function getSize(): SizeInterface { return new Size($this->width(), $this->height()); } public function getResizer(): Resizer { - return new Resizer($this->size()); + return new Resizer($this->getSize()); } public function isAnimated(): bool @@ -77,17 +77,17 @@ public function modify(ModifierInterface $modifier): ImageInterface public function encode(EncoderInterface $encoder): EncodedImage { - return new EncodedImage($encoder->encode($this)); + return $encoder->encode($this); } - public function toJpeg(?int $quality = null): string + public function toJpeg(?int $quality = null): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\JpegEncoder', $quality) ); } - public function toGif(): string + public function toGif(): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\GifEncoder') diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 47dbb7cb5..99d37001a 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -2,25 +2,28 @@ namespace Intervention\Image\Drivers\Gd\Encoders; +use Intervention\Gif\Builder as GifBuilder; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Gif\Builder as GifBuilder; class GifEncoder extends AbstractEncoder implements EncoderInterface { - public function encode(ImageInterface $image): string + public function encode(ImageInterface $image): EncodedImage { if ($image->isAnimated()) { return $this->encodeAnimated($image); } - return $this->getBuffered(function () use ($image) { + $data = $this->getBuffered(function () use ($image) { imagegif($image->getFrames()->first()->getCore()); }); + + return new EncodedImage($data, 'image/gif'); } - protected function encodeAnimated($image): string + protected function encodeAnimated($image): EncodedImage { $builder = GifBuilder::canvas($image->width(), $image->height(), $image->loops()); foreach ($image as $key => $frame) { @@ -28,6 +31,6 @@ protected function encodeAnimated($image): string $builder->addFrame($source, $frame->getDelay()); } - return $builder->encode(); + return new EncodedImage($builder->encode(), 'image/gif'); } } diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php index 408c7b957..02a3e969d 100644 --- a/src/Drivers/Gd/Encoders/JpegEncoder.php +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -3,15 +3,18 @@ namespace Intervention\Image\Drivers\Gd\Encoders; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class JpegEncoder extends AbstractEncoder implements EncoderInterface { - public function encode(ImageInterface $image): string + public function encode(ImageInterface $image): EncodedImage { - return $this->getBuffered(function () use ($image) { + $data = $this->getBuffered(function () use ($image) { imagejpeg($image->getFrames()->first()->getCore(), null, $this->quality); }); + + return new EncodedImage($data, 'image/jpeg'); } } diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index fa846238f..f795f174d 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -4,12 +4,13 @@ use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class GifEncoder extends AbstractEncoder implements EncoderInterface { - public function encode(ImageInterface $image): string + public function encode(ImageInterface $image): EncodedImage { $format = 'gif'; $compression = Imagick::COMPRESSION_LZW; @@ -26,6 +27,6 @@ public function encode(ImageInterface $image): string $gif->setImageCompression($compression); $gif = $gif->deconstructImages(); - return $gif->getImagesBlob(); + return new EncodedImage($gif->getImagesBlob(), 'image/gif'); } } diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index 179531d7d..45fd72200 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -4,12 +4,13 @@ use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class JpegEncoder extends AbstractEncoder implements EncoderInterface { - public function encode(ImageInterface $image): string + public function encode(ImageInterface $image): EncodedImage { $format = 'jpeg'; $compression = Imagick::COMPRESSION_JPEG; @@ -24,6 +25,6 @@ public function encode(ImageInterface $image): string $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); - return $imagick->getImagesBlob(); + return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg'); } } diff --git a/src/EncodedImage.php b/src/EncodedImage.php index fa01f6564..7f8d08482 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -9,9 +9,10 @@ class EncodedImage protected $data; protected $mimetype; - public function __construct(string $data) + public function __construct(string $data, string $mimetype = 'application/octet-stream') { $this->data = $data; + $this->mimetype = $mimetype; } public function save(string $filepath): void @@ -26,7 +27,7 @@ public function save(string $filepath): void public function toDataUrl(): string { - return ''; + return sprintf('data:%s;base64,%s', $this->mimetype, base64_encode($this->data)); } public function __toString(): string diff --git a/src/Interfaces/EncoderInterface.php b/src/Interfaces/EncoderInterface.php index 03163d229..c2d9c41c2 100644 --- a/src/Interfaces/EncoderInterface.php +++ b/src/Interfaces/EncoderInterface.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Interfaces; +use Intervention\Image\EncodedImage; + interface EncoderInterface { - public function encode(ImageInterface $image): string; + public function encode(ImageInterface $image): EncodedImage; } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index a7609d05b..33231f2ca 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -6,7 +6,7 @@ interface ImageInterface { - public function size(): SizeInterface; + public function getSize(): SizeInterface; public function width(): int; public function height(): int; public function isAnimated(): bool; diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index b22756806..0f966290c 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -39,15 +39,8 @@ public function testHeight(): void $this->assertEquals(2, $this->image->height()); } - public function testSize(): void + public function testGetSize(): void { - $this->assertInstanceOf(Size::class, $this->image->size()); - } - - public function testResize(): void - { - $this->assertInstanceOf(Image::class, $this->image->resize(function ($size) { - $size->width(300); - })); + $this->assertInstanceOf(Size::class, $this->image->getSize()); } } diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 611825d0f..a70666dca 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -44,8 +44,8 @@ public function testHeight(): void $this->assertEquals(2, $this->image->height()); } - public function testSize(): void + public function testGetSize(): void { - $this->assertInstanceOf(Size::class, $this->image->size()); + $this->assertInstanceOf(Size::class, $this->image->getSize()); } } From e9a3e4a034f02192ba8863dd0727c6b199b6bbd9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 17:47:52 +0000 Subject: [PATCH 0168/1667] EncodedImage test --- src/EncodedImage.php | 2 +- tests/EncodedImageTest.php | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/EncodedImageTest.php diff --git a/src/EncodedImage.php b/src/EncodedImage.php index 7f8d08482..7244048c7 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -25,7 +25,7 @@ public function save(string $filepath): void } } - public function toDataUrl(): string + public function toDataUri(): string { return sprintf('data:%s;base64,%s', $this->mimetype, base64_encode($this->data)); } diff --git a/tests/EncodedImageTest.php b/tests/EncodedImageTest.php new file mode 100644 index 000000000..7e4ac6370 --- /dev/null +++ b/tests/EncodedImageTest.php @@ -0,0 +1,37 @@ +assertInstanceOf(EncodedImage::class, $image); + } + + public function testSave(): void + { + $image = new EncodedImage('foo', 'bar'); + $path = __DIR__ . '/foo.tmp'; + $this->assertFalse(file_exists($path)); + $image->save($path); + $this->assertTrue(file_exists($path)); + $this->assertEquals('foo', file_get_contents($path)); + unlink($path); + } + + public function testToDataUri(): void + { + $image = new EncodedImage('foo', 'bar'); + $this->assertEquals('data:bar;base64,Zm9v', $image->toDataUri()); + } + + public function testToString(): void + { + $image = new EncodedImage('foo', 'bar'); + $this->assertEquals('foo', (string) $image); + } +} From f043cebdd2a4834a031bef54fbe043387ebcbba1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 18:05:32 +0000 Subject: [PATCH 0169/1667] Factory methods --- src/Drivers/Gd/ImageFactory.php | 3 +++ src/Drivers/Gd/Modifiers/FillModifier.php | 31 +++++++++++++++++++++++ src/Drivers/Imagick/ImageFactory.php | 26 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/FillModifier.php create mode 100644 src/Drivers/Imagick/ImageFactory.php diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 0127f36c6..e5a0990c8 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -11,6 +11,9 @@ class ImageFactory implements FactoryInterface public function newImage(int $width, int $height): ImageInterface { $gd = imagecreatetruecolor($width, $height); + $color = imagecolorallocatealpha($gd, 0, 0, 0, 127); + imagefill($gd, 0, 0, $color); + imagesavealpha($gd, true); return new Image(new Collection([ new Frame($gd) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php new file mode 100644 index 000000000..9135653b4 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -0,0 +1,31 @@ +filling = $filling; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->blurFrame($frame); + } + + return $image; + } + + protected function blurFrame(FrameInterface $frame): void + { + for ($i = 0; $i < $this->amount; $i++) { + imagefilter($frame->getCore(), IMG_FILTER_GAUSSIAN_BLUR); + } + } +} diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php new file mode 100644 index 000000000..3aaba6a7f --- /dev/null +++ b/src/Drivers/Imagick/ImageFactory.php @@ -0,0 +1,26 @@ +newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); + $imagick->setType(Imagick::IMGTYPE_UNDEFINED); + $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); + $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); + + return new Image(new Collection([ + new Frame($imagick) + ])); + } +} From 6d387e1c38e151617ec91aad2304109d7a75d091 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 20:23:48 +0200 Subject: [PATCH 0170/1667] FillModifier --- src/Drivers/Abstract/AbstractImage.php | 7 ++++ src/Drivers/Gd/ImageFactory.php | 8 +++-- src/Drivers/Gd/Modifiers/FillModifier.php | 15 ++++---- src/Drivers/Imagick/Color.php | 5 +++ src/Drivers/Imagick/ImageFactory.php | 8 +++-- .../Imagick/Modifiers/FillModifier.php | 34 +++++++++++++++++++ 6 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 src/Drivers/Imagick/Modifiers/FillModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3d682bf78..df7364827 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -143,4 +143,11 @@ public function scaleDown(...$arguments): ImageInterface $this->resolveDriverClass('Modifiers\ResizeModifier', $size) ); } + + public function fill($filling): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FillModifier', $filling) + ); + } } diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index e5a0990c8..5e93a729d 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -15,8 +15,10 @@ public function newImage(int $width, int $height): ImageInterface imagefill($gd, 0, 0, $color); imagesavealpha($gd, true); - return new Image(new Collection([ - new Frame($gd) - ])); + return new Image( + new Collection([ + new Frame($imagick) + ]) + ); } } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 9135653b4..6c3ce2bcc 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -2,30 +2,33 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { - public function __construct($filling, int $x, int $y) + public function __construct($filling, ?int $x = null, ?int $y = null) { $this->filling = $filling; } public function apply(ImageInterface $image): ImageInterface { + $width = $image->getWidth(); + $height = $image->getHeight(); + $filling = $this->getApplicableFilling(); + foreach ($image as $frame) { - $this->blurFrame($frame); + imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling); } return $image; } - protected function blurFrame(FrameInterface $frame): void + protected function getApplicableFilling() { - for ($i = 0; $i < $this->amount; $i++) { - imagefilter($frame->getCore(), IMG_FILTER_GAUSSIAN_BLUR); - } + return (new InputHandler())->handle($this->filling); } } diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php index f07a82ca2..904b1c7ac 100644 --- a/src/Drivers/Imagick/Color.php +++ b/src/Drivers/Imagick/Color.php @@ -21,6 +21,11 @@ public function __construct(ImagickPixel $pixel) $this->pixel = $pixel; } + public function getPixel(): ImagickPixel + { + return $this->pixel; + } + public function red(): int { return round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255); diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 3aaba6a7f..7cd240c04 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -19,8 +19,10 @@ public function newImage(int $width, int $height): ImageInterface $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); - return new Image(new Collection([ - new Frame($imagick) - ])); + return new Image( + new Collection([ + new Frame($imagick) + ]) + ); } } diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php new file mode 100644 index 000000000..3175bb8c9 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -0,0 +1,34 @@ +filling = $filling; + } + + public function apply(ImageInterface $image): ImageInterface + { + $draw = new ImagickDraw(); + $draw->setFillColor($this->getApplicableFilling()->getPixel()); + $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); + + foreach ($image as $frame) { + $frame->getCore()->drawImage($draw); + } + + return $image; + } + + protected function getApplicableFilling() + { + return (new InputHandler())->handle($this->filling); + } +} From 80c08fd989db8a9b5218e34e627a729b349ed775 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 20:25:20 +0200 Subject: [PATCH 0171/1667] Bugfix --- src/Drivers/Gd/ImageFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 5e93a729d..f68017e2e 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -17,7 +17,7 @@ public function newImage(int $width, int $height): ImageInterface return new Image( new Collection([ - new Frame($imagick) + new Frame($gd) ]) ); } From d4eaea5a9e791e44d3fe27d81b7960b9ce4543fd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Oct 2021 20:33:45 +0200 Subject: [PATCH 0172/1667] Png Encoders --- src/Drivers/Abstract/AbstractImage.php | 7 ++++++ src/Drivers/Gd/Encoders/PngEncoder.php | 20 ++++++++++++++++ src/Drivers/Imagick/Encoders/PngEncoder.php | 26 +++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/Drivers/Gd/Encoders/PngEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/PngEncoder.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index df7364827..1556c4518 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -94,6 +94,13 @@ public function toGif(): EncodedImage ); } + public function toPng(): EncodedImage + { + return $this->encode( + $this->resolveDriverClass('Encoders\PngEncoder') + ); + } + public function greyscale(): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Encoders/PngEncoder.php b/src/Drivers/Gd/Encoders/PngEncoder.php new file mode 100644 index 000000000..f93afb397 --- /dev/null +++ b/src/Drivers/Gd/Encoders/PngEncoder.php @@ -0,0 +1,20 @@ +getBuffered(function () use ($image) { + imagepng($image->getFrames()->first()->getCore()); + }); + + return new EncodedImage($data, 'image/png'); + } +} diff --git a/src/Drivers/Imagick/Encoders/PngEncoder.php b/src/Drivers/Imagick/Encoders/PngEncoder.php new file mode 100644 index 000000000..8bdc03e6c --- /dev/null +++ b/src/Drivers/Imagick/Encoders/PngEncoder.php @@ -0,0 +1,26 @@ +getFrames()->first()->getCore(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + + return new EncodedImage($imagick->getImagesBlob(), 'image/png'); + } +} From 4b7c182152c3c48da8a2e0dfd9573d21cb4a813e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 07:46:01 +0100 Subject: [PATCH 0173/1667] Color Decoder --- src/Drivers/Gd/Decoders/ArrayColorDecoder.php | 8 +++- src/Drivers/Gd/Decoders/HexColorDecoder.php | 27 +++++++++++++ src/Drivers/Gd/InputHandler.php | 10 +++-- src/Drivers/Gd/Modifiers/FillModifier.php | 10 ++--- .../Imagick/Decoders/ArrayColorDecoder.php | 4 +- .../Imagick/Decoders/HexColorDecoder.php | 27 +++++++++++++ src/Drivers/Imagick/InputHandler.php | 10 +++-- .../Imagick/Modifiers/FillModifier.php | 12 +++--- src/Traits/CanHandleInput.php | 11 +++++ ...teColorArray.php => CanValidateColors.php} | 4 +- tests/Drivers/Gd/InputHandlerTest.php | 40 +++++++++++++++++++ 11 files changed, 137 insertions(+), 26 deletions(-) create mode 100644 src/Drivers/Gd/Decoders/HexColorDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/HexColorDecoder.php create mode 100644 src/Traits/CanHandleInput.php rename src/Traits/{CanValidateColorArray.php => CanValidateColors.php} (85%) diff --git a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php index 7a1b71618..9f8d7a709 100644 --- a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php @@ -7,11 +7,11 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColorArray; +use Intervention\Image\Traits\CanValidateColors; class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface { - use CanValidateColorArray; + use CanValidateColors; public function decode($input): ImageInterface|ColorInterface { @@ -19,6 +19,10 @@ public function decode($input): ImageInterface|ColorInterface $this->fail(); } + if (count($input) === 3) { + $input[] = 1; + } + list($r, $g, $b, $a) = $input; return new Color( diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php new file mode 100644 index 000000000..940c327e9 --- /dev/null +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -0,0 +1,27 @@ +fail(); + } + + return parent::decode([ + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), + ]); + } +} diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 1e95b4956..7950e73f8 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -10,10 +10,12 @@ class InputHandler extends AbstractInputHandler protected function chain(): AbstractDecoder { return new Decoders\ArrayColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\HexColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 6c3ce2bcc..2cf7fd54e 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -6,9 +6,12 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class FillModifier implements ModifierInterface { + use CanHandleInput; + public function __construct($filling, ?int $x = null, ?int $y = null) { $this->filling = $filling; @@ -18,7 +21,7 @@ public function apply(ImageInterface $image): ImageInterface { $width = $image->getWidth(); $height = $image->getHeight(); - $filling = $this->getApplicableFilling(); + $filling = $this->handleInput($this->filling); foreach ($image as $frame) { imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling); @@ -26,9 +29,4 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - - protected function getApplicableFilling() - { - return (new InputHandler())->handle($this->filling); - } } diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php index e21107445..89f52eebb 100644 --- a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php @@ -8,11 +8,11 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColorArray; +use Intervention\Image\Traits\CanValidateColors; class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface { - use CanValidateColorArray; + use CanValidateColors; public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php new file mode 100644 index 000000000..a0609b348 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -0,0 +1,27 @@ +fail(); + } + + return parent::decode([ + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), + ]); + } +} diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index d1fcbde2a..2062fa714 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -10,10 +10,12 @@ class InputHandler extends AbstractInputHandler protected function chain(): AbstractDecoder { return new Decoders\ArrayColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\HexColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index 3175bb8c9..81e20cfd3 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -6,9 +6,12 @@ use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class FillModifier implements ModifierInterface { + use CanHandleInput; + public function __construct($filling, ?int $x = null, ?int $y = null) { $this->filling = $filling; @@ -16,8 +19,10 @@ public function __construct($filling, ?int $x = null, ?int $y = null) public function apply(ImageInterface $image): ImageInterface { + $filling = $this->handleInput($this->filling); + $draw = new ImagickDraw(); - $draw->setFillColor($this->getApplicableFilling()->getPixel()); + $draw->setFillColor($filling->getPixel()); $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); foreach ($image as $frame) { @@ -26,9 +31,4 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - - protected function getApplicableFilling() - { - return (new InputHandler())->handle($this->filling); - } } diff --git a/src/Traits/CanHandleInput.php b/src/Traits/CanHandleInput.php new file mode 100644 index 000000000..3182e2afc --- /dev/null +++ b/src/Traits/CanHandleInput.php @@ -0,0 +1,11 @@ +handle($value); + } +} diff --git a/src/Traits/CanValidateColorArray.php b/src/Traits/CanValidateColors.php similarity index 85% rename from src/Traits/CanValidateColorArray.php rename to src/Traits/CanValidateColors.php index f06dd9409..99f14cb46 100644 --- a/src/Traits/CanValidateColorArray.php +++ b/src/Traits/CanValidateColors.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Traits; -trait CanValidateColorArray +trait CanValidateColors { protected function isValidColorArray($input): bool { @@ -22,7 +22,7 @@ protected function isValidColorArray($input): bool } // validate alpha value - if ($input[3] > 1 || $input[3] < 0) { + if (isset($input[3]) && ($input[3] > 1 || $input[3] < 0)) { return false; } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index f4d2d2584..41fdf9546 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -58,5 +58,45 @@ public function testHandleArrayColor(): void $input = [181, 55, 23, .5]; $result = $handler->handle($input); $this->assertInstanceOf(Color::class, $result); + + $handler = new InputHandler(); + $input = [181, 55, 23]; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + } + + public function testHandleHexColor(): void + { + $handler = new InputHandler(); + $input = 'ccff33'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(204, $result->red()); + $this->assertEquals(255, $result->green()); + $this->assertEquals(51, $result->blue()); + + $handler = new InputHandler(); + $input = 'cf3'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(204, $result->red()); + $this->assertEquals(255, $result->green()); + $this->assertEquals(51, $result->blue()); + + $handler = new InputHandler(); + $input = '#123456'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(18, $result->red()); + $this->assertEquals(52, $result->green()); + $this->assertEquals(86, $result->blue()); + + $handler = new InputHandler(); + $input = '#333'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(51, $result->red()); + $this->assertEquals(51, $result->green()); + $this->assertEquals(51, $result->blue()); } } From 9f885da391e345afc2bea48ec176bf07bc999cc7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 07:55:33 +0100 Subject: [PATCH 0174/1667] Fill modifier filling --- src/Drivers/Gd/Modifiers/FillModifier.php | 11 ++++++++--- src/Drivers/Imagick/Modifiers/FillModifier.php | 12 +++++++++--- src/Traits/CanHandleInput.php | 11 ----------- 3 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 src/Traits/CanHandleInput.php diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 2cf7fd54e..c2eb41d88 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -6,11 +6,11 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Traits\CanHandleInput; +use Intervention\Image\Traits\CanResolveDriverClass; class FillModifier implements ModifierInterface { - use CanHandleInput; + use CanResolveDriverClass; public function __construct($filling, ?int $x = null, ?int $y = null) { @@ -21,7 +21,7 @@ public function apply(ImageInterface $image): ImageInterface { $width = $image->getWidth(); $height = $image->getHeight(); - $filling = $this->handleInput($this->filling); + $filling = $this->getApplicableFilling(); foreach ($image as $frame) { imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling); @@ -29,4 +29,9 @@ public function apply(ImageInterface $image): ImageInterface return $image; } + + protected function getApplicableFilling(): ColorInterface + { + return $this->resolveDriverClass('InputHandler')->handle($this->filling); + } } diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index 81e20cfd3..fb4f35b53 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -4,13 +4,14 @@ use ImagickDraw; use Intervention\Image\Drivers\Imagick\InputHandler; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Traits\CanHandleInput; +use Intervention\Image\Traits\CanResolveDriverClass; class FillModifier implements ModifierInterface { - use CanHandleInput; + use CanResolveDriverClass; public function __construct($filling, ?int $x = null, ?int $y = null) { @@ -19,7 +20,7 @@ public function __construct($filling, ?int $x = null, ?int $y = null) public function apply(ImageInterface $image): ImageInterface { - $filling = $this->handleInput($this->filling); + $filling = $this->getApplicableFilling(); $draw = new ImagickDraw(); $draw->setFillColor($filling->getPixel()); @@ -31,4 +32,9 @@ public function apply(ImageInterface $image): ImageInterface return $image; } + + protected function getApplicableFilling(): ColorInterface + { + return $this->resolveDriverClass('InputHandler')->handle($this->filling); + } } diff --git a/src/Traits/CanHandleInput.php b/src/Traits/CanHandleInput.php deleted file mode 100644 index 3182e2afc..000000000 --- a/src/Traits/CanHandleInput.php +++ /dev/null @@ -1,11 +0,0 @@ -handle($value); - } -} From 2c0e2eb719dc41ff4837dac5b26ce80c06667529 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 06:59:48 +0000 Subject: [PATCH 0175/1667] Added Color::toInt --- src/Drivers/Gd/Color.php | 5 +++++ src/Drivers/Gd/Modifiers/FillModifier.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php index 351072bd2..a7ef9a53c 100644 --- a/src/Drivers/Gd/Color.php +++ b/src/Drivers/Gd/Color.php @@ -54,4 +54,9 @@ public function toArray(): array return [$r, $g, $b, $a]; } + + public function toInt(): int + { + return $this->value; + } } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index c2eb41d88..8306e762e 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -24,7 +24,7 @@ public function apply(ImageInterface $image): ImageInterface $filling = $this->getApplicableFilling(); foreach ($image as $frame) { - imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling); + imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling->toInt()); } return $image; From b1900c32c758a51a09caeab9a3f3b37eeeb18e7f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 08:04:05 +0100 Subject: [PATCH 0176/1667] Bugfixes --- src/Drivers/Gd/Modifiers/FillModifier.php | 1 + src/Drivers/Imagick/Decoders/ArrayColorDecoder.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 8306e762e..97faa06e8 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Gd\InputHandler; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php index 89f52eebb..aee0df579 100644 --- a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php @@ -20,6 +20,10 @@ public function decode($input): ImageInterface|ColorInterface $this->fail(); } + if (count($input) === 3) { + $input[] = 1; + } + list($r, $g, $b, $a) = $input; $pixel = new ImagickPixel( From def7a406af27e4e558af976ee0d76133c3cdcbde Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 31 Oct 2021 08:10:16 +0100 Subject: [PATCH 0177/1667] Bugfix --- src/Drivers/Gd/Modifiers/FillModifier.php | 4 ++-- src/Drivers/Imagick/Modifiers/FillModifier.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 97faa06e8..3a857bd24 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -20,8 +20,8 @@ public function __construct($filling, ?int $x = null, ?int $y = null) public function apply(ImageInterface $image): ImageInterface { - $width = $image->getWidth(); - $height = $image->getHeight(); + $width = $image->width(); + $height = $image->height(); $filling = $this->getApplicableFilling(); foreach ($image as $frame) { diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index fb4f35b53..f3eddc0a2 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -24,7 +24,7 @@ public function apply(ImageInterface $image): ImageInterface $draw = new ImagickDraw(); $draw->setFillColor($filling->getPixel()); - $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); + $draw->rectangle(0, 0, $image->width(), $image->height()); foreach ($image as $frame) { $frame->getCore()->drawImage($draw); From e2b9c17ba3d4b7d9f2c1c97d45d47fea53ac3d61 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 3 Nov 2021 18:48:10 +0000 Subject: [PATCH 0178/1667] Added Size::align() --- src/Geometry/Size.php | 87 +++++++++++++++++++++++++++++++++++++ tests/Geometry/SizeTest.php | 45 +++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 4f19bc30e..b2e4f71f5 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -89,4 +89,91 @@ public function isPortrait(): bool { return $this->getWidth() < $this->getHeight(); } + + /** + * Aligns current size's pivot point to given position + * and moves point automatically by offset. + * + * @param string $position + * @param int $offset_x + * @param int $offset_y + * @return Size + */ + public function align(string $position, int $offset_x = 0, int $offset_y = 0): self + { + switch (strtolower($position)) { + case 'top': + case 'top-center': + case 'top-middle': + case 'center-top': + case 'middle-top': + $x = intval($this->width / 2); + $y = 0 + $offset_y; + break; + + case 'top-right': + case 'right-top': + $x = $this->width - $offset_x; + $y = 0 + $offset_y; + break; + + case 'left': + case 'left-center': + case 'left-middle': + case 'center-left': + case 'middle-left': + $x = 0 + $offset_x; + $y = intval($this->height / 2); + break; + + case 'right': + case 'right-center': + case 'right-middle': + case 'center-right': + case 'middle-right': + $x = $this->width - $offset_x; + $y = intval($this->height / 2); + break; + + case 'bottom-left': + case 'left-bottom': + $x = 0 + $offset_x; + $y = $this->height - $offset_y; + break; + + case 'bottom': + case 'bottom-center': + case 'bottom-middle': + case 'center-bottom': + case 'middle-bottom': + $x = intval($this->width / 2); + $y = $this->height - $offset_y; + break; + + case 'bottom-right': + case 'right-bottom': + $x = $this->width - $offset_x; + $y = $this->height - $offset_y; + break; + + case 'center': + case 'middle': + case 'center-center': + case 'middle-middle': + $x = intval($this->width / 2) + $offset_x; + $y = intval($this->height / 2) + $offset_y; + break; + + default: + case 'top-left': + case 'left-top': + $x = 0 + $offset_x; + $y = 0 + $offset_y; + break; + } + + $this->pivot->setPosition($x, $y); + + return $this; + } } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index e3e9c348f..470902381 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -98,4 +98,49 @@ public function testIsPortrait() $box = new Size(200, 300); $this->assertTrue($box->isPortrait()); } + + public function testAlign(): void + { + $box = new Size(640, 480); + $this->assertEquals(0, $box->getPivot()->getX()); + $this->assertEquals(0, $box->getPivot()->getY()); + + $box->align('top-left', 3, 3); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); + + $box->align('top', 3, 3); + $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); + + $box->align('top-right', 3, 3); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); + + $box->align('left', 3, 3); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(240, $box->getPivot()->getY()); + + $box->align('center', 3, 3); + $this->assertEquals(323, $box->getPivot()->getX()); + $this->assertEquals(243, $box->getPivot()->getY()); + + $box->align('right', 3, 3); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(240, $box->getPivot()->getY()); + + $box->align('bottom-left', 3, 3); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); + + $box->align('bottom', 3, 3); + $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); + + $result = $box->align('bottom-right', 3, 3); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); + + $this->assertInstanceOf(Size::class, $result); + } } From 0c3e08b46e057c5966f39838c2da5354931c8ca5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 3 Nov 2021 18:48:58 +0000 Subject: [PATCH 0179/1667] removed fill modifier for now --- src/Drivers/Abstract/AbstractImage.php | 7 ---- src/Drivers/Gd/Modifiers/FillModifier.php | 38 ------------------ .../Imagick/Modifiers/FillModifier.php | 40 ------------------- 3 files changed, 85 deletions(-) delete mode 100644 src/Drivers/Gd/Modifiers/FillModifier.php delete mode 100644 src/Drivers/Imagick/Modifiers/FillModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1556c4518..4c1b1fba9 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -150,11 +150,4 @@ public function scaleDown(...$arguments): ImageInterface $this->resolveDriverClass('Modifiers\ResizeModifier', $size) ); } - - public function fill($filling): ImageInterface - { - return $this->modify( - $this->resolveDriverClass('Modifiers\FillModifier', $filling) - ); - } } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php deleted file mode 100644 index 3a857bd24..000000000 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ /dev/null @@ -1,38 +0,0 @@ -filling = $filling; - } - - public function apply(ImageInterface $image): ImageInterface - { - $width = $image->width(); - $height = $image->height(); - $filling = $this->getApplicableFilling(); - - foreach ($image as $frame) { - imagefilledrectangle($frame->getCore(), 0, 0, $width - 1, $height - 1, $filling->toInt()); - } - - return $image; - } - - protected function getApplicableFilling(): ColorInterface - { - return $this->resolveDriverClass('InputHandler')->handle($this->filling); - } -} diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php deleted file mode 100644 index f3eddc0a2..000000000 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ /dev/null @@ -1,40 +0,0 @@ -filling = $filling; - } - - public function apply(ImageInterface $image): ImageInterface - { - $filling = $this->getApplicableFilling(); - - $draw = new ImagickDraw(); - $draw->setFillColor($filling->getPixel()); - $draw->rectangle(0, 0, $image->width(), $image->height()); - - foreach ($image as $frame) { - $frame->getCore()->drawImage($draw); - } - - return $image; - } - - protected function getApplicableFilling(): ColorInterface - { - return $this->resolveDriverClass('InputHandler')->handle($this->filling); - } -} From 0afda4b9e4752e06f2924045c40d939a0efcccc4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 15:48:16 +0000 Subject: [PATCH 0180/1667] 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 4c1b1fba9..b315f0b1e 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -36,6 +36,11 @@ public function getFrames(): Collection 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 @@ public function blur(int $amount): ImageInterface ); } + 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 @@ public function scaleDown(...$arguments): ImageInterface $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 478ae7f68..32dcc6b14 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 @@ public function decode($input): ImageInterface|ColorInterface 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 @@ protected function decodeGif($input): ImageInterface 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 be11857ee..d226269aa 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -8,6 +8,7 @@ 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 47073a259..a0c289afc 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -7,6 +7,7 @@ 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 @@ public function height(): int { 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 33231f2ca..56b5ec010 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 @@ public function greyscale(): 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 0f966290c..1635bc618 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 @@ public function testGetSize(): void { $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 000000000..9062eda45 --- /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 000000000..a1ff3750f --- /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 000000000..f9f4238e9 --- /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 000000000..0ff426753 --- /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 From 1b856c81bcc912d45dd1c4bb57a916a9c28d0960 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 16:42:48 +0000 Subject: [PATCH 0181/1667] Added tests --- src/Drivers/Abstract/AbstractColor.php | 16 ++++++++++ .../Gd/Decoders/BinaryImageDecoder.php | 32 ++----------------- .../Gd/Modifiers/GreyscaleModifierTest.php | 21 ++++++++++++ .../Gd/Modifiers/ResizeModifierTest.php | 24 ++++++++++++++ .../Modifiers/GreyscaleModifierTest.php | 21 ++++++++++++ .../Imagick/Modifiers/ResizeModifierTest.php | 24 ++++++++++++++ 6 files changed, 109 insertions(+), 29 deletions(-) create mode 100644 tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/ResizeModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php diff --git a/src/Drivers/Abstract/AbstractColor.php b/src/Drivers/Abstract/AbstractColor.php index 449e4747b..f8ea601a3 100644 --- a/src/Drivers/Abstract/AbstractColor.php +++ b/src/Drivers/Abstract/AbstractColor.php @@ -4,6 +4,12 @@ abstract class AbstractColor { + /** + * Format color to hexadecimal color code + * + * @param string $prefix + * @return string + */ public function toHex(string $prefix = ''): string { return sprintf( @@ -14,4 +20,14 @@ public function toHex(string $prefix = ''): string $this->blue() ); } + + /** + * Determine if color is greyscale + * + * @return boolean + */ + public function isGreyscale(): bool + { + return ($this->red() === $this->green()) && ($this->green() === $this->blue()); + } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 32dcc6b14..f60a97023 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -33,7 +33,9 @@ public function decode($input): ImageInterface|ColorInterface $this->fail(); } - $gd = $this->gdImageToTruecolor($gd); + if (! imageistruecolor($gd)) { + imagepalettetotruecolor($gd); + } return new Image(new Collection([new Frame($gd)])); } @@ -58,32 +60,4 @@ protected function decodeGif($input): ImageInterface 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/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php new file mode 100644 index 000000000..9302d460c --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); + $image->modify(new GreyscaleModifier()); + $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php new file mode 100644 index 000000000..15e4c0dd1 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals(50, $image->width()); + $this->assertEquals(50, $image->height()); + $image->modify(new ResizeModifier(new Size(30, 20))); + $this->assertEquals(30, $image->width()); + $this->assertEquals(20, $image->height()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php new file mode 100644 index 000000000..64b5be616 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); + $image->modify(new GreyscaleModifier()); + $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php new file mode 100644 index 000000000..af8bca076 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals(50, $image->width()); + $this->assertEquals(50, $image->height()); + $image->modify(new ResizeModifier(new Size(30, 20))); + $this->assertEquals(30, $image->width()); + $this->assertEquals(20, $image->height()); + } +} From 7c6c7be2badd62d738941f79df4300aa18547179 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 17:13:27 +0000 Subject: [PATCH 0182/1667] PlaceModifier --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 65 +++++++++++++++++++ .../Imagick/Modifiers/PlaceModifier.php | 58 +++++++++++++++++ src/Geometry/Size.php | 15 +++++ .../Gd/Modifiers/PlaceModifierTest.php | 22 +++++++ .../Imagick/Modifiers/PlaceModifierTest.php | 22 +++++++ 5 files changed, 182 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/PlaceModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/PlaceModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/PlaceModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php new file mode 100644 index 000000000..059ac16ce --- /dev/null +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -0,0 +1,65 @@ +element = $element; + $this->position = $position; + $this->offset_x = $offset_x; + $this->offset_y = $offset_y; + } + + public function apply(ImageInterface $image): ImageInterface + { + $watermark = $this->decodeWatermark(); + $position = $this->getPosition($image, $watermark); + + foreach ($image as $frame) { + imagecopy( + $frame->getCore(), + $watermark->getFrame()->getCore(), + $position->getX(), + $position->getY(), + 0, + 0, + $watermark->width(), + $watermark->height() + ); + } + + return $image; + } + + protected function decodeWatermark(): Image + { + return $this->resolveDriverClass('InputHandler')->handle($this->element); + } + + protected function getPosition(Image $image, Image $watermark): Point + { + $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->align($this->position); + + return $image_size->relativePosition($watermark_size); + } +} diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php new file mode 100644 index 000000000..e91714bc6 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -0,0 +1,58 @@ +element = $element; + $this->position = $position; + $this->offset_x = $offset_x; + $this->offset_y = $offset_y; + } + + public function apply(ImageInterface $image): ImageInterface + { + $watermark = $this->decodeWatermark(); + $position = $this->getPosition($image, $watermark); + + foreach ($image as $frame) { + $frame->getCore()->compositeImage( + $watermark->getFrame()->getCore(), + Imagick::COMPOSITE_DEFAULT, + $position->getX(), + $position->getY() + ); + } + + return $image; + } + + protected function decodeWatermark(): Image + { + return $this->resolveDriverClass('InputHandler')->handle($this->element); + } + + protected function getPosition(Image $image, Image $watermark): Point + { + $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->align($this->position); + + return $image_size->relativePosition($watermark_size); + } +} diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index b2e4f71f5..c1adef145 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -176,4 +176,19 @@ public function align(string $position, int $offset_x = 0, int $offset_y = 0): s return $this; } + + /** + * Calculate the relative position to another Size + * based on the pivot point settings of both sizes. + * + * @param Size $size + * @return Point + */ + public function relativePosition(Size $size): Point + { + $x = $this->getPivot()->getX() - $size->getPivot()->getX(); + $y = $this->getPivot()->getY() - $size->getPivot()->getY(); + + return new Point($x, $y); + } } diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php new file mode 100644 index 000000000..bd1d3e0b0 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -0,0 +1,22 @@ +createTestImage('test.jpg'); + $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); + $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); + $this->assertEquals('32250d', $image->pickColor(300, 25)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php new file mode 100644 index 000000000..20e2f077a --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -0,0 +1,22 @@ +createTestImage('test.jpg'); + $this->assertEquals('fdbd42', $image->pickColor(300, 25)->toHex()); + $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); + $this->assertEquals('33260d', $image->pickColor(300, 25)->toHex()); + } +} From 66d12c74a2ff965a6f95b833570f45710564bc8f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 18:12:20 +0000 Subject: [PATCH 0183/1667] PHP 7.4 support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 93776f08d..b075794eb 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "php": "^8", + "php": "^7.4|^8", "intervention/gif": "dev-master", "intervention/mimesniffer": "^0.4.2" }, From cec4a37b76a8d20a0d1cd8f25fe8fd6b83c0eba2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 19:21:40 +0100 Subject: [PATCH 0184/1667] PHP 7.4 support --- src/Collection.php | 6 ++++-- src/Drivers/Abstract/Decoders/AbstractDecoder.php | 6 ++++-- src/Drivers/Abstract/Encoders/AbstractEncoder.php | 6 ++++-- src/Drivers/Gd/Frame.php | 11 +++++++++-- src/Drivers/Imagick/Frame.php | 11 +++++++++-- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 2c40af196..024718c5b 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -10,15 +10,17 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable { + protected $items = []; + /** * Create a collection. * * @param array $items * @return void */ - public function __construct(protected array $items = []) + public function __construct(array $items = []) { - // + $this->items = $items } /** diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 937400415..32a8070ba 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -10,9 +10,11 @@ abstract class AbstractDecoder { - public function __construct(protected ?AbstractDecoder $successor = null) + protected $successor = null; + + public function __construct(?AbstractDecoder $successor = null) { - // + $this->successor = $successor; } final public function handle($input): null|ImageInterface|ColorInterface diff --git a/src/Drivers/Abstract/Encoders/AbstractEncoder.php b/src/Drivers/Abstract/Encoders/AbstractEncoder.php index 31a53ac51..a557311f2 100644 --- a/src/Drivers/Abstract/Encoders/AbstractEncoder.php +++ b/src/Drivers/Abstract/Encoders/AbstractEncoder.php @@ -6,9 +6,11 @@ abstract class AbstractEncoder { - public function __construct(protected ?int $quality = null) + protected $quality; + + public function __construct(?int $quality = null) { - // + $this->quality = $quality; } /** diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 76803cb8f..7cbca2e7a 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -12,6 +12,13 @@ class Frame extends AbstractFrame implements FrameInterface { + /** + * Gd image representation of frame + * + * @var GdImage + */ + protected $core; + /** * Delay time in seconds after next frame is shown * @@ -40,9 +47,9 @@ class Frame extends AbstractFrame implements FrameInterface */ protected $offset_top = 0; - public function __construct(protected GdImage $core) + public function __construct(GdImage $core) { - // + $this->core = $core; } public function getCore(): GdImage diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 94cc9c680..b787f7732 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -12,9 +12,16 @@ class Frame extends AbstractFrame implements FrameInterface { - public function __construct(protected Imagick $core) + /** + * Imagick image representation of frame + * + * @var Imagick + */ + protected $core; + + public function __construct(Imagick $core) { - // + $this->core = $core; } public function getCore(): Imagick From f035494b3d8be2d62b69a48083ca8da787d05b26 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 19:35:26 +0100 Subject: [PATCH 0185/1667] Bugfix --- src/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Collection.php b/src/Collection.php index 024718c5b..996997267 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -20,7 +20,7 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable */ public function __construct(array $items = []) { - $this->items = $items + $this->items = $items; } /** From 304d158fb045cb5bfc58ff1bc8257afa5b35cdbd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 19:47:09 +0100 Subject: [PATCH 0186/1667] Bugfix --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 059ac16ce..c9576f2c6 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -35,6 +35,7 @@ public function apply(ImageInterface $image): ImageInterface $position = $this->getPosition($image, $watermark); foreach ($image as $frame) { + imagealphablending($frame->getCore(), true); imagecopy( $frame->getCore(), $watermark->getFrame()->getCore(), From 9ac8e765c2793b9246d8836d4beccb00405a82b1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 7 Nov 2021 11:37:42 +0000 Subject: [PATCH 0187/1667] Geometry refactoring --- src/Geometry/Resizer.php | 114 ++++++++++++++++++--------------- src/Geometry/Size.php | 7 ++ tests/Geometry/ResizerTest.php | 14 ++-- tests/Geometry/SizeTest.php | 43 +++++++++++++ 4 files changed, 119 insertions(+), 59 deletions(-) diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index d804194c9..6132d58c5 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -11,7 +11,7 @@ class Resizer * * @var SizeInterface */ - protected $size; + protected $original; /** * Target size @@ -20,20 +20,20 @@ class Resizer */ protected $target; - public function __construct(SizeInterface $size) + /** + * Create new instance + * + * @param SizeInterface $size + */ + public function __construct(SizeInterface $original) { - $this->size = $size; + $this->original = $original; $this->target = new Size(0, 0); } - public static function fromSize(SizeInterface $size): self - { - return new self($size); - } - - public function getSize(): SizeInterface + protected function copyOriginal(): SizeInterface { - return $this->size; + return new Size($this->original->getWidth(), $this->original->getHeight()); } protected function hasTargetWidth(): bool @@ -81,14 +81,32 @@ public function setTargetSizeByArray(array $arguments): self public function setTargetSize(SizeInterface $size): self { - $this->target = $size; + $this->target = new Size($size->getWidth(), $size->getHeight()); return $this; } + protected function getProportionalWidth(): int + { + if (! $this->hasTargetHeight()) { + return $this->original->getWidth(); + } + + return (int) round($this->target->getHeight() * $this->original->getAspectRatio()); + } + + protected function getProportionalHeight(): int + { + if (! $this->hasTargetWidth()) { + return $this->original->getHeight(); + } + + return (int) round($this->target->getWidth() / $this->original->getAspectRatio()); + } + public function resize(): SizeInterface { - $resized = clone $this->size; + $resized = $this->copyOriginal(); if ($this->hasTargetWidth()) { $resized->setWidth($this->target->getWidth()); @@ -103,92 +121,82 @@ public function resize(): SizeInterface public function resizeDown(): SizeInterface { + $resized = $this->copyOriginal(); + if ($this->hasTargetWidth()) { - $this->target->setWidth( - min($this->target->getWidth(), $this->size->getWidth()) + $resized->setWidth( + min($this->target->getWidth(), $this->original->getWidth()) ); } if ($this->hasTargetHeight()) { - $this->target->setHeight( - min($this->target->getHeight(), $this->size->getHeight()) + $resized->setHeight( + min($this->target->getHeight(), $this->original->getHeight()) ); } - return $this->resize(); + return $resized; } public function scale(): SizeInterface { + $resized = $this->copyOriginal(); + if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $this->target->setWidth(min( + $resized->setWidth(min( $this->getProportionalWidth(), $this->target->getWidth() )); - $this->target->setHeight(min( + $resized->setHeight(min( $this->getProportionalHeight(), $this->target->getHeight() )); } elseif ($this->hasTargetWidth()) { - $this->target->setHeight($this->getProportionalHeight()); + $resized->setWidth($this->target->getWidth()); + $resized->setHeight($this->getProportionalHeight()); } elseif ($this->hasTargetHeight()) { - $this->target->setWidth($this->getProportionalWidth()); + $resized->setWidth($this->getProportionalWidth()); + $resized->setHeight($this->target->getHeight()); } - return $this->resize(); + return $resized; } public function scaleDown(): SizeInterface { + $resized = $this->copyOriginal(); + if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $this->target->setWidth(min( + $resized->setWidth(min( $this->getProportionalWidth(), $this->target->getWidth(), - $this->size->getWidth() + $this->original->getWidth() )); - $this->target->setHeight(min( + $resized->setHeight(min( $this->getProportionalHeight(), $this->target->getHeight(), - $this->size->getHeight() + $this->original->getHeight() )); } elseif ($this->hasTargetWidth()) { - $this->target->setWidth(min( + $resized->setWidth(min( $this->target->getWidth(), - $this->size->getWidth() + $this->original->getWidth() )); - $this->target->setHeight(min( + $resized->setHeight(min( $this->getProportionalHeight(), - $this->size->getHeight() + $this->original->getHeight() )); } elseif ($this->hasTargetHeight()) { - $this->target->setWidth(min( + $resized->setWidth(min( $this->getProportionalWidth(), - $this->size->getWidth() + $this->original->getWidth() )); - $this->target->setHeight(min( + $resized->setHeight(min( $this->target->getHeight(), - $this->size->getHeight() + $this->original->getHeight() )); } - return $this->resize(); - } - - protected function getProportionalWidth(): int - { - if (! $this->hasTargetHeight()) { - return $this->size->getWidth(); - } - - return (int) round($this->target->getHeight() * $this->size->getAspectRatio()); - } - - protected function getProportionalHeight(): int - { - if (! $this->hasTargetWidth()) { - return $this->size->getHeight(); - } - - return (int) round($this->target->getWidth() / $this->size->getAspectRatio()); + return $resized; } } diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index c1adef145..70aee3e18 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -38,6 +38,13 @@ public function getPivot(): PointInterface return $this->pivot; } + public function setPivot(PointInterface $pivot): self + { + $this->pivot = $pivot; + + return $this; + } + public function setWidth(int $width): SizeInterface { $this->width = $width; diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 5395aafff..9c84ee91c 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -8,6 +8,13 @@ class ResizerTest extends TestCase { + public function testConstructor(): void + { + $size = new Size(300, 200); + $resizer = new Resizer($size); + $this->assertInstanceOf(Resizer::class, $resizer); + } + public function testSetTargetSizeByArray() { $resizer = new Resizer(new Size(300, 200)); @@ -63,22 +70,16 @@ public function testResize() $resizer = new Resizer($size); $resizer->width(150); $result = $resizer->resize(); - $original = $resizer->getSize(); $this->assertEquals(150, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); - $this->assertEquals(300, $original->getWidth()); - $this->assertEquals(200, $original->getHeight()); $size = new Size(300, 200); $resizer = new Resizer($size); $resizer->width(20); $resizer->height(10); $result = $resizer->resize(); - $original = $resizer->getSize(); $this->assertEquals(20, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); - $this->assertEquals(300, $original->getWidth()); - $this->assertEquals(200, $original->getHeight()); } public function testResizeDown() @@ -367,4 +368,5 @@ public function testScaleDown() $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } + } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 470902381..37ab296aa 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -143,4 +143,47 @@ public function testAlign(): void $this->assertInstanceOf(Size::class, $result); } + + public function testRelativePosition(): void + { + $container = new Size(800, 600); + $input = new Size(200, 100); + $container->align('top-left'); + $input->align('top-left'); + $pos = $container->relativePosition($input); + $this->assertEquals(0, $pos->getX()); + $this->assertEquals(0, $pos->getY()); + + $container = new Size(800, 600); + $input = new Size(200, 100); + $container->align('center'); + $input->align('top-left'); + $pos = $container->relativePosition($input); + $this->assertEquals(400, $pos->getX()); + $this->assertEquals(300, $pos->getY()); + + $container = new Size(800, 600); + $input = new Size(200, 100); + $container->align('bottom-right'); + $input->align('top-right'); + $pos = $container->relativePosition($input); + $this->assertEquals(600, $pos->getX()); + $this->assertEquals(600, $pos->getY()); + + $container = new Size(800, 600); + $input = new Size(200, 100); + $container->align('center'); + $input->align('center'); + $pos = $container->relativePosition($input); + $this->assertEquals(300, $pos->getX()); + $this->assertEquals(250, $pos->getY()); + + $container = new Size(100, 200); + $input = new Size(100, 100); + $container->align('center'); + $input->align('center'); + $pos = $container->relativePosition($input); + $this->assertEquals(0, $pos->getX()); + $this->assertEquals(50, $pos->getY()); + } } From de343e197947ca0cb108f6c570b0e9ead0f1ef3d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 7 Nov 2021 16:15:30 +0000 Subject: [PATCH 0188/1667] Renamed relativePosition --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 2 +- .../Imagick/Modifiers/PlaceModifier.php | 2 +- src/Geometry/Resizer.php | 47 +++++++++++++++++-- src/Geometry/Size.php | 2 +- tests/Geometry/ResizerTest.php | 1 - tests/Geometry/SizeTest.php | 12 ++--- 6 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index c9576f2c6..92f8aff1d 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -61,6 +61,6 @@ protected function getPosition(Image $image, Image $watermark): Point $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->align($this->position); - return $image_size->relativePosition($watermark_size); + return $image_size->getRelativePositionTo($watermark_size); } } diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index e91714bc6..efc8a1661 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -53,6 +53,6 @@ protected function getPosition(Image $image, Image $watermark): Point $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->align($this->position); - return $image_size->relativePosition($watermark_size); + return $image_size->getRelativePositionTo($watermark_size); } } diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 6132d58c5..345afef06 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -4,6 +4,43 @@ use Intervention\Image\Interfaces\SizeInterface; +/* + +modes: fill, contain, cover +resize($width, $height, $mode, $only_reduce) +resize(function($size) { + $size->width(300); + $size->contain(); + $size->reduce(); +}); + +- resize +- resizeDown +- scale +- scaleDown +- contain +- containDown +- cover +- coverDown + +- resize +- resizeDown +- scale +- scaleDown +- fit(contain|cover) +- fitDown(contain|cover) + +- resize +- resizeDown +- scale +- scaleDown +- fit +- fitDown +- pad +- padDown + + */ + class Resizer { /** @@ -31,11 +68,6 @@ public function __construct(SizeInterface $original) $this->target = new Size(0, 0); } - protected function copyOriginal(): SizeInterface - { - return new Size($this->original->getWidth(), $this->original->getHeight()); - } - protected function hasTargetWidth(): bool { return $this->target->getWidth() > 0; @@ -104,6 +136,11 @@ protected function getProportionalHeight(): int return (int) round($this->target->getWidth() / $this->original->getAspectRatio()); } + protected function copyOriginal(): SizeInterface + { + return new Size($this->original->getWidth(), $this->original->getHeight()); + } + public function resize(): SizeInterface { $resized = $this->copyOriginal(); diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 70aee3e18..735439f2e 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -191,7 +191,7 @@ public function align(string $position, int $offset_x = 0, int $offset_y = 0): s * @param Size $size * @return Point */ - public function relativePosition(Size $size): Point + public function getRelativePositionTo(Size $size): Point { $x = $this->getPivot()->getX() - $size->getPivot()->getX(); $y = $this->getPivot()->getY() - $size->getPivot()->getY(); diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 9c84ee91c..13f2219b5 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -368,5 +368,4 @@ public function testScaleDown() $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } - } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 37ab296aa..0dc55166a 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -144,13 +144,13 @@ public function testAlign(): void $this->assertInstanceOf(Size::class, $result); } - public function testRelativePosition(): void + public function testgetRelativePositionTo(): void { $container = new Size(800, 600); $input = new Size(200, 100); $container->align('top-left'); $input->align('top-left'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(0, $pos->getY()); @@ -158,7 +158,7 @@ public function testRelativePosition(): void $input = new Size(200, 100); $container->align('center'); $input->align('top-left'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(400, $pos->getX()); $this->assertEquals(300, $pos->getY()); @@ -166,7 +166,7 @@ public function testRelativePosition(): void $input = new Size(200, 100); $container->align('bottom-right'); $input->align('top-right'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(600, $pos->getX()); $this->assertEquals(600, $pos->getY()); @@ -174,7 +174,7 @@ public function testRelativePosition(): void $input = new Size(200, 100); $container->align('center'); $input->align('center'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(300, $pos->getX()); $this->assertEquals(250, $pos->getY()); @@ -182,7 +182,7 @@ public function testRelativePosition(): void $input = new Size(100, 100); $container->align('center'); $input->align('center'); - $pos = $container->relativePosition($input); + $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(50, $pos->getY()); } From aaaf78c7dc8147fb26308e42f670d912ce804e4c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 8 Nov 2021 16:04:14 +0000 Subject: [PATCH 0189/1667] Resizing --- src/Drivers/Abstract/AbstractImage.php | 7 + .../Gd/Modifiers/CropResizeModifier.php | 126 ++++++++++++++++++ src/Drivers/Gd/Modifiers/PlaceModifier.php | 4 +- .../Imagick/Modifiers/PlaceModifier.php | 4 +- src/Geometry/Size.php | 14 +- tests/Geometry/SizeTest.php | 73 +++++++--- 6 files changed, 203 insertions(+), 25 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/CropResizeModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index b315f0b1e..ab789ce3e 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -166,6 +166,13 @@ public function scaleDown(...$arguments): ImageInterface ); } + public function fit(int $width, int $height, string $position = 'center'): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\CropResizeModifier', $width, $height, $position) + ); + } + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/CropResizeModifier.php b/src/Drivers/Gd/Modifiers/CropResizeModifier.php new file mode 100644 index 000000000..a1b6539c9 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/CropResizeModifier.php @@ -0,0 +1,126 @@ +width = $width; + $this->height = $height; + $this->position = $position; + } + + public function apply(ImageInterface $image): ImageInterface + { + echo "
";
+        var_dump($this->getCropSize($image));
+        var_dump($this->getResizeSize($image));
+        echo "
"; + exit; + + // foreach ($image as $frame) { + // $this->modify($frame); + // } + + return $image; + } + + protected function getCropSize(ImageInterface $image): SizeInterface + { + $resizer = new Resizer(new Size($this->width, $this->height)); + $resizer->width($image->width()); + $resizer->height($image->height()); + + return $resizer->scale()->align($this->position); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + $resizer = new Resizer($this->getCropSize($image)); + $resizer->width($this->width); + $resizer->height($this->height); + + return $resizer->scale()->align($this->position); + } + + /** + * Wrapper function for 'imagecopyresampled' + * + * @param FrameInterface $frame + * @param int $dst_x + * @param int $dst_y + * @param int $src_x + * @param int $src_y + * @param int $dst_w + * @param int $dst_h + * @param int $src_w + * @param int $src_h + * @return void + */ + protected function modify(FrameInterface $frame): void + { + // create new image + $modified = imagecreatetruecolor( + $this->resizeTo->getWidth(), + $this->resizeTo->getHeight() + ); + + // get current image + $gd = $frame->getCore(); + + // preserve transparency + $transIndex = imagecolortransparent($gd); + + 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); + } + + // copy content from resource + $result = imagecopyresampled( + $modified, + $gd, + $this->resizeTo->getPivot()->getX(), + $this->resizeTo->getPivot()->getY(), + $this->cropTo->getPivot()->getX(), + $this->cropTo->getPivot()->getY(), + $this->resizeTo->getWidth(), + $this->resizeTo->getHeight(), + $this->cropTo->getWidth(), + $this->cropTo->getHeight() + ); + + imagedestroy($gd); + + // set new content as recource + $frame->setCore($modified); + } +} diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 92f8aff1d..596ee79f6 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -58,8 +58,8 @@ protected function decodeWatermark(): Image protected function getPosition(Image $image, Image $watermark): Point { - $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); - $watermark_size = $watermark->getSize()->align($this->position); + $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->alignPivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index efc8a1661..c043adf09 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -50,8 +50,8 @@ protected function decodeWatermark(): Image protected function getPosition(Image $image, Image $watermark): Point { - $image_size = $image->getSize()->align($this->position, $this->offset_x, $this->offset_y); - $watermark_size = $watermark->getSize()->align($this->position); + $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->alignPivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 735439f2e..18f6a8e3b 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -106,7 +106,7 @@ public function isPortrait(): bool * @param int $offset_y * @return Size */ - public function align(string $position, int $offset_x = 0, int $offset_y = 0): self + public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): self { switch (strtolower($position)) { case 'top': @@ -184,6 +184,18 @@ public function align(string $position, int $offset_x = 0, int $offset_y = 0): s return $this; } + public function alignPivotTo(Size $size, string $position): self + { + $reference = new Size($size->getWidth(), $size->getHeight()); + $reference->alignPivot($position); + + $this->alignPivot($position)->setPivot( + $reference->getRelativePositionTo($this) + ); + + return $this; + } + /** * Calculate the relative position to another Size * based on the pivot point settings of both sizes. diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 0dc55166a..40eb5ecba 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -99,89 +99,122 @@ public function testIsPortrait() $this->assertTrue($box->isPortrait()); } - public function testAlign(): void + public function testAlignPivot(): void { $box = new Size(640, 480); $this->assertEquals(0, $box->getPivot()->getX()); $this->assertEquals(0, $box->getPivot()->getY()); - $box->align('top-left', 3, 3); + $box->alignPivot('top-left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); $this->assertEquals(3, $box->getPivot()->getY()); - $box->align('top', 3, 3); + $box->alignPivot('top', 3, 3); $this->assertEquals(320, $box->getPivot()->getX()); $this->assertEquals(3, $box->getPivot()->getY()); - $box->align('top-right', 3, 3); + $box->alignPivot('top-right', 3, 3); $this->assertEquals(637, $box->getPivot()->getX()); $this->assertEquals(3, $box->getPivot()->getY()); - $box->align('left', 3, 3); + $box->alignPivot('left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); $this->assertEquals(240, $box->getPivot()->getY()); - $box->align('center', 3, 3); + $box->alignPivot('center', 3, 3); $this->assertEquals(323, $box->getPivot()->getX()); $this->assertEquals(243, $box->getPivot()->getY()); - $box->align('right', 3, 3); + $box->alignPivot('right', 3, 3); $this->assertEquals(637, $box->getPivot()->getX()); $this->assertEquals(240, $box->getPivot()->getY()); - $box->align('bottom-left', 3, 3); + $box->alignPivot('bottom-left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); - $box->align('bottom', 3, 3); + $box->alignPivot('bottom', 3, 3); $this->assertEquals(320, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); - $result = $box->align('bottom-right', 3, 3); + $result = $box->alignPivot('bottom-right', 3, 3); $this->assertEquals(637, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); $this->assertInstanceOf(Size::class, $result); } + public function testAlignPivotTo(): void + { + $container = new Size(800, 600); + $size = new Size(200, 100); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(300, $size->getPivot()->getX()); + $this->assertEquals(250, $size->getPivot()->getY()); + + $container = new Size(800, 600); + $size = new Size(100, 100); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(350, $size->getPivot()->getX()); + $this->assertEquals(250, $size->getPivot()->getY()); + + $container = new Size(800, 600); + $size = new Size(800, 600); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(0, $size->getPivot()->getX()); + $this->assertEquals(0, $size->getPivot()->getY()); + + $container = new Size(100, 100); + $size = new Size(800, 600); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(-350, $size->getPivot()->getX()); + $this->assertEquals(-250, $size->getPivot()->getY()); + + $container = new Size(100, 100); + $size = new Size(800, 600); + $size->alignPivotTo($container, 'bottom-right'); + $this->assertEquals(-700, $size->getPivot()->getX()); + $this->assertEquals(-500, $size->getPivot()->getY()); + } + public function testgetRelativePositionTo(): void { $container = new Size(800, 600); $input = new Size(200, 100); - $container->align('top-left'); - $input->align('top-left'); + $container->alignPivot('top-left'); + $input->alignPivot('top-left'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(0, $pos->getY()); $container = new Size(800, 600); $input = new Size(200, 100); - $container->align('center'); - $input->align('top-left'); + $container->alignPivot('center'); + $input->alignPivot('top-left'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(400, $pos->getX()); $this->assertEquals(300, $pos->getY()); $container = new Size(800, 600); $input = new Size(200, 100); - $container->align('bottom-right'); - $input->align('top-right'); + $container->alignPivot('bottom-right'); + $input->alignPivot('top-right'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(600, $pos->getX()); $this->assertEquals(600, $pos->getY()); $container = new Size(800, 600); $input = new Size(200, 100); - $container->align('center'); - $input->align('center'); + $container->alignPivot('center'); + $input->alignPivot('center'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(300, $pos->getX()); $this->assertEquals(250, $pos->getY()); $container = new Size(100, 200); $input = new Size(100, 100); - $container->align('center'); - $input->align('center'); + $container->alignPivot('center'); + $input->alignPivot('center'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(50, $pos->getY()); From 50972167e9b4e9936311a844bc0e12ba90e46d48 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 8 Nov 2021 16:41:41 +0000 Subject: [PATCH 0190/1667] refactoring --- src/Drivers/Abstract/AbstractImage.php | 4 +- .../Gd/Modifiers/CropResizeModifier.php | 70 ++++++++++--------- src/Geometry/Resizer.php | 10 +++ src/Traits/CanResizeGeometrically.php | 14 ++++ 4 files changed, 63 insertions(+), 35 deletions(-) create mode 100644 src/Traits/CanResizeGeometrically.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index ab789ce3e..e275881a9 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -168,8 +168,10 @@ public function scaleDown(...$arguments): ImageInterface public function fit(int $width, int $height, string $position = 'center'): ImageInterface { + $size = new Size($width, $height); + return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $width, $height, $position) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $size, $position) ); } diff --git a/src/Drivers/Gd/Modifiers/CropResizeModifier.php b/src/Drivers/Gd/Modifiers/CropResizeModifier.php index a1b6539c9..c03c441ab 100644 --- a/src/Drivers/Gd/Modifiers/CropResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/CropResizeModifier.php @@ -3,10 +3,12 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Geometry\Resizer; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanResizeGeometrically; /* @@ -22,48 +24,48 @@ class CropResizeModifier implements ModifierInterface { - protected $width; - protected $height; + use CanResizeGeometrically; + + protected $target; protected $position; - public function __construct(int $width, int $height, string $position) + public function __construct(SizeInterface $target, string $position = 'top-left') { - $this->width = $width; - $this->height = $height; + $this->target = $target; $this->position = $position; } public function apply(ImageInterface $image): ImageInterface { - echo "
";
-        var_dump($this->getCropSize($image));
-        var_dump($this->getResizeSize($image));
-        echo "
"; - exit; + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($image); - // foreach ($image as $frame) { - // $this->modify($frame); - // } + foreach ($image as $frame) { + $this->modify($frame, $crop, $resize); + } return $image; } protected function getCropSize(ImageInterface $image): SizeInterface { - $resizer = new Resizer(new Size($this->width, $this->height)); - $resizer->width($image->width()); - $resizer->height($image->height()); - - return $resizer->scale()->align($this->position); + $size = $this->resizeGeometrically($this->target) + ->toWidth($image->width()) + ->toHeight($image->height()) + ->scale(); + + return $size->alignPivotTo( + $image->getSize()->alignPivot($this->position), + $this->position + ); } protected function getResizeSize(ImageInterface $image): SizeInterface { - $resizer = new Resizer($this->getCropSize($image)); - $resizer->width($this->width); - $resizer->height($this->height); - - return $resizer->scale()->align($this->position); + return $this->resizeGeometrically($this->getCropSize($image)) + ->toWidth($this->target->getWidth()) + ->toHeight($this->target->getHeight()) + ->scale(); } /** @@ -80,12 +82,12 @@ protected function getResizeSize(ImageInterface $image): SizeInterface * @param int $src_h * @return void */ - protected function modify(FrameInterface $frame): void + protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image $modified = imagecreatetruecolor( - $this->resizeTo->getWidth(), - $this->resizeTo->getHeight() + $resize->getWidth(), + $resize->getHeight() ); // get current image @@ -108,14 +110,14 @@ protected function modify(FrameInterface $frame): void $result = imagecopyresampled( $modified, $gd, - $this->resizeTo->getPivot()->getX(), - $this->resizeTo->getPivot()->getY(), - $this->cropTo->getPivot()->getX(), - $this->cropTo->getPivot()->getY(), - $this->resizeTo->getWidth(), - $this->resizeTo->getHeight(), - $this->cropTo->getWidth(), - $this->cropTo->getHeight() + $resize->getPivot()->getX(), + $resize->getPivot()->getY(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $resize->getWidth(), + $resize->getHeight(), + $crop->getWidth(), + $crop->getHeight() ); imagedestroy($gd); diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 345afef06..92c43668a 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -92,6 +92,16 @@ public function height(int $height): self return $this; } + public function toWidth(int $width): self + { + return $this->height($width); + } + + public function toHeight(int $height): self + { + return $this->height($height); + } + public function setTargetSizeByArray(array $arguments): self { if (isset($arguments[0]) && is_callable($arguments[0])) { diff --git a/src/Traits/CanResizeGeometrically.php b/src/Traits/CanResizeGeometrically.php new file mode 100644 index 000000000..9a66a068a --- /dev/null +++ b/src/Traits/CanResizeGeometrically.php @@ -0,0 +1,14 @@ + Date: Mon, 8 Nov 2021 16:48:34 +0000 Subject: [PATCH 0191/1667] ResizeModifier & FitModifier relation --- src/Drivers/Abstract/AbstractImage.php | 2 +- ...CropResizeModifier.php => FitModifier.php} | 60 +------------------ src/Drivers/Gd/Modifiers/ResizeModifier.php | 55 +++++------------ 3 files changed, 16 insertions(+), 101 deletions(-) rename src/Drivers/Gd/Modifiers/{CropResizeModifier.php => FitModifier.php} (50%) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index e275881a9..f734c03b2 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -171,7 +171,7 @@ public function fit(int $width, int $height, string $position = 'center'): Image $size = new Size($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $size, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $size, $position) ); } diff --git a/src/Drivers/Gd/Modifiers/CropResizeModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php similarity index 50% rename from src/Drivers/Gd/Modifiers/CropResizeModifier.php rename to src/Drivers/Gd/Modifiers/FitModifier.php index c03c441ab..2f43650ce 100644 --- a/src/Drivers/Gd/Modifiers/CropResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -22,7 +22,7 @@ */ -class CropResizeModifier implements ModifierInterface +class FitModifier extends ResizeModifier implements ModifierInterface { use CanResizeGeometrically; @@ -67,62 +67,4 @@ protected function getResizeSize(ImageInterface $image): SizeInterface ->toHeight($this->target->getHeight()) ->scale(); } - - /** - * Wrapper function for 'imagecopyresampled' - * - * @param FrameInterface $frame - * @param int $dst_x - * @param int $dst_y - * @param int $src_x - * @param int $src_y - * @param int $dst_w - * @param int $dst_h - * @param int $src_w - * @param int $src_h - * @return void - */ - protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void - { - // create new image - $modified = imagecreatetruecolor( - $resize->getWidth(), - $resize->getHeight() - ); - - // get current image - $gd = $frame->getCore(); - - // preserve transparency - $transIndex = imagecolortransparent($gd); - - 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); - } - - // copy content from resource - $result = imagecopyresampled( - $modified, - $gd, - $resize->getPivot()->getX(), - $resize->getPivot()->getY(), - $crop->getPivot()->getX(), - $crop->getPivot()->getY(), - $resize->getWidth(), - $resize->getHeight(), - $crop->getWidth(), - $crop->getHeight() - ); - - imagedestroy($gd); - - // set new content as recource - $frame->setCore($modified); - } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 673fe9fca..1944e9aa9 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -9,11 +9,6 @@ class ResizeModifier implements ModifierInterface { - /** - * Target size - * - * @var SizeInterface - */ protected $target; public function __construct(SizeInterface $target) @@ -24,41 +19,19 @@ public function __construct(SizeInterface $target) public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - $framesize = $frame->getSize(); - $this->modify( - $frame, - 0, - 0, - 0, - 0, - $this->target->getWidth(), - $this->target->getHeight(), - $framesize->getWidth(), - $framesize->getHeight() - ); + $this->modify($frame, $frame->getSize(), $this->target); } return $image; } - /** - * Wrapper function for 'imagecopyresampled' - * - * @param Image $image - * @param int $dst_x - * @param int $dst_y - * @param int $src_x - * @param int $src_y - * @param int $dst_w - * @param int $dst_h - * @param int $src_w - * @param int $src_h - * @return boolean - */ - protected function modify(FrameInterface $frame, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) + protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image - $modified = imagecreatetruecolor($dst_w, $dst_h); + $modified = imagecreatetruecolor( + $resize->getWidth(), + $resize->getHeight() + ); // get current image $gd = $frame->getCore(); @@ -80,14 +53,14 @@ protected function modify(FrameInterface $frame, $dst_x, $dst_y, $src_x, $src_y, $result = imagecopyresampled( $modified, $gd, - $dst_x, - $dst_y, - $src_x, - $src_y, - $dst_w, - $dst_h, - $src_w, - $src_h + $resize->getPivot()->getX(), + $resize->getPivot()->getY(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $resize->getWidth(), + $resize->getHeight(), + $crop->getWidth(), + $crop->getHeight() ); imagedestroy($gd); From 585766b4af48ad91b084cf453e445d8c11717824 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 8 Nov 2021 19:52:45 +0100 Subject: [PATCH 0192/1667] FitModifier --- src/Drivers/Abstract/AbstractImage.php | 9 +++++++ src/Drivers/Gd/Modifiers/FitDownModifier.php | 22 ++++++++++++++++ src/Drivers/Gd/Modifiers/FitModifier.php | 27 +++++++++----------- src/Geometry/Resizer.php | 2 +- 4 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/FitDownModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index f734c03b2..a6a527197 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -175,6 +175,15 @@ public function fit(int $width, int $height, string $position = 'center'): Image ); } + public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface + { + $size = new Size($width, $height); + + return $this->modify( + $this->resolveDriverClass('Modifiers\FitDownModifier', $size, $position) + ); + } + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/FitDownModifier.php b/src/Drivers/Gd/Modifiers/FitDownModifier.php new file mode 100644 index 000000000..143b4bef1 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FitDownModifier.php @@ -0,0 +1,22 @@ +resizeGeometrically($this->getCropSize($image)) + ->toWidth($this->target->getWidth()) + ->toHeight($this->target->getHeight()) + ->scaleDown(); + } +} diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 2f43650ce..ba1702ec8 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -10,18 +10,6 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanResizeGeometrically; -/* - -# contain -1. Scale (keep aspect ratio) Original to fit Target -2. Scale (keep aspect ratio) Up/Down to fit Target (obsolete) - -# cover -1. Scale (keep aspect ratio) Target to fit Original -2. Scale (keep aspect ratio) Up/Down to fit Target - - */ - class FitModifier extends ResizeModifier implements ModifierInterface { use CanResizeGeometrically; @@ -49,13 +37,22 @@ public function apply(ImageInterface $image): ImageInterface protected function getCropSize(ImageInterface $image): SizeInterface { + $imagesize = $image->getSize(); + + // auto height $size = $this->resizeGeometrically($this->target) - ->toWidth($image->width()) - ->toHeight($image->height()) + ->toWidth($imagesize->getWidth()) + ->scale(); + + if (!$size->fitsInto($imagesize)) { + // auto width + $size = $this->resizeGeometrically($this->target) + ->toHeight($imagesize->getHeight()) ->scale(); + } return $size->alignPivotTo( - $image->getSize()->alignPivot($this->position), + $imagesize->alignPivot($this->position), $this->position ); } diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 92c43668a..b9b79923c 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -94,7 +94,7 @@ public function height(int $height): self public function toWidth(int $width): self { - return $this->height($width); + return $this->width($width); } public function toHeight(int $height): self From e168824057957ab1e0b25ae04dbf21386bfee4f6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 9 Nov 2021 14:17:18 +0000 Subject: [PATCH 0193/1667] Fit & Resize Modifier --- src/Drivers/Gd/Modifiers/FitModifier.php | 15 --------------- src/Drivers/Gd/Modifiers/ResizeModifier.php | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index ba1702ec8..87a06059f 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -12,9 +12,6 @@ class FitModifier extends ResizeModifier implements ModifierInterface { - use CanResizeGeometrically; - - protected $target; protected $position; public function __construct(SizeInterface $target, string $position = 'top-left') @@ -23,18 +20,6 @@ public function __construct(SizeInterface $target, string $position = 'top-left' $this->position = $position; } - public function apply(ImageInterface $image): ImageInterface - { - $crop = $this->getCropSize($image); - $resize = $this->getResizeSize($image); - - foreach ($image as $frame) { - $this->modify($frame, $crop, $resize); - } - - return $image; - } - protected function getCropSize(ImageInterface $image): SizeInterface { $imagesize = $image->getSize(); diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 1944e9aa9..a9f0ab26a 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -6,9 +6,12 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanResizeGeometrically; class ResizeModifier implements ModifierInterface { + use CanResizeGeometrically; + protected $target; public function __construct(SizeInterface $target) @@ -18,13 +21,26 @@ public function __construct(SizeInterface $target) public function apply(ImageInterface $image): ImageInterface { + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($image); + foreach ($image as $frame) { - $this->modify($frame, $frame->getSize(), $this->target); + $this->modify($frame, $crop, $resize); } return $image; } + protected function getCropSize(ImageInterface $image): SizeInterface + { + return $image->getSize(); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return $this->target; + } + protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image From 84e946a588c344d46dc00e16b67518cf6a492c10 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 9 Nov 2021 14:59:59 +0000 Subject: [PATCH 0194/1667] Modifiers --- .../Modifiers/AbstractResizeModifier.php | 30 ++++++++++++++++++ src/Drivers/Gd/Modifiers/FitDownModifier.php | 4 --- src/Drivers/Gd/Modifiers/FitModifier.php | 4 --- src/Drivers/Gd/Modifiers/ResizeModifier.php | 22 ++----------- .../Imagick/Modifiers/ResizeModifier.php | 31 ++++++++++--------- tests/Geometry/SizeTest.php | 17 ++++++++++ 6 files changed, 65 insertions(+), 43 deletions(-) create mode 100644 src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php diff --git a/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php b/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php new file mode 100644 index 000000000..9f9b04f25 --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php @@ -0,0 +1,30 @@ +target = $target; + } + + protected function getCropSize(ImageInterface $image): SizeInterface + { + return $image->getSize(); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return $this->target; + } +} diff --git a/src/Drivers/Gd/Modifiers/FitDownModifier.php b/src/Drivers/Gd/Modifiers/FitDownModifier.php index 143b4bef1..18802f22c 100644 --- a/src/Drivers/Gd/Modifiers/FitDownModifier.php +++ b/src/Drivers/Gd/Modifiers/FitDownModifier.php @@ -2,13 +2,9 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Geometry\Resizer; -use Intervention\Image\Geometry\Size; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Traits\CanResizeGeometrically; class FitDownModifier extends FitModifier implements ModifierInterface { diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 87a06059f..6910381cd 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -2,13 +2,9 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Geometry\Resizer; -use Intervention\Image\Geometry\Size; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Traits\CanResizeGeometrically; class FitModifier extends ResizeModifier implements ModifierInterface { diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index a9f0ab26a..ff9a185b7 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -2,23 +2,15 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractResizeModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanResizeGeometrically; -class ResizeModifier implements ModifierInterface +class ResizeModifier extends AbstractResizeModifier implements ModifierInterface { - use CanResizeGeometrically; - - protected $target; - - public function __construct(SizeInterface $target) - { - $this->target = $target; - } - public function apply(ImageInterface $image): ImageInterface { $crop = $this->getCropSize($image); @@ -31,16 +23,6 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - protected function getCropSize(ImageInterface $image): SizeInterface - { - return $image->getSize(); - } - - protected function getResizeSize(ImageInterface $image): SizeInterface - { - return $this->target; - } - protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index ee9de7557..bcccd01f9 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -2,31 +2,32 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractResizeModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -class ResizeModifier implements ModifierInterface +class ResizeModifier extends AbstractResizeModifier implements ModifierInterface { - /** - * Target size - * - * @var SizeInterface - */ - protected $target; - - public function __construct(SizeInterface $target) - { - $this->target = $target; - } - public function apply(ImageInterface $image): ImageInterface { + $resize = $this->getResizeSize($image); + $crop = $this->getResizeSize($image); + $shouldCrop = $crop != $image->getSize(); foreach ($image as $frame) { + if ($shouldCrop) { + $frame->getCore()->cropImage( + $crop->getWidth(), + $crop->getHeight(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY() + ); + } + $frame->getCore()->scaleImage( - $this->target->getWidth(), - $this->target->getHeight() + $resize->getWidth(), + $resize->getHeight() ); } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 40eb5ecba..4551d28d8 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -20,6 +20,23 @@ public function testConstructor() $this->assertEquals(200, $size->getHeight()); } + public function testCompareSizes(): void + { + $size1 = new Size(300, 200); + $size2 = new Size(300, 200); + $size2a = new Size(300, 200, new Point(1, 1)); + $size2b = new Size(300, 200, new Point(1, 1)); + $size3 = new Size(300, 201); + $size4 = new Size(301, 200); + + $this->assertTrue($size1 == $size2); + $this->assertTrue($size2a == $size2b); + $this->assertFalse($size2 == $size2a); + $this->assertFalse($size2 == $size3); + $this->assertFalse($size2 == $size4); + $this->assertFalse($size3 == $size4); + } + public function testGetWidth() { $size = new Size(800, 600); From 45205805110c4e75cf006be95b4c51abe779c04e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 10 Nov 2021 13:50:23 +0000 Subject: [PATCH 0195/1667] Resizer refactoring --- src/Geometry/Resizer.php | 73 ++++++----- tests/Geometry/ResizerTest.php | 217 ++++++++++++++++----------------- 2 files changed, 144 insertions(+), 146 deletions(-) diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index b9b79923c..71885ec01 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -62,9 +62,8 @@ class Resizer * * @param SizeInterface $size */ - public function __construct(SizeInterface $original) + public function __construct() { - $this->original = $original; $this->target = new Size(0, 0); } @@ -128,32 +127,32 @@ public function setTargetSize(SizeInterface $size): self return $this; } - protected function getProportionalWidth(): int + public function toSize(SizeInterface $size): self + { + return $this->setTargetSize($size); + } + + protected function getProportionalWidth(SizeInterface $size): int { if (! $this->hasTargetHeight()) { - return $this->original->getWidth(); + return $size->getWidth(); } - return (int) round($this->target->getHeight() * $this->original->getAspectRatio()); + return (int) round($this->target->getHeight() * $size->getAspectRatio()); } - protected function getProportionalHeight(): int + protected function getProportionalHeight(SizeInterface $size): int { if (! $this->hasTargetWidth()) { - return $this->original->getHeight(); + return $size->getHeight(); } - return (int) round($this->target->getWidth() / $this->original->getAspectRatio()); - } - - protected function copyOriginal(): SizeInterface - { - return new Size($this->original->getWidth(), $this->original->getHeight()); + return (int) round($this->target->getWidth() / $size->getAspectRatio()); } - public function resize(): SizeInterface + public function resize(SizeInterface $size): SizeInterface { - $resized = $this->copyOriginal(); + $resized = clone $size; if ($this->hasTargetWidth()) { $resized->setWidth($this->target->getWidth()); @@ -166,81 +165,81 @@ public function resize(): SizeInterface return $resized; } - public function resizeDown(): SizeInterface + public function resizeDown(SizeInterface $size): SizeInterface { - $resized = $this->copyOriginal(); + $resized = clone $size; if ($this->hasTargetWidth()) { $resized->setWidth( - min($this->target->getWidth(), $this->original->getWidth()) + min($this->target->getWidth(), $size->getWidth()) ); } if ($this->hasTargetHeight()) { $resized->setHeight( - min($this->target->getHeight(), $this->original->getHeight()) + min($this->target->getHeight(), $size->getHeight()) ); } return $resized; } - public function scale(): SizeInterface + public function scale(SizeInterface $size): SizeInterface { - $resized = $this->copyOriginal(); + $resized = clone $size; if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( - $this->getProportionalWidth(), + $this->getProportionalWidth($size), $this->target->getWidth() )); $resized->setHeight(min( - $this->getProportionalHeight(), + $this->getProportionalHeight($size), $this->target->getHeight() )); } elseif ($this->hasTargetWidth()) { $resized->setWidth($this->target->getWidth()); - $resized->setHeight($this->getProportionalHeight()); + $resized->setHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { - $resized->setWidth($this->getProportionalWidth()); + $resized->setWidth($this->getProportionalWidth($size)); $resized->setHeight($this->target->getHeight()); } return $resized; } - public function scaleDown(): SizeInterface + public function scaleDown(SizeInterface $size): SizeInterface { - $resized = $this->copyOriginal(); + $resized = clone $size; if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( - $this->getProportionalWidth(), + $this->getProportionalWidth($size), $this->target->getWidth(), - $this->original->getWidth() + $size->getWidth() )); $resized->setHeight(min( - $this->getProportionalHeight(), + $this->getProportionalHeight($size), $this->target->getHeight(), - $this->original->getHeight() + $size->getHeight() )); } elseif ($this->hasTargetWidth()) { $resized->setWidth(min( $this->target->getWidth(), - $this->original->getWidth() + $size->getWidth() )); $resized->setHeight(min( - $this->getProportionalHeight(), - $this->original->getHeight() + $this->getProportionalHeight($size), + $size->getHeight() )); } elseif ($this->hasTargetHeight()) { $resized->setWidth(min( - $this->getProportionalWidth(), - $this->original->getWidth() + $this->getProportionalWidth($size), + $size->getWidth() )); $resized->setHeight(min( $this->target->getHeight(), - $this->original->getHeight() + $size->getHeight() )); } diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 13f2219b5..6c67528d7 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -8,76 +8,75 @@ class ResizerTest extends TestCase { - public function testConstructor(): void + public function testSetTargetSizeByArray() { $size = new Size(300, 200); - $resizer = new Resizer($size); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([800, 600]); $this->assertInstanceOf(Resizer::class, $resizer); - } + $this->assertEquals(800, $resizer->resize($size)->getWidth()); + $this->assertEquals(600, $resizer->resize($size)->getHeight()); - public function testSetTargetSizeByArray() - { - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([800, 600]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(800, $result->resize()->getWidth()); - $this->assertEquals(600, $result->resize()->getHeight()); - - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([800]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(800, $result->resize()->getWidth()); - $this->assertEquals(200, $result->resize()->getHeight()); - - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([function ($size) { + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([800]); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(800, $resizer->resize($size)->getWidth()); + $this->assertEquals(200, $resizer->resize($size)->getHeight()); + + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([function ($size) { $size->width(80); $size->height(40); }]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(80, $result->resize()->getWidth()); - $this->assertEquals(40, $result->resize()->getHeight()); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(80, $resizer->resize($size)->getWidth()); + $this->assertEquals(40, $resizer->resize($size)->getHeight()); - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([function ($size) { + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([function ($size) { $size->width(80); }]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(80, $result->resize()->getWidth()); - $this->assertEquals(200, $result->resize()->getHeight()); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(80, $resizer->resize($size)->getWidth()); + $this->assertEquals(200, $resizer->resize($size)->getHeight()); - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSizeByArray([function ($size) { + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSizeByArray([function ($size) { $size->height(10); }]); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(300, $result->resize()->getWidth()); - $this->assertEquals(10, $result->resize()->getHeight()); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(300, $resizer->resize($size)->getWidth()); + $this->assertEquals(10, $resizer->resize($size)->getHeight()); } public function testSetTargetSize(): void { - $resizer = new Resizer(new Size(300, 200)); - $result = $resizer->setTargetSize(new Size(200, 100)); - $this->assertInstanceOf(Resizer::class, $result); - $this->assertEquals(200, $result->resize()->getWidth()); - $this->assertEquals(100, $result->resize()->getHeight()); + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->setTargetSize(new Size(200, 100)); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(200, $resizer->resize($size)->getWidth()); + $this->assertEquals(100, $resizer->resize($size)->getHeight()); } public function testResize() { $size = new Size(300, 200); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(150); - $result = $resizer->resize(); + $result = $resizer->resize($size); $this->assertEquals(150, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); $size = new Size(300, 200); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(20); $resizer->height(10); - $result = $resizer->resize(); + $result = $resizer->resize($size); $this->assertEquals(20, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } @@ -86,53 +85,53 @@ public function testResizeDown() { // 800x600 > 1000x2000 = 800x600 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(2000); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); // 800x600 > 400x1000 = 400x600 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(1000); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); // 800x600 > 1000x400 = 800x400 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(400); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(400, $result->getHeight()); // 800x600 > 400x300 = 400x300 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(300); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); // 800x600 > 1000xnull = 800x600 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); // 800x600 > nullx1000 = 800x600 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(1000); - $result = $resizer->resizeDown(); + $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); } @@ -141,136 +140,136 @@ public function testScale() { // 800x600 > 1000x2000 = 1000x750 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(2000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(1000, $result->getWidth()); $this->assertEquals(750, $result->getHeight()); // 800x600 > 2000x1000 = 1333x1000 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(2000); $resizer->height(1000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(1333, $result->getWidth()); $this->assertEquals(1000, $result->getHeight()); // // 800x600 > nullx3000 = 4000x3000 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(3000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(3000, $result->getHeight()); // // 800x600 > 8000xnull = 8000x6000 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(8000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(8000, $result->getWidth()); $this->assertEquals(6000, $result->getHeight()); // // 800x600 > 100x400 = 100x75 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(100); $resizer->height(400); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(100, $result->getWidth()); $this->assertEquals(75, $result->getHeight()); // // 800x600 > 400x100 = 133x100 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(100); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(133, $result->getWidth()); $this->assertEquals(100, $result->getHeight()); // // 800x600 > nullx300 = 400x300 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(300); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); // // 800x600 > 80xnull = 80x60 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(80); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(80, $result->getWidth()); $this->assertEquals(60, $result->getHeight()); // // 640x480 > 225xnull = 225x169 $size = new Size(640, 480); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(225); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(169, $result->getHeight()); // // 640x480 > 223xnull = 223x167 $size = new Size(640, 480); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(223); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(223, $result->getWidth()); $this->assertEquals(167, $result->getHeight()); // // 600x800 > 300x300 = 225x300 $size = new Size(600, 800); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(300); $resizer->height(300); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); // // 800x600 > 400x10 = 13x10 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(10); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); // // 800x600 > 1000x1200 = 1000x750 $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(1200); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(1000, $result->getWidth()); $this->assertEquals(750, $result->getHeight()); $size = new Size(12000, 12); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(4000); $resizer->height(3000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(4, $result->getHeight()); $size = new Size(12, 12000); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(4000); $resizer->height(3000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(3, $result->getWidth()); $this->assertEquals(3000, $result->getHeight()); $size = new Size(12000, 6000); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(4000); $resizer->height(3000); - $result = $resizer->scale(); + $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(2000, $result->getHeight()); } @@ -278,93 +277,93 @@ public function testScale() public function testScaleDown() { $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(2000); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(600); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); $resizer->height(300); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(1000); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(300); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(1000); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->height(1000); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(100); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(100, $result->getWidth()); $this->assertEquals(75, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(300); $resizer->height(200); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(267, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); $size = new Size(600, 800); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(300); $resizer->height(300); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); - $resizer = new Resizer($size); + $resizer = new Resizer(); $resizer->width(400); $resizer->height(10); - $result = $resizer->scaleDown(); + $result = $resizer->scaleDown($size); $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } From 372f84ef4c8cbf9251b722eb88b99c07d8c64238 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 10 Nov 2021 18:47:12 +0000 Subject: [PATCH 0196/1667] Resizing --- src/Drivers/Abstract/AbstractImage.php | 47 +++++++---- ...izeModifier.php => CropResizeModifier.php} | 19 +++-- src/Drivers/Gd/Modifiers/FitModifier.php | 10 +-- src/Geometry/Resizer.php | 72 +++++++++++++++++ .../Drivers/Gd/Modifiers/FitModifierTest.php | 28 +++++++ tests/Geometry/ResizerTest.php | 81 +++++++++++++++++++ 6 files changed, 226 insertions(+), 31 deletions(-) rename src/Drivers/Gd/Modifiers/{ResizeModifier.php => CropResizeModifier.php} (82%) create mode 100644 tests/Drivers/Gd/Modifiers/FitModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index a6a527197..447de89c2 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -65,11 +65,6 @@ public function getSize(): SizeInterface return new Size($this->width(), $this->height()); } - public function getResizer(): Resizer - { - return new Resizer($this->getSize()); - } - public function isAnimated(): bool { return $this->getFrames()->count() > 1; @@ -132,46 +127,66 @@ public function pickColors(int $x, int $y): Collection public function resize(...$arguments): ImageInterface { - $size = $this->getResizer()->setTargetSizeByArray($arguments)->resize(); + $crop = $this->getSize(); + $resize = Resizer::make() + ->setTargetSizeByArray($arguments) + ->resize($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) ); } public function resizeDown(...$arguments): ImageInterface { - $size = $this->getResizer()->setTargetSizeByArray($arguments)->resizeDown(); + $crop = $this->getSize(); + $resize = Resizer::make() + ->setTargetSizeByArray($arguments) + ->resizeDown($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) ); } public function scale(...$arguments): ImageInterface { - $size = $this->getResizer()->setTargetSizeByArray($arguments)->scale(); + $crop = $this->getSize(); + $resize = Resizer::make() + ->setTargetSizeByArray($arguments) + ->scale($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) ); } public function scaleDown(...$arguments): ImageInterface { - $size = $this->getResizer()->setTargetSizeByArray($arguments)->scaleDown(); + $crop = $this->getSize(); + $resize = Resizer::make() + ->setTargetSizeByArray($arguments) + ->scaleDown($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $size) ); } public function fit(int $width, int $height, string $position = 'center'): ImageInterface { - $size = new Size($width, $height); + // crop + $crop = Resizer::make() + ->toSize($this->getSize()) + ->contain(new Size($width, $height)); + $crop = Resizer::make() + ->toSize($crop) + ->crop($this->getSize(), $position); + + $resize = new Size($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $size, $position) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) ); } @@ -180,7 +195,7 @@ public function fitDown(int $width, int $height, string $position = 'center'): I $size = new Size($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\FitDownModifier', $size, $position) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) ); } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/CropResizeModifier.php similarity index 82% rename from src/Drivers/Gd/Modifiers/ResizeModifier.php rename to src/Drivers/Gd/Modifiers/CropResizeModifier.php index ff9a185b7..efccf46ce 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/CropResizeModifier.php @@ -2,22 +2,29 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Abstract\Modifiers\AbstractResizeModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanResizeGeometrically; -class ResizeModifier extends AbstractResizeModifier implements ModifierInterface +class CropResizeModifier implements ModifierInterface { - public function apply(ImageInterface $image): ImageInterface + protected $crop; + protected $resize; + protected $position; + + public function __construct(SizeInterface $crop, SizeInterface $resize, string $position = 'top-left') { - $crop = $this->getCropSize($image); - $resize = $this->getResizeSize($image); + $this->crop = $crop; + $this->resize = $resize; + $this->position = $position; + } + public function apply(ImageInterface $image): ImageInterface + { foreach ($image as $frame) { - $this->modify($frame, $crop, $resize); + $this->modify($frame, $this->crop, $this->resize); } return $image; diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 6910381cd..9c69bc4f6 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -6,16 +6,8 @@ use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -class FitModifier extends ResizeModifier implements ModifierInterface +class FitModifier extends CropResizeModifier implements ModifierInterface { - protected $position; - - public function __construct(SizeInterface $target, string $position = 'top-left') - { - $this->target = $target; - $this->position = $position; - } - protected function getCropSize(ImageInterface $image): SizeInterface { $imagesize = $image->getSize(); diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 71885ec01..5b35f366a 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -67,6 +67,17 @@ public function __construct() $this->target = new Size(0, 0); } + public static function make(callable $callback = null): self + { + $resizer = new self(); + + if (is_callable($callback)) { + $callback($resizer); + } + + return $resizer; + } + protected function hasTargetWidth(): bool { return $this->target->getWidth() > 0; @@ -245,4 +256,65 @@ public function scaleDown(SizeInterface $size): SizeInterface return $resized; } + + /** + * Scale given size to cover target size + * + * @param SizeInterface $size Size to be resized + * @return SizeInterface + */ + public function cover(SizeInterface $size): SizeInterface + { + $resized = clone $size; + + // auto height + $resized->setWidth($this->target->getWidth()); + $resized->setHeight($this->getProportionalHeight($size)); + + if ($resized->fitsInto($this->target)) { + // auto width + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->target->getHeight()); + } + + return $resized; + } + + /** + * Scale given size to contain target size + * + * @param SizeInterface $size Size to be resized + * @return SizeInterface + */ + public function contain(SizeInterface $size): SizeInterface + { + $resized = clone $size; + + // auto height + $resized->setWidth($this->target->getWidth()); + $resized->setHeight($this->getProportionalHeight($size)); + + if (!$resized->fitsInto($this->target)) { + // auto width + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->target->getHeight()); + } + + return $resized; + } + + /** + * Crop target size out of given size at given position (i.e. move the pivot point) + * + * @param SizeInterface $size + * @param string $position + * @return SizeInterface + */ + public function crop(SizeInterface $size, string $position = 'top-left'): SizeInterface + { + return $this->resize($size)->alignPivotTo( + $size->alignPivot($position), + $position + ); + } } diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php new file mode 100644 index 000000000..08913c9a6 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -0,0 +1,28 @@ +createTestImage('test.jpg'); + $image->resize(800, 600); + $this->assertEquals(800, $image->width()); + $this->assertEquals(600, $image->height()); + + $image->fit(100, 100); + + // $image->modify(new FitModifier(new Size(100, 100))); + // $this->assertEquals(30, $image->width()); + // $this->assertEquals(20, $image->height()); + } +} diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 6c67528d7..fbe237ad5 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Geometry; +use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use PHPUnit\Framework\TestCase; @@ -367,4 +368,84 @@ public function testScaleDown() $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); } + + /** + * @dataProvider coverDataProvider + */ + public function testCover($origin, $target, $result): void + { + $resizer = new Resizer(); + $resizer->toSize($target); + $resized = $resizer->cover($origin); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); + } + + public function coverDataProvider(): array + { + return [ + [new Size(800, 600), new Size(100, 100), new Size(133, 100)], + [new Size(800, 600), new Size(200, 100), new Size(200, 150)], + [new Size(800, 600), new Size(100, 200), new Size(267, 200)], + [new Size(800, 600), new Size(2000, 10), new Size(2000, 1500)], + [new Size(800, 600), new Size(10, 2000), new Size(2667, 2000)], + [new Size(800, 600), new Size(800, 600), new Size(800, 600)], + [new Size(400, 300), new Size(120, 120), new Size(160, 120)], + [new Size(600, 800), new Size(100, 100), new Size(100, 133)], + ]; + } + + /** + * @dataProvider containDataProvider + */ + public function testContain($origin, $target, $result): void + { + $resizer = new Resizer(); + $resizer->toSize($target); + $resized = $resizer->contain($origin); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); + } + + public function containDataProvider(): array + { + return [ + [new Size(800, 600), new Size(100, 100), new Size(100, 75)], + [new Size(800, 600), new Size(200, 100), new Size(133, 100)], + [new Size(800, 600), new Size(100, 200), new Size(100, 75)], + [new Size(800, 600), new Size(2000, 10), new Size(13, 10)], + [new Size(800, 600), new Size(10, 2000), new Size(10, 8)], + [new Size(800, 600), new Size(800, 600), new Size(800, 600)], + [new Size(400, 300), new Size(120, 120), new Size(120, 90)], + [new Size(600, 800), new Size(100, 100), new Size(75, 100)], + ]; + } + + /** + * @dataProvider cropDataProvider + */ + public function testCrop($origin, $target, $position, $result): void + { + $resizer = new Resizer(); + $resizer->toSize($target); + $resized = $resizer->crop($origin, $position); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); + $this->assertEquals($result->getPivot()->getX(), $resized->getPivot()->getX()); + $this->assertEquals($result->getPivot()->getY(), $resized->getPivot()->getY()); + } + + public function cropDataProvider(): array + { + return [ + [new Size(800, 600), new Size(100, 100), 'center', new Size(100, 100, new Point(350, 250))], + [new Size(800, 600), new Size(200, 100), 'center', new Size(200, 100, new Point(300, 250))], + [new Size(800, 600), new Size(100, 200), 'center', new Size(100, 200, new Point(350, 200))], + [new Size(800, 600), new Size(2000, 10), 'center', new Size(2000, 10, new Point(-600, 295))], + [new Size(800, 600), new Size(10, 2000), 'center', new Size(10, 2000, new Point(395, -700))], + [new Size(800, 600), new Size(800, 600), 'center', new Size(800, 600, new Point(0, 0))], + [new Size(400, 300), new Size(120, 120), 'center', new Size(120, 120, new Point(140, 90))], + [new Size(600, 800), new Size(100, 100), 'center', new Size(100, 100, new Point(250, 350))], + ]; + } } From afa86daac422a25baaa6843fb17e044da73f7e5f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 14:11:01 +0000 Subject: [PATCH 0197/1667] Resizing --- src/Drivers/Abstract/AbstractImage.php | 32 +++++++---- src/Geometry/Resizer.php | 17 ++++-- src/Geometry/Size.php | 39 +++++++++++++ tests/Geometry/ResizerTest.php | 2 + tests/Geometry/SizeTest.php | 80 ++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 16 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 447de89c2..9949a07f4 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -169,21 +169,23 @@ public function scaleDown(...$arguments): ImageInterface ->scaleDown($crop); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $size) + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) ); } public function fit(int $width, int $height, string $position = 'center'): ImageInterface { - // crop - $crop = Resizer::make() - ->toSize($this->getSize()) - ->contain(new Size($width, $height)); - $crop = Resizer::make() - ->toSize($crop) - ->crop($this->getSize(), $position); + $imagesize = $this->getSize(); + + // crop + $crop = new Size($width, $height); + $crop = $crop->contain($imagesize)->alignPivotTo( + $imagesize->alignPivot($position), + $position + ); - $resize = new Size($width, $height); + // resize + $resize = $crop->scale($width, $height); return $this->modify( $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) @@ -192,7 +194,17 @@ public function fit(int $width, int $height, string $position = 'center'): Image public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface { - $size = new Size($width, $height); + $imagesize = $this->getSize(); + + // crop + $crop = new Size($width, $height); + $crop = $crop->contain($imagesize)->alignPivotTo( + $imagesize->alignPivot($position), + $position + ); + + // resize + $resize = $crop->scaleDown($width, $height); return $this->modify( $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 5b35f366a..e704314b0 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Geometry; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\SizeInterface; /* @@ -120,6 +121,10 @@ public function setTargetSizeByArray(array $arguments): self return $this; } + if (isset($arguments[0]) && is_a($arguments[0], Size::class)) { + return $this->toSize($arguments[0]); + } + if (isset($arguments[0]) && is_numeric($arguments[0])) { $this->width($arguments[0]); } @@ -163,7 +168,7 @@ protected function getProportionalHeight(SizeInterface $size): int public function resize(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth()) { $resized->setWidth($this->target->getWidth()); @@ -178,7 +183,7 @@ public function resize(SizeInterface $size): SizeInterface public function resizeDown(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth()) { $resized->setWidth( @@ -197,7 +202,7 @@ public function resizeDown(SizeInterface $size): SizeInterface public function scale(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( @@ -221,7 +226,7 @@ public function scale(SizeInterface $size): SizeInterface public function scaleDown(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( @@ -265,7 +270,7 @@ public function scaleDown(SizeInterface $size): SizeInterface */ public function cover(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); // auto height $resized->setWidth($this->target->getWidth()); @@ -288,7 +293,7 @@ public function cover(SizeInterface $size): SizeInterface */ public function contain(SizeInterface $size): SizeInterface { - $resized = clone $size; + $resized = new Size($size->getWidth(), $size->getHeight()); // auto height $resized->setWidth($this->target->getWidth()); diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 18f6a8e3b..3f4e76a46 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Geometry; +use Intervention\Image\Geometry\Resizer; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -210,4 +211,42 @@ public function getRelativePositionTo(Size $size): Point return new Point($x, $y); } + + protected function getResizer(...$arguments): Resizer + { + $resizer = new Resizer(); + $resizer->setTargetSizeByArray($arguments[0]); + + return $resizer; + } + + public function resize(...$arguments): self + { + return $this->getResizer($arguments)->resize($this); + } + + public function resizeDown(...$arguments): self + { + return $this->getResizer($arguments)->resizeDown($this); + } + + public function scale(...$arguments): self + { + return $this->getResizer($arguments)->scale($this); + } + + public function scaleDown(...$arguments): self + { + return $this->getResizer($arguments)->scaleDown($this); + } + + public function cover(...$arguments): self + { + return $this->getResizer($arguments)->cover($this); + } + + public function contain(...$arguments): self + { + return $this->getResizer($arguments)->contain($this); + } } diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index fbe237ad5..70ec340a3 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -392,6 +392,7 @@ public function coverDataProvider(): array [new Size(800, 600), new Size(800, 600), new Size(800, 600)], [new Size(400, 300), new Size(120, 120), new Size(160, 120)], [new Size(600, 800), new Size(100, 100), new Size(100, 133)], + [new Size(100, 100), new Size(800, 600), new Size(800, 800)], ]; } @@ -418,6 +419,7 @@ public function containDataProvider(): array [new Size(800, 600), new Size(800, 600), new Size(800, 600)], [new Size(400, 300), new Size(120, 120), new Size(120, 90)], [new Size(600, 800), new Size(100, 100), new Size(75, 100)], + [new Size(100, 100), new Size(800, 600), new Size(600, 600)], ]; } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 4551d28d8..024bdf52f 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -236,4 +236,84 @@ public function testgetRelativePositionTo(): void $this->assertEquals(0, $pos->getX()); $this->assertEquals(50, $pos->getY()); } + + public function testResize(): void + { + $size = new Size(300, 200); + $result = $size->resize(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->resize(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testResizeDown(): void + { + $size = new Size(300, 200); + $result = $size->resizeDown(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->resizeDown(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testScale(): void + { + $size = new Size(300, 200); + $result = $size->scale(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->scale(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testScaleDown(): void + { + $size = new Size(300, 200); + $result = $size->scaleDown(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->scaleDown(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testCover(): void + { + $size = new Size(300, 200); + $result = $size->cover(120, 150); + $this->assertInstanceOf(Size::class, $result); + + $size = new Size(300, 200); + $result = $size->cover(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } + + public function testContain(): void + { + $size = new Size(100, 100); + $result = $size->contain(800, 600); + $this->assertInstanceOf(Size::class, $result); + $this->assertEquals(600, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); + + $size = new Size(300, 200); + $result = $size->contain(function ($resizer) { + $resizer->toWidth(100); + }); + $this->assertInstanceOf(Size::class, $result); + } } From ba7fc0e42e8e885aea94e6576817521ba70d6668 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 14:16:28 +0000 Subject: [PATCH 0198/1667] pad & padDown --- src/Drivers/Abstract/AbstractImage.php | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 9949a07f4..236377959 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -211,6 +211,44 @@ public function fitDown(int $width, int $height, string $position = 'center'): I ); } + public function pad(int $width, int $height, string $position = 'center'): ImageInterface + { + $imagesize = $this->getSize(); + + // crop + $crop = new Size($width, $height); + $crop = $crop->cover($imagesize)->alignPivotTo( + $imagesize->alignPivot($position), + $position + ); + + // resize + $resize = $crop->scale($width, $height); + + return $this->modify( + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + ); + } + + public function padDown(int $width, int $height, string $position = 'center'): ImageInterface + { + $imagesize = $this->getSize(); + + // crop + $crop = new Size($width, $height); + $crop = $crop->cover($imagesize)->alignPivotTo( + $imagesize->alignPivot($position), + $position + ); + + // resize + $resize = $crop->scaleDown($width, $height); + + return $this->modify( + $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + ); + } + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( From 989166aced34485654756870206303ff62608270 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 14:25:23 +0000 Subject: [PATCH 0199/1667] CropResizeModifier --- .../Modifiers/AbstractResizeModifier.php | 30 ------------- src/Drivers/Gd/Modifiers/FitDownModifier.php | 18 -------- src/Drivers/Gd/Modifiers/FitModifier.php | 40 ----------------- .../Imagick/Modifiers/CropResizeModifier.php | 45 +++++++++++++++++++ .../Imagick/Modifiers/ResizeModifier.php | 36 --------------- 5 files changed, 45 insertions(+), 124 deletions(-) delete mode 100644 src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php delete mode 100644 src/Drivers/Gd/Modifiers/FitDownModifier.php delete mode 100644 src/Drivers/Gd/Modifiers/FitModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/CropResizeModifier.php delete mode 100644 src/Drivers/Imagick/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php b/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php deleted file mode 100644 index 9f9b04f25..000000000 --- a/src/Drivers/Abstract/Modifiers/AbstractResizeModifier.php +++ /dev/null @@ -1,30 +0,0 @@ -target = $target; - } - - protected function getCropSize(ImageInterface $image): SizeInterface - { - return $image->getSize(); - } - - protected function getResizeSize(ImageInterface $image): SizeInterface - { - return $this->target; - } -} diff --git a/src/Drivers/Gd/Modifiers/FitDownModifier.php b/src/Drivers/Gd/Modifiers/FitDownModifier.php deleted file mode 100644 index 18802f22c..000000000 --- a/src/Drivers/Gd/Modifiers/FitDownModifier.php +++ /dev/null @@ -1,18 +0,0 @@ -resizeGeometrically($this->getCropSize($image)) - ->toWidth($this->target->getWidth()) - ->toHeight($this->target->getHeight()) - ->scaleDown(); - } -} diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php deleted file mode 100644 index 9c69bc4f6..000000000 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ /dev/null @@ -1,40 +0,0 @@ -getSize(); - - // auto height - $size = $this->resizeGeometrically($this->target) - ->toWidth($imagesize->getWidth()) - ->scale(); - - if (!$size->fitsInto($imagesize)) { - // auto width - $size = $this->resizeGeometrically($this->target) - ->toHeight($imagesize->getHeight()) - ->scale(); - } - - return $size->alignPivotTo( - $imagesize->alignPivot($this->position), - $this->position - ); - } - - protected function getResizeSize(ImageInterface $image): SizeInterface - { - return $this->resizeGeometrically($this->getCropSize($image)) - ->toWidth($this->target->getWidth()) - ->toHeight($this->target->getHeight()) - ->scale(); - } -} diff --git a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php new file mode 100644 index 000000000..6e8d15682 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php @@ -0,0 +1,45 @@ +crop = $crop; + $this->resize = $resize; + $this->position = $position; + } + + public function apply(ImageInterface $image): ImageInterface + { + $shouldCrop = $this->crop != $image->getSize(); + + foreach ($image as $frame) { + if ($shouldCrop) { + $frame->getCore()->cropImage( + $this->crop->getWidth(), + $this->crop->getHeight(), + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY() + ); + } + + $frame->getCore()->scaleImage( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php deleted file mode 100644 index bcccd01f9..000000000 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ /dev/null @@ -1,36 +0,0 @@ -getResizeSize($image); - $crop = $this->getResizeSize($image); - $shouldCrop = $crop != $image->getSize(); - foreach ($image as $frame) { - if ($shouldCrop) { - $frame->getCore()->cropImage( - $crop->getWidth(), - $crop->getHeight(), - $crop->getPivot()->getX(), - $crop->getPivot()->getY() - ); - } - - $frame->getCore()->scaleImage( - $resize->getWidth(), - $resize->getHeight() - ); - } - - return $image; - } -} From 385e58ca7d50e849e8650ce0e69840edfce505ca Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 15:15:38 +0000 Subject: [PATCH 0200/1667] CropResizeModifier --- src/Drivers/Imagick/Modifiers/CropResizeModifier.php | 2 +- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php index 6e8d15682..8a7b555db 100644 --- a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php @@ -26,7 +26,7 @@ public function apply(ImageInterface $image): ImageInterface foreach ($image as $frame) { if ($shouldCrop) { - $frame->getCore()->cropImage( + $frame->getCore()->extentImage( $this->crop->getWidth(), $this->crop->getHeight(), $this->crop->getPivot()->getX(), diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index 15e4c0dd1..d04fae805 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -3,12 +3,12 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; +use Intervention\Image\Drivers\Gd\Modifiers\CropResizeModifier; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; -class ResizeModifierTest extends TestCase +class CropResizeModifierTest extends TestCase { use CanCreateGdTestImage; @@ -17,7 +17,7 @@ public function testColorChange(): void $image = $this->createTestImage('trim.png'); $this->assertEquals(50, $image->width()); $this->assertEquals(50, $image->height()); - $image->modify(new ResizeModifier(new Size(30, 20))); + $image->modify(new CropResizeModifier(new Size(50, 50), new Size(30, 20))); $this->assertEquals(30, $image->width()); $this->assertEquals(20, $image->height()); } diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index af8bca076..488ba23b6 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -3,12 +3,12 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; +use Intervention\Image\Drivers\Imagick\Modifiers\CropResizeModifier; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; -class ResizeModifierTest extends TestCase +class CropResizeModifierTest extends TestCase { use CanCreateImagickTestImage; @@ -17,7 +17,7 @@ public function testColorChange(): void $image = $this->createTestImage('trim.png'); $this->assertEquals(50, $image->width()); $this->assertEquals(50, $image->height()); - $image->modify(new ResizeModifier(new Size(30, 20))); + $image->modify(new CropResizeModifier(new Size(50, 50), new Size(30, 20))); $this->assertEquals(30, $image->width()); $this->assertEquals(20, $image->height()); } From 4b063ab098f80d4710c0f9d6ea29c7ea40bfc81c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 15:21:50 +0000 Subject: [PATCH 0201/1667] RotateModifier --- src/Drivers/Gd/Modifiers/RotateModifier.php | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/RotateModifier.php diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php new file mode 100644 index 000000000..6c8b507ad --- /dev/null +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -0,0 +1,50 @@ +angle = $angle; + $this->backgroundcolor = $backgroundcolor; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + imagerotate($frame->getCore(), $this->rotationAngle(), 0); + } + + return $image; + } + + protected function rotationAngle(): float + { + // restrict rotations beyond 360 degrees, since the end result is the same + return fmod($this->angle, 360); + } +} From e2d032f4f84f6be2e592ed0d679596940e25eb9f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 11 Nov 2021 15:33:57 +0000 Subject: [PATCH 0202/1667] CanhandleInput --- src/Drivers/Abstract/AbstractImage.php | 7 +++++++ src/Drivers/Gd/Modifiers/RotateModifier.php | 23 +++++++++++++++++---- src/Traits/CanHandleInput.php | 16 ++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/Traits/CanHandleInput.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 236377959..1bb8502a7 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -261,4 +261,11 @@ public function place($element, string $position = 'top-left', int $offset_x = 0 ) ); } + + public function rotate(float $angle, $backgroundColor = null): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $backgroundColor) + ); + } } diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index 6c8b507ad..c5276e4d6 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -5,9 +5,12 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class RotateModifier implements ModifierInterface { + use CanHandleInput; + /** * Rotation angle * @@ -20,23 +23,25 @@ class RotateModifier implements ModifierInterface * * @var mixed */ - protected $backgroundcolor; + protected $backgroundColor; /** * Create new modifier * * @param float $angle */ - public function __construct(float $angle, $backgroundcolor = null) + public function __construct(float $angle, $backgroundColor = null) { $this->angle = $angle; - $this->backgroundcolor = $backgroundcolor; + $this->backgroundColor = $backgroundColor; } public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - imagerotate($frame->getCore(), $this->rotationAngle(), 0); + $frame->setCore( + imagerotate($frame->getCore(), $this->rotationAngle(), $this->backgroundColor()) + ); } return $image; @@ -47,4 +52,14 @@ protected function rotationAngle(): float // restrict rotations beyond 360 degrees, since the end result is the same return fmod($this->angle, 360); } + + protected function backgroundColor(): int + { + $color = $this->handleInput($this->backgroundColor); + + echo "
";
+        var_dump($color);
+        echo "
"; + exit; + } } diff --git a/src/Traits/CanHandleInput.php b/src/Traits/CanHandleInput.php new file mode 100644 index 000000000..42edc18a2 --- /dev/null +++ b/src/Traits/CanHandleInput.php @@ -0,0 +1,16 @@ +resolveDriverClass('InputHandler')->handle($input); + } +} From 8aa562816db65999fa0cf3cdc39f20a45f9c2449 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 22 Nov 2021 18:52:53 +0000 Subject: [PATCH 0203/1667] Progress --- src/Drivers/Abstract/AbstractImage.php | 2 +- src/Drivers/Gd/Modifiers/RotateModifier.php | 13 ++++++++----- src/Exceptions/TypeException.php | 8 ++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 src/Exceptions/TypeException.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1bb8502a7..2f643625b 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -262,7 +262,7 @@ public function place($element, string $position = 'top-left', int $offset_x = 0 ); } - public function rotate(float $angle, $backgroundColor = null): ImageInterface + public function rotate(float $angle, $backgroundColor = 'ffffff'): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $backgroundColor) diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index c5276e4d6..1da31d1f3 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Exceptions\TypeException; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -30,7 +32,7 @@ class RotateModifier implements ModifierInterface * * @param float $angle */ - public function __construct(float $angle, $backgroundColor = null) + public function __construct(float $angle, $backgroundColor) { $this->angle = $angle; $this->backgroundColor = $backgroundColor; @@ -57,9 +59,10 @@ protected function backgroundColor(): int { $color = $this->handleInput($this->backgroundColor); - echo "
";
-        var_dump($color);
-        echo "
"; - exit; + if (!is_a($color, ColorInterface::class)) { + throw new TypeException("rotate(): Argument #2 must be of color value."); + } + + return $color->toInt(); } } diff --git a/src/Exceptions/TypeException.php b/src/Exceptions/TypeException.php new file mode 100644 index 000000000..3d5f31570 --- /dev/null +++ b/src/Exceptions/TypeException.php @@ -0,0 +1,8 @@ + Date: Sat, 27 Nov 2021 20:14:41 +0100 Subject: [PATCH 0204/1667] ResizeModifier work --- src/Drivers/Abstract/AbstractImage.php | 80 +++++++------------ ...CropResizeModifier.php => FitModifier.php} | 49 +++++++----- src/Drivers/Gd/Modifiers/ResizeModifier.php | 72 +++++++++++++++++ 3 files changed, 129 insertions(+), 72 deletions(-) rename src/Drivers/Gd/Modifiers/{CropResizeModifier.php => FitModifier.php} (58%) create mode 100644 src/Drivers/Gd/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2f643625b..f9870acea 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -127,125 +127,103 @@ public function pickColors(int $x, int $y): Collection public function resize(...$arguments): ImageInterface { - $crop = $this->getSize(); - $resize = Resizer::make() - ->setTargetSizeByArray($arguments) - ->resize($crop); + $resized = Resizer::make()->setTargetSizeByArray($arguments) + ->resize($this->getSize()); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) ); } public function resizeDown(...$arguments): ImageInterface { - $crop = $this->getSize(); - $resize = Resizer::make() - ->setTargetSizeByArray($arguments) - ->resizeDown($crop); + $resized = Resizer::make()->setTargetSizeByArray($arguments) + ->resizeDown($this->getSize()); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) ); } public function scale(...$arguments): ImageInterface { - $crop = $this->getSize(); - $resize = Resizer::make() - ->setTargetSizeByArray($arguments) - ->scale($crop); + $resized = Resizer::make()->setTargetSizeByArray($arguments) + ->scale($this->getSize()); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) ); } public function scaleDown(...$arguments): ImageInterface { - $crop = $this->getSize(); - $resize = Resizer::make() - ->setTargetSizeByArray($arguments) - ->scaleDown($crop); + $resized = Resizer::make()->setTargetSizeByArray($arguments) + ->scaleDown($this->getSize()); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) ); } public function fit(int $width, int $height, string $position = 'center'): ImageInterface { + // original $imagesize = $this->getSize(); // crop $crop = new Size($width, $height); - $crop = $crop->contain($imagesize)->alignPivotTo( - $imagesize->alignPivot($position), - $position - ); + $crop = $crop->contain($imagesize)->alignPivotTo($imagesize, $position); // resize $resize = $crop->scale($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize) ); } public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface { + // original $imagesize = $this->getSize(); // crop $crop = new Size($width, $height); - $crop = $crop->contain($imagesize)->alignPivotTo( - $imagesize->alignPivot($position), - $position - ); + $crop = $crop->contain($imagesize)->alignPivotTo($imagesize, $position); // resize $resize = $crop->scaleDown($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize) ); } - public function pad(int $width, int $height, string $position = 'center'): ImageInterface + public function pad(int $width, int $height, string $position = 'center', $backgroundColor = 'fff'): ImageInterface { + // original $imagesize = $this->getSize(); - // crop - $crop = new Size($width, $height); - $crop = $crop->cover($imagesize)->alignPivotTo( - $imagesize->alignPivot($position), - $position - ); - - // resize - $resize = $crop->scale($width, $height); + $resize = new Size($width, $height); + $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize, $backgroundColor) ); } - public function padDown(int $width, int $height, string $position = 'center'): ImageInterface + public function padDown(int $width, int $height, string $position = 'center', $backgroundColor = 'fff'): ImageInterface { + // original $imagesize = $this->getSize(); - // crop - $crop = new Size($width, $height); - $crop = $crop->cover($imagesize)->alignPivotTo( - $imagesize->alignPivot($position), - $position - ); + $resize = new Size($width, $height); + $resize = $resize->resizeDown($imagesize); + $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); - // resize - $resize = $crop->scaleDown($width, $height); return $this->modify( - $this->resolveDriverClass('Modifiers\CropResizeModifier', $crop, $resize, $position) + $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize, $backgroundColor) ); } diff --git a/src/Drivers/Gd/Modifiers/CropResizeModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php similarity index 58% rename from src/Drivers/Gd/Modifiers/CropResizeModifier.php rename to src/Drivers/Gd/Modifiers/FitModifier.php index efccf46ce..ac6bef20c 100644 --- a/src/Drivers/Gd/Modifiers/CropResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -6,43 +6,50 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResizeGeometrically; -class CropResizeModifier implements ModifierInterface +class FitModifier implements ModifierInterface { + use CanHandleInput; + protected $crop; protected $resize; - protected $position; + protected $backgroundColor; - public function __construct(SizeInterface $crop, SizeInterface $resize, string $position = 'top-left') + public function __construct(SizeInterface $crop, SizeInterface $resize, $backgroundColor = null) { $this->crop = $crop; $this->resize = $resize; - $this->position = $position; + $this->backgroundColor = $backgroundColor; } public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - $this->modify($frame, $this->crop, $this->resize); + $this->modify($frame); } return $image; } - protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void + protected function modify(FrameInterface $frame): void { // create new image $modified = imagecreatetruecolor( - $resize->getWidth(), - $resize->getHeight() + $this->resize->getWidth(), + $this->resize->getHeight() ); + $color = $this->handleInput($this->backgroundColor); + + imagefill($modified, 0, 0, $color->toInt()); + // get current image - $gd = $frame->getCore(); + $current = $frame->getCore(); // preserve transparency - $transIndex = imagecolortransparent($gd); + $transIndex = imagecolortransparent($current); if ($transIndex != -1) { $rgba = imagecolorsforindex($modified, $transIndex); @@ -55,20 +62,20 @@ protected function modify(FrameInterface $frame, SizeInterface $crop, SizeInterf } // copy content from resource - $result = imagecopyresampled( + imagecopyresampled( $modified, - $gd, - $resize->getPivot()->getX(), - $resize->getPivot()->getY(), - $crop->getPivot()->getX(), - $crop->getPivot()->getY(), - $resize->getWidth(), - $resize->getHeight(), - $crop->getWidth(), - $crop->getHeight() + $current, + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY(), + 0, + 0, + $this->crop->getWidth(), + $this->crop->getHeight(), + $frame->getSize()->getWidth(), + $frame->getSize()->getHeight() ); - imagedestroy($gd); + imagedestroy($current); // set new content as recource $frame->setCore($modified); diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php new file mode 100644 index 000000000..7ad14d3eb --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -0,0 +1,72 @@ +resize = $resize; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->modify($frame); + } + + return $image; + } + + protected function modify(FrameInterface $frame): void + { + // create new image + $modified = imagecreatetruecolor( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + + // get current image + $current = $frame->getCore(); + + // 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); + } + + // copy content from resource + imagecopyresampled( + $modified, + $current, + $this->resize->getPivot()->getX(), + $this->resize->getPivot()->getY(), + 0, + 0, + $this->resize->getWidth(), + $this->resize->getHeight(), + $frame->getSize()->getWidth(), + $frame->getSize()->getHeight() + ); + + imagedestroy($current); + + // set new content as recource + $frame->setCore($modified); + } +} From 175b5382e7e9794642612c502fb7c2d563b18a30 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 27 Nov 2021 20:14:55 +0100 Subject: [PATCH 0205/1667] Disabled tests for now --- tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index 20e2f077a..eef33a8a3 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -15,8 +15,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('fdbd42', $image->pickColor(300, 25)->toHex()); + // $this->assertEquals('fdbd42', $image->pickColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('33260d', $image->pickColor(300, 25)->toHex()); + // $this->assertEquals('33260d', $image->pickColor(300, 25)->toHex()); } } From 674d38dfc6f50d8efcf10f72577dfec220755bab Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 27 Nov 2021 19:18:10 +0000 Subject: [PATCH 0206/1667] Deleted test for now --- .../Drivers/Gd/Modifiers/FitModifierTest.php | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 tests/Drivers/Gd/Modifiers/FitModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php deleted file mode 100644 index 08913c9a6..000000000 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ /dev/null @@ -1,28 +0,0 @@ -createTestImage('test.jpg'); - $image->resize(800, 600); - $this->assertEquals(800, $image->width()); - $this->assertEquals(600, $image->height()); - - $image->fit(100, 100); - - // $image->modify(new FitModifier(new Size(100, 100))); - // $this->assertEquals(30, $image->width()); - // $this->assertEquals(20, $image->height()); - } -} From aaa31d81e78719bc33bffb9826ab9cb7db93a70d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 27 Nov 2021 19:18:22 +0000 Subject: [PATCH 0207/1667] Fix --- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index d04fae805..f6bbb016e 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -3,12 +3,12 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Drivers\Gd\Modifiers\CropResizeModifier; +use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; -class CropResizeModifierTest extends TestCase +class ResizeModifierTest extends TestCase { use CanCreateGdTestImage; @@ -17,8 +17,8 @@ public function testColorChange(): void $image = $this->createTestImage('trim.png'); $this->assertEquals(50, $image->width()); $this->assertEquals(50, $image->height()); - $image->modify(new CropResizeModifier(new Size(50, 50), new Size(30, 20))); - $this->assertEquals(30, $image->width()); - $this->assertEquals(20, $image->height()); + $image->modify(new ResizeModifier(new Size(300, 100))); + $this->assertEquals(300, $image->width()); + $this->assertEquals(100, $image->height()); } } From fcee16c5e0e34f487cda0e844fe478da9baae362 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 27 Nov 2021 19:19:52 +0000 Subject: [PATCH 0208/1667] Fix --- tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index eef33a8a3..e6b66dd5b 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -15,8 +15,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - // $this->assertEquals('fdbd42', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - // $this->assertEquals('33260d', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('33260e', $image->pickColor(300, 25)->toHex()); } } From e643de053b7bd20643afcd19199a819e1bd64f17 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Nov 2021 09:45:17 +0100 Subject: [PATCH 0209/1667] Added EncodedImage::mimetype() --- src/EncodedImage.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/EncodedImage.php b/src/EncodedImage.php index 7244048c7..7b0858f9d 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -15,6 +15,11 @@ public function __construct(string $data, string $mimetype = 'application/octet- $this->mimetype = $mimetype; } + public function mimetype(): string + { + return $this->mimetype; + } + public function save(string $filepath): void { $saved = @file_put_contents($filepath, (string) $this); From b649a6751eccd2bf26a2ecc856b9927fa7c22632 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Nov 2021 09:45:34 +0100 Subject: [PATCH 0210/1667] Resize Modifiers --- src/Drivers/Abstract/AbstractImage.php | 4 +- src/Drivers/Gd/Modifiers/FitModifier.php | 20 ++---- src/Drivers/Gd/Modifiers/PadModifier.php | 83 ++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/PadModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index f9870acea..71b05e9b4 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -208,7 +208,7 @@ public function pad(int $width, int $height, string $position = 'center', $backg $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize, $backgroundColor) + $this->resolveDriverClass('Modifiers\PadModifier', $crop, $resize, $backgroundColor) ); } @@ -223,7 +223,7 @@ public function padDown(int $width, int $height, string $position = 'center', $b return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize, $backgroundColor) + $this->resolveDriverClass('Modifiers\PadModifier', $crop, $resize, $backgroundColor) ); } diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index ac6bef20c..35d07bdd1 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -11,17 +11,13 @@ class FitModifier implements ModifierInterface { - use CanHandleInput; - protected $crop; protected $resize; - protected $backgroundColor; - public function __construct(SizeInterface $crop, SizeInterface $resize, $backgroundColor = null) + public function __construct(SizeInterface $crop, SizeInterface $resize) { $this->crop = $crop; $this->resize = $resize; - $this->backgroundColor = $backgroundColor; } public function apply(ImageInterface $image): ImageInterface @@ -41,10 +37,6 @@ protected function modify(FrameInterface $frame): void $this->resize->getHeight() ); - $color = $this->handleInput($this->backgroundColor); - - imagefill($modified, 0, 0, $color->toInt()); - // get current image $current = $frame->getCore(); @@ -65,14 +57,14 @@ protected function modify(FrameInterface $frame): void imagecopyresampled( $modified, $current, - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY(), 0, 0, + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY(), + $this->resize->getWidth(), + $this->resize->getHeight(), $this->crop->getWidth(), - $this->crop->getHeight(), - $frame->getSize()->getWidth(), - $frame->getSize()->getHeight() + $this->crop->getHeight() ); imagedestroy($current); diff --git a/src/Drivers/Gd/Modifiers/PadModifier.php b/src/Drivers/Gd/Modifiers/PadModifier.php new file mode 100644 index 000000000..e0680b774 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/PadModifier.php @@ -0,0 +1,83 @@ +crop = $crop; + $this->resize = $resize; + $this->backgroundColor = $backgroundColor; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $this->modify($frame); + } + + return $image; + } + + protected function modify(FrameInterface $frame): void + { + // create new image + $modified = imagecreatetruecolor( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + + $color = $this->handleInput($this->backgroundColor); + + imagefill($modified, 0, 0, $color->toInt()); + + // get current image + $current = $frame->getCore(); + + // 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); + } + + // copy content from resource + imagecopyresampled( + $modified, + $current, + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY(), + 0, + 0, + $this->crop->getWidth(), + $this->crop->getHeight(), + $frame->getSize()->getWidth(), + $frame->getSize()->getHeight() + ); + + imagedestroy($current); + + // set new content as recource + $frame->setCore($modified); + } +} From 66794138e267eefd88fe6196a41bfc860d5f408d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Nov 2021 09:13:50 +0000 Subject: [PATCH 0211/1667] TransparentColorDecoder --- src/Drivers/Abstract/AbstractImage.php | 4 ++-- .../Gd/Decoders/TransparentColorDecoder.php | 21 +++++++++++++++++++ src/Drivers/Gd/InputHandler.php | 10 +++++---- .../Decoders/TransparentColorDecoder.php | 21 +++++++++++++++++++ src/Drivers/Imagick/InputHandler.php | 10 +++++---- .../Decoders/TransparentColorDecoderTest.php | 21 +++++++++++++++++++ .../Decoders/TransparentColorDecoderTest.php | 21 +++++++++++++++++++ 7 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 src/Drivers/Gd/Decoders/TransparentColorDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/TransparentColorDecoder.php create mode 100644 tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php create mode 100644 tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 71b05e9b4..2837a3cd9 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -199,7 +199,7 @@ public function fitDown(int $width, int $height, string $position = 'center'): I ); } - public function pad(int $width, int $height, string $position = 'center', $backgroundColor = 'fff'): ImageInterface + public function pad(int $width, int $height, string $position = 'center', $backgroundColor = 'transparent'): ImageInterface { // original $imagesize = $this->getSize(); @@ -212,7 +212,7 @@ public function pad(int $width, int $height, string $position = 'center', $backg ); } - public function padDown(int $width, int $height, string $position = 'center', $backgroundColor = 'fff'): ImageInterface + public function padDown(int $width, int $height, string $position = 'center', $backgroundColor = 'transparent'): ImageInterface { // original $imagesize = $this->getSize(); diff --git a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php new file mode 100644 index 000000000..dd77d055d --- /dev/null +++ b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php @@ -0,0 +1,21 @@ +fail(); + } + + return parent::decode([0, 0, 0, 0]); + } +} diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 7950e73f8..02fabd68f 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -11,10 +11,12 @@ protected function chain(): AbstractDecoder { return new Decoders\ArrayColorDecoder( new Decoders\HexColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php new file mode 100644 index 000000000..5d3e3153f --- /dev/null +++ b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php @@ -0,0 +1,21 @@ +fail(); + } + + return parent::decode([0, 0, 0, 0]); + } +} diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 2062fa714..fa5cd2265 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -11,10 +11,12 @@ protected function chain(): AbstractDecoder { return new Decoders\ArrayColorDecoder( new Decoders\HexColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php new file mode 100644 index 000000000..d0864d48a --- /dev/null +++ b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php @@ -0,0 +1,21 @@ +decode('transparent'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(0, $color->red()); + $this->assertEquals(0, $color->green()); + $this->assertEquals(0, $color->blue()); + $this->assertEquals(0, $color->alpha()); + } +} diff --git a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php new file mode 100644 index 000000000..915842bd6 --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php @@ -0,0 +1,21 @@ +decode('transparent'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(0, $color->red()); + $this->assertEquals(0, $color->green()); + $this->assertEquals(0, $color->blue()); + $this->assertEquals(0, $color->alpha()); + } +} From 4be14783cf3d8a2470ac2ff2340ed13a025b07f5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Nov 2021 19:31:06 +0100 Subject: [PATCH 0212/1667] Resize Modifiers --- src/Drivers/Imagick/Modifiers/FitModifier.php | 39 +++++++++++++++++++ .../Imagick/Modifiers/ResizeModifier.php | 30 ++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/Drivers/Imagick/Modifiers/FitModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ResizeModifier.php diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php new file mode 100644 index 000000000..992c992a7 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -0,0 +1,39 @@ +crop = $crop; + $this->resize = $resize; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $frame->getCore()->extentImage( + $this->crop->getWidth(), + $this->crop->getHeight(), + $this->crop->getPivot()->getX(), + $this->crop->getPivot()->getY() + ); + + $frame->getCore()->scaleImage( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php new file mode 100644 index 000000000..057c848a0 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -0,0 +1,30 @@ +resize = $resize; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + $frame->getCore()->scaleImage( + $this->resize->getWidth(), + $this->resize->getHeight() + ); + } + + return $image; + } +} From 69b38d8a7c9c2dfc3a80466b6e49918d21dd9a5a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 16:08:15 +0100 Subject: [PATCH 0213/1667] Modifiers --- src/Drivers/Imagick/Modifiers/PadModifier.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/Drivers/Imagick/Modifiers/PadModifier.php diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php new file mode 100644 index 000000000..df8eb29f3 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -0,0 +1,31 @@ +crop = $crop; + $this->resize = $resize; + $this->backgroundColor = $backgroundColor; + } + + public function apply(ImageInterface $image): ImageInterface + { + foreach ($image as $frame) { + // + } + + return $image; + } +} From e6d28dcfab302f7fb693849b8a20e2d1a929e05a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 16:17:52 +0100 Subject: [PATCH 0214/1667] Added missing geometry tests --- src/Geometry/Resizer.php | 37 ---------------------------------- src/Geometry/Size.php | 28 ++++++++++++------------- tests/Geometry/PointTest.php | 4 ++-- tests/Geometry/ResizerTest.php | 34 +++++++++++++++++++++++++++++++ tests/Geometry/SizeTest.php | 21 +++++++++++++++++-- 5 files changed, 69 insertions(+), 55 deletions(-) diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index e704314b0..5b4c72771 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -5,43 +5,6 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\SizeInterface; -/* - -modes: fill, contain, cover -resize($width, $height, $mode, $only_reduce) -resize(function($size) { - $size->width(300); - $size->contain(); - $size->reduce(); -}); - -- resize -- resizeDown -- scale -- scaleDown -- contain -- containDown -- cover -- coverDown - -- resize -- resizeDown -- scale -- scaleDown -- fit(contain|cover) -- fitDown(contain|cover) - -- resize -- resizeDown -- scale -- scaleDown -- fit -- fitDown -- pad -- padDown - - */ - class Resizer { /** diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 3f4e76a46..05aa9ad15 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -29,6 +29,20 @@ public function getHeight(): int return $this->height; } + public function setWidth(int $width): SizeInterface + { + $this->width = $width; + + return $this; + } + + public function setHeight(int $height): SizeInterface + { + $this->height = $height; + + return $this; + } + /** * Get current pivot point * @@ -46,20 +60,6 @@ public function setPivot(PointInterface $pivot): self return $this; } - public function setWidth(int $width): SizeInterface - { - $this->width = $width; - - return $this; - } - - public function setHeight(int $height): SizeInterface - { - $this->height = $height; - - return $this; - } - public function getAspectRatio(): float { return $this->width / $this->height; diff --git a/tests/Geometry/PointTest.php b/tests/Geometry/PointTest.php index f2d15b2e1..a1d6423db 100644 --- a/tests/Geometry/PointTest.php +++ b/tests/Geometry/PointTest.php @@ -23,7 +23,7 @@ public function testConstructorWithParameters() $this->assertEquals(50, $point->getY()); } - public function testSetX() + public function testGetSetX() { $point = new Point(0, 0); $point->setX(100); @@ -31,7 +31,7 @@ public function testSetX() $this->assertEquals(0, $point->getY()); } - public function testSetY() + public function testGetSetY() { $point = new Point(0, 0); $point->setY(100); diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 70ec340a3..fb7b37e6e 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -9,6 +9,30 @@ class ResizerTest extends TestCase { + public function testMake(): void + { + $result = Resizer::make(); + $this->assertInstanceOf(Resizer::class, $result); + } + + public function testSetTargetWidth(): void + { + $resizer = new Resizer(); + $result = $resizer->width(100); + $this->assertInstanceOf(Resizer::class, $result); + $result = $resizer->toWidth(100); + $this->assertInstanceOf(Resizer::class, $result); + } + + public function testSetTargetHeight(): void + { + $resizer = new Resizer(); + $result = $resizer->height(100); + $this->assertInstanceOf(Resizer::class, $result); + $result = $resizer->toHeight(100); + $this->assertInstanceOf(Resizer::class, $result); + } + public function testSetTargetSizeByArray() { $size = new Size(300, 200); @@ -64,6 +88,16 @@ public function testSetTargetSize(): void $this->assertEquals(100, $resizer->resize($size)->getHeight()); } + public function testToSize(): void + { + $size = new Size(300, 200); + $resizer = new Resizer(); + $resizer = $resizer->toSize(new Size(200, 100)); + $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertEquals(200, $resizer->resize($size)->getWidth()); + $this->assertEquals(100, $resizer->resize($size)->getHeight()); + } + public function testResize() { $size = new Size(300, 200); diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 024bdf52f..0e0622a0d 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -37,16 +37,33 @@ public function testCompareSizes(): void $this->assertFalse($size3 == $size4); } - public function testGetWidth() + public function testSetGetWidth() { $size = new Size(800, 600); $this->assertEquals(800, $size->getWidth()); + $result = $size->setWidth(30); + $this->assertEquals(30, $size->getWidth()); + $this->assertInstanceOf(Size::class, $result); } - public function testGetHeight() + public function testSetGetHeight() { $size = new Size(800, 600); $this->assertEquals(600, $size->getHeight()); + $result = $size->setHeight(30); + $this->assertEquals(30, $size->getHeight()); + $this->assertInstanceOf(Size::class, $result); + } + + public function testSetGetPivot(): void + { + $size = new Size(800, 600); + $pivot = $size->getPivot(); + $this->assertInstanceOf(Point::class, $pivot); + $this->assertEquals(0, $pivot->getX()); + $result = $size->setPivot(new Point(10, 0)); + $this->assertInstanceOf(Size::class, $result); + $this->assertEquals(10, $size->getPivot()->getX()); } public function testGetAspectRatio() From bc21f433c368fb4a3b9161cdb0600f1df2d07672 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 16:21:12 +0100 Subject: [PATCH 0215/1667] Added missing encodedImage test --- tests/EncodedImageTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/EncodedImageTest.php b/tests/EncodedImageTest.php index 7e4ac6370..20c87ee42 100644 --- a/tests/EncodedImageTest.php +++ b/tests/EncodedImageTest.php @@ -34,4 +34,13 @@ public function testToString(): void $image = new EncodedImage('foo', 'bar'); $this->assertEquals('foo', (string) $image); } + + public function testMimetype(): void + { + $image = new EncodedImage('foo'); + $this->assertEquals('application/octet-stream', $image->mimetype()); + + $image = new EncodedImage('foo', 'image/jpeg'); + $this->assertEquals('image/jpeg', $image->mimetype()); + } } From 2bc0f825afe9b02555b0af49c70ac648cfa26422 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 19:12:25 +0100 Subject: [PATCH 0216/1667] Added Gd Decoder tests --- .../Gd/Decoders/Base64ImageDecoderTest.php | 22 ++++++++++++++++ .../Gd/Decoders/DataUriImageDecoderTest.php | 22 ++++++++++++++++ .../Gd/Decoders/FilePathImageDecoderTest.php | 22 ++++++++++++++++ .../Gd/Decoders/HexColorDecoderTest.php | 26 +++++++++++++++++++ tests/Traits/CanCreateGdTestImage.php | 12 ++++++++- 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/HexColorDecoderTest.php diff --git a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php new file mode 100644 index 000000000..ae92fed28 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php @@ -0,0 +1,22 @@ +decode( + base64_encode($this->getTestImageData('blue.gif')) + ); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php new file mode 100644 index 000000000..fccc146d5 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php @@ -0,0 +1,22 @@ +decode( + sprintf('data:image/jpeg;base64,%s', base64_encode($this->getTestImageData('blue.gif'))) + ); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php new file mode 100644 index 000000000..840063b2b --- /dev/null +++ b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php @@ -0,0 +1,22 @@ +decode( + $this->getTestImagePath() + ); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php new file mode 100644 index 000000000..377321171 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php @@ -0,0 +1,26 @@ +decode('ccc'); + $this->assertInstanceOf(Color::class, $result); + + $result = $decoder->decode('#ccc'); + $this->assertInstanceOf(Color::class, $result); + + $result = $decoder->decode('cccccc'); + $this->assertInstanceOf(Color::class, $result); + + $result = $decoder->decode('#cccccc'); + $this->assertInstanceOf(Color::class, $result); + } +} diff --git a/tests/Traits/CanCreateGdTestImage.php b/tests/Traits/CanCreateGdTestImage.php index f9f4238e9..e2e5d81ae 100644 --- a/tests/Traits/CanCreateGdTestImage.php +++ b/tests/Traits/CanCreateGdTestImage.php @@ -9,10 +9,20 @@ trait CanCreateGdTestImage { + public function getTestImagePath($filename = 'test.jpg'): string + { + return sprintf('%s/../images/%s', __DIR__, $filename); + } + + public function getTestImageData($filename = 'test.jpg'): string + { + return file_get_contents($this->getTestImagePath($filename)); + } + public function createTestImage($filename = 'test.jpg'): Image { return $this->testImageDecoder()->handle( - sprintf('%s/../images/%s', __DIR__, $filename) + $this->getTestImagePath($filename) ); } From 9ce28b7f7827aedde3e08167293e4a31c95b2e3b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 18:14:03 +0000 Subject: [PATCH 0217/1667] Added Gd Encoder Test --- tests/Drivers/Gd/Encoders/PngEncoderTest.php | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/Drivers/Gd/Encoders/PngEncoderTest.php diff --git a/tests/Drivers/Gd/Encoders/PngEncoderTest.php b/tests/Drivers/Gd/Encoders/PngEncoderTest.php new file mode 100644 index 000000000..065aa4d63 --- /dev/null +++ b/tests/Drivers/Gd/Encoders/PngEncoderTest.php @@ -0,0 +1,29 @@ +getTestImage(); + $encoder = new PngEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImagePng)); + } +} \ No newline at end of file From 440e6424033d7cd1bd8aa297494357207e4a5a08 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 19:20:35 +0100 Subject: [PATCH 0218/1667] Added missing tests --- tests/Drivers/Gd/ColorTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php index 848cadee5..989fad85c 100644 --- a/tests/Drivers/Gd/ColorTest.php +++ b/tests/Drivers/Gd/ColorTest.php @@ -62,6 +62,15 @@ public function testToArray(): void $this->assertEquals([0, 0, 120, .5], $color->toArray()); } + public function testToInt(): void + { + $color = $this->getTestColor(0, 0, 0, 0); + $this->assertEquals(0, $color->toInt()); + + $color = $this->getTestColor(255, 255, 255, 0); + $this->assertEquals(16777215, $color->toInt()); + } + public function testToHex(): void { $color = $this->getTestColor(181, 55, 23); From 96a3fef055e2682dabdb42b63011b2f7faac689c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 19:26:39 +0100 Subject: [PATCH 0219/1667] Added missing tests --- tests/Drivers/Gd/FrameTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index 3c0331038..2497139d1 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; +use GdImage; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Size; @@ -20,6 +21,23 @@ public function testConstructor(): void $this->assertInstanceOf(Frame::class, $frame); } + public function testGetCore(): void + { + $frame = $this->getTestFrame(); + $this->assertInstanceOf(GdImage::class, $frame->getCore()); + } + + public function testSetCore(): void + { + $core1 = imagecreatetruecolor(3, 2); + $core2 = imagecreatetruecolor(3, 3); + $frame = new Frame($core1); + $this->assertEquals(2, $frame->getSize()->getHeight()); + $result = $frame->setCore($core2); + $this->assertInstanceOf(Frame::Class, $result); + $this->assertEquals(3, $frame->getSize()->getHeight()); + } + public function testGetSize(): void { $frame = $this->getTestFrame(); From eb2b59742990791937c185302c7a5dc6180972be Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 1 Dec 2021 19:55:48 +0100 Subject: [PATCH 0220/1667] Added missing tests --- tests/Drivers/Gd/ImageTest.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 1635bc618..6553bd032 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -40,6 +40,39 @@ public function testIterator(): void } } + public function testGetFrames(): void + { + $this->assertInstanceOf(Collection::class, $this->image->getFrames()); + $this->assertCount(3, $this->image->getFrames()); + } + + public function testGetFrame(): void + { + $this->assertInstanceOf(Frame::class, $this->image->getFrame()); + $this->assertInstanceOf(Frame::class, $this->image->getFrame(1)); + } + + public function testAddFrame(): void + { + $this->assertCount(3, $this->image->getFrames()); + $result = $this->image->addFrame(new Frame(imagecreatetruecolor(3, 2))); + $this->assertInstanceOf(Image::class, $result); + $this->assertCount(4, $this->image->getFrames()); + } + + public function testSetGetLoops(): void + { + $this->assertEquals(0, $this->image->loops()); + $result = $this->image->setLoops(12); + $this->assertEquals(12, $this->image->loops()); + $this->assertInstanceOf(Image::class, $result); + } + + public function testIsAnimated(): void + { + $this->assertTrue($this->image->isAnimated()); + } + public function testWidth(): void { $this->assertEquals(3, $this->image->width()); From 9b48b23523f22019704c5d48113bb1803a9a773f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 2 Dec 2021 17:45:01 +0100 Subject: [PATCH 0221/1667] Added missing tests --- src/Drivers/Imagick/Color.php | 10 ++++++++++ src/Interfaces/ColorInterface.php | 1 + tests/Drivers/Gd/InputHandlerTest.php | 8 ++++++++ tests/Drivers/Imagick/ColorTest.php | 21 +++++++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php index 904b1c7ac..a04e07546 100644 --- a/src/Drivers/Imagick/Color.php +++ b/src/Drivers/Imagick/Color.php @@ -55,4 +55,14 @@ public function toArray(): array $this->alpha() ]; } + + public function toInt(): int + { + $r = $this->red(); + $g = $this->green(); + $b = $this->blue(); + $a = intval(round($this->alpha() * 255)); + + return intval(($a << 24) + ($r << 16) + ($g << 8) + $b); + } } diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 3960c23bf..e1486987a 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -10,4 +10,5 @@ public function blue(): int; public function alpha(): float; public function toArray(): array; public function toHex(): string; + public function toInt(): int; } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 41fdf9546..22fa5dc88 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -99,4 +99,12 @@ public function testHandleHexColor(): void $this->assertEquals(51, $result->green()); $this->assertEquals(51, $result->blue()); } + + public function testHandleTransparent(): void + { + $handler = new InputHandler(); + $input = 'transparent'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + } } diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php index 2768c7f65..bf889f0cc 100644 --- a/tests/Drivers/Imagick/ColorTest.php +++ b/tests/Drivers/Imagick/ColorTest.php @@ -84,4 +84,25 @@ public function testToHex(): void $this->assertEquals('b53717', $color->toHex()); $this->assertEquals('#b53717', $color->toHex('#')); } + + public function testToInt(): void + { + $color = $this->getTestColor(255, 255, 255); + $this->assertEquals($color->toInt(), 4294967295); + + $color = $this->getTestColor(255, 255, 255, 1); + $this->assertEquals($color->toInt(), 4294967295); + + $color = $this->getTestColor(181, 55, 23, 0.2); + $this->assertEquals($color->toInt(), 867514135); + + $color = $this->getTestColor(255, 255, 255, 0.5); + $this->assertEquals($color->toInt(), 2164260863); + + $color = $this->getTestColor(181, 55, 23, 1); + $this->assertEquals($color->toInt(), 4290066199); + + $color = $this->getTestColor(0, 0, 0, 0); + $this->assertEquals($color->toInt(), 0); + } } From faa64b28def721936f0e686db8e9aa66dab3de81 Mon Sep 17 00:00:00 2001 From: Stephan Tijink Date: Wed, 15 Dec 2021 20:33:38 +0100 Subject: [PATCH 0222/1667] Since symfony/http-foundation 5.1: The "Symfony\Component\HttpFoundation\Response::create()" method is deprecated, use "new Symfony\Component\HttpFoundation\Response()" instead --- src/Intervention/Image/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Response.php b/src/Intervention/Image/Response.php index 8e7940811..59a51e992 100644 --- a/src/Intervention/Image/Response.php +++ b/src/Intervention/Image/Response.php @@ -62,7 +62,7 @@ public function make() } elseif (class_exists('\Symfony\Component\HttpFoundation\Response')) { - $response = SymfonyResponse::create($data); + $response = new SymfonyResponse($data); $response->headers->set('Content-Type', $mime); $response->headers->set('Content-Length', $length); From 228189b82edcbd2af83c83e0f7d7f62e065113d9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 7 Dec 2021 11:00:06 +0000 Subject: [PATCH 0223/1667] Added progress from last month Refined ResizeModifier tests Added AbstracImageTest AbstracImageTest AbstracImageTest Added AbstractColorTest FitModifierTests Changed preserve transparency logic in PadModifier PSR fix Imagick PadModifier FillModifier Added imagesavealpha when decoding new gd images Added ImageFactory::newCore() ImageFactory Imagick PadModifier FillModifier Fixed Jpeg Quality paramter Fixed Encoder Parameters PHP 8 only Added PixelateModifiers Removed dev from gitignore Added RgbStringColorDecoder for Imagick Driver Fixed lost transparency on FillModifier Changed default PadModifier backgroud to white Size & Resizer Refactoring Refactored ResizeModifiers Refactored FitModifiers Refactored PadModifier Changed resize & scale signatures Refactored ResizeModifiers Refactored ImageManager Added Constructor property promotion Added ImageObjectDecoder Fixed bugs Added default value for Image::blur() Added methods to image interface Renamed Image::loops() to Image::getLoops() Renamed Image::width() and Image::height() to getWidth(), getHeight() Changed exception texts and type RotateModifiers Added DestroyModifiers Added SharpenModifier Added methods Added InvertModifiers Added Collection::query Added default value to Collection::get Added brightness modifiers Added contrast modifiers Added readme.md --- .gitignore | 1 - composer.json | 5 +- readme.md | 61 +++++ src/Collection.php | 46 +++- src/Drivers/Abstract/AbstractImage.php | 177 +++++++------ .../Abstract/Decoders/AbstractDecoder.php | 6 +- .../Abstract/Encoders/AbstractEncoder.php | 5 - .../Modifiers/AbstractFitModifier.php | 36 +++ .../Modifiers/AbstractPadModifier.php | 31 +++ .../Modifiers/AbstractRotateModifier.php | 38 +++ src/Drivers/Gd/Color.php | 16 +- .../Gd/Decoders/BinaryImageDecoder.php | 6 + src/Drivers/Gd/Decoders/HexColorDecoder.php | 4 + .../Gd/Decoders/ImageObjectDecoder.php | 20 ++ .../Gd/Decoders/RgbStringColorDecoder.php | 35 +++ src/Drivers/Gd/Encoders/GifEncoder.php | 2 +- src/Drivers/Gd/Encoders/JpegEncoder.php | 5 + src/Drivers/Gd/Encoders/PngEncoder.php | 2 +- src/Drivers/Gd/Frame.php | 51 +--- src/Drivers/Gd/Image.php | 4 +- src/Drivers/Gd/ImageFactory.php | 18 +- src/Drivers/Gd/InputHandler.php | 18 +- src/Drivers/Gd/Modifiers/BlurModifier.php | 9 +- .../Gd/Modifiers/BrightnessModifier.php | 23 ++ src/Drivers/Gd/Modifiers/ContrastModifier.php | 23 ++ src/Drivers/Gd/Modifiers/DestroyModifier.php | 18 ++ src/Drivers/Gd/Modifiers/FillModifier.php | 58 +++++ src/Drivers/Gd/Modifiers/FitDownModifier.php | 13 + src/Drivers/Gd/Modifiers/FitModifier.php | 36 ++- src/Drivers/Gd/Modifiers/InvertModifier.php | 18 ++ src/Drivers/Gd/Modifiers/PadDownModifier.php | 25 ++ src/Drivers/Gd/Modifiers/PadModifier.php | 58 ++--- src/Drivers/Gd/Modifiers/PixelateModifier.php | 24 ++ src/Drivers/Gd/Modifiers/PlaceModifier.php | 22 +- .../Gd/Modifiers/ResizeDownModifier.php | 14 + src/Drivers/Gd/Modifiers/ResizeModifier.php | 30 ++- src/Drivers/Gd/Modifiers/RotateModifier.php | 57 +---- .../Gd/Modifiers/ScaleDownModifier.php | 14 + src/Drivers/Gd/Modifiers/ScaleModifier.php | 14 + src/Drivers/Gd/Modifiers/SharpenModifier.php | 37 +++ src/Drivers/Imagick/Color.php | 11 +- .../Imagick/Decoders/BinaryImageDecoder.php | 4 + .../Imagick/Decoders/HexColorDecoder.php | 4 + .../Imagick/Decoders/ImageObjectDecoder.php | 20 ++ .../Decoders/RgbStringColorDecoder.php | 34 +++ src/Drivers/Imagick/Encoders/GifEncoder.php | 2 +- src/Drivers/Imagick/Encoders/JpegEncoder.php | 5 + src/Drivers/Imagick/Frame.php | 18 +- src/Drivers/Imagick/Image.php | 4 +- src/Drivers/Imagick/ImageFactory.php | 15 +- src/Drivers/Imagick/InputHandler.php | 18 +- .../Imagick/Modifiers/BlurModifier.php | 9 +- .../Imagick/Modifiers/BrightnessModifier.php | 23 ++ .../Imagick/Modifiers/ContrastModifier.php | 23 ++ .../Imagick/Modifiers/CropResizeModifier.php | 45 ---- .../Imagick/Modifiers/DestroyModifier.php | 18 ++ .../Imagick/Modifiers/FillModifier.php | 70 +++++ .../Imagick/Modifiers/FitDownModifier.php | 13 + src/Drivers/Imagick/Modifiers/FitModifier.php | 30 +-- .../Imagick/Modifiers/InvertModifier.php | 18 ++ .../Imagick/Modifiers/PadDownModifier.php | 25 ++ src/Drivers/Imagick/Modifiers/PadModifier.php | 76 +++++- .../Imagick/Modifiers/PixelateModifier.php | 37 +++ .../Imagick/Modifiers/PlaceModifier.php | 18 +- .../Imagick/Modifiers/ResizeDownModifier.php | 14 + .../Imagick/Modifiers/ResizeModifier.php | 17 +- .../Imagick/Modifiers/RotateModifier.php | 27 ++ .../Imagick/Modifiers/ScaleDownModifier.php | 14 + .../Imagick/Modifiers/ScaleModifier.php | 14 + .../Imagick/Modifiers/SharpenModifier.php | 23 ++ src/EncodedImage.php | 12 +- src/Exceptions/GeometryException.php | 8 + .../MissingDriverComponentException.php | 8 + src/Geometry/Point.php | 25 +- src/Geometry/Resizer.php | 144 ++++------- src/Geometry/Size.php | 34 +-- src/ImageManager.php | 73 +----- src/Interfaces/ImageInterface.php | 37 ++- src/Traits/CanBuildNewImage.php | 15 ++ src/Traits/CanResolveDriverClass.php | 12 +- tests/AbstractImageTest.php | 240 ++++++++++++++++++ tests/CollectionTest.php | 24 ++ tests/Drivers/Abstract/AbstractColorTest.php | 36 +++ .../Gd/Decoders/BinaryImageDecoderTest.php | 12 +- .../Gd/Decoders/ImageObjectDecoderTest.php | 20 ++ .../Gd/Decoders/RgbStringColorDecoderTest.php | 32 +++ tests/Drivers/Gd/Encoders/JpegEncoderTest.php | 6 +- tests/Drivers/Gd/ImageFactoryTest.php | 8 + tests/Drivers/Gd/ImageTest.php | 8 +- .../Gd/Modifiers/BrightnessModifierTest.php | 21 ++ .../Gd/Modifiers/ContrastModifierTest.php | 21 ++ .../Gd/Modifiers/DestroyModifierTest.php | 21 ++ .../Drivers/Gd/Modifiers/FillModifierTest.php | 34 +++ .../Drivers/Gd/Modifiers/FitModifierTest.php | 29 +++ .../Gd/Modifiers/InvertModifierTest.php | 23 ++ .../Gd/Modifiers/PixelateModifierTest.php | 23 ++ .../Gd/Modifiers/ResizeModifierTest.php | 18 +- .../Gd/Modifiers/SharpenModifierTest.php | 21 ++ .../Decoders/ImageObjectDecoderTest.php | 20 ++ .../Decoders/RgbStringColorDecoderTest.php | 32 +++ .../Imagick/Encoders/JpegEncoderTest.php | 6 +- tests/Drivers/Imagick/ImageFactoryTest.php | 25 ++ tests/Drivers/Imagick/ImageTest.php | 4 +- .../Modifiers/BrightnessModifierTest.php | 21 ++ .../Modifiers/ContrastModifierTest.php | 21 ++ .../Imagick/Modifiers/DestroyModifierTest.php | 21 ++ .../Imagick/Modifiers/FillModifierTest.php | 36 +++ .../Imagick/Modifiers/FitModifierTest.php | 29 +++ .../Imagick/Modifiers/InvertModifierTest.php | 23 ++ .../Modifiers/PixelateModifierTest.php | 23 ++ .../Imagick/Modifiers/ResizeModifierTest.php | 20 +- .../Imagick/Modifiers/SharpenModifierTest.php | 21 ++ tests/Geometry/ResizerTest.php | 204 ++++++--------- tests/Geometry/SizeTest.php | 28 +- tests/ImageManagerTest.php | 29 +-- tests/TestCase.php | 16 +- tests/images/blocks.png | Bin 0 -> 467 bytes 117 files changed, 2418 insertions(+), 855 deletions(-) create mode 100644 readme.md create mode 100644 src/Drivers/Abstract/Modifiers/AbstractFitModifier.php create mode 100644 src/Drivers/Abstract/Modifiers/AbstractPadModifier.php create mode 100644 src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php create mode 100644 src/Drivers/Gd/Decoders/ImageObjectDecoder.php create mode 100644 src/Drivers/Gd/Decoders/RgbStringColorDecoder.php create mode 100644 src/Drivers/Gd/Modifiers/BrightnessModifier.php create mode 100644 src/Drivers/Gd/Modifiers/ContrastModifier.php create mode 100644 src/Drivers/Gd/Modifiers/DestroyModifier.php create mode 100644 src/Drivers/Gd/Modifiers/FillModifier.php create mode 100644 src/Drivers/Gd/Modifiers/FitDownModifier.php create mode 100644 src/Drivers/Gd/Modifiers/InvertModifier.php create mode 100644 src/Drivers/Gd/Modifiers/PadDownModifier.php create mode 100644 src/Drivers/Gd/Modifiers/PixelateModifier.php create mode 100644 src/Drivers/Gd/Modifiers/ResizeDownModifier.php create mode 100644 src/Drivers/Gd/Modifiers/ScaleDownModifier.php create mode 100644 src/Drivers/Gd/Modifiers/ScaleModifier.php create mode 100644 src/Drivers/Gd/Modifiers/SharpenModifier.php create mode 100644 src/Drivers/Imagick/Decoders/ImageObjectDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php create mode 100644 src/Drivers/Imagick/Modifiers/BrightnessModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ContrastModifier.php delete mode 100644 src/Drivers/Imagick/Modifiers/CropResizeModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DestroyModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/FillModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/FitDownModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/InvertModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/PadDownModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/PixelateModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ResizeDownModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/RotateModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ScaleDownModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/ScaleModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/SharpenModifier.php create mode 100644 src/Exceptions/GeometryException.php create mode 100644 src/Exceptions/MissingDriverComponentException.php create mode 100644 src/Traits/CanBuildNewImage.php create mode 100644 tests/AbstractImageTest.php create mode 100644 tests/Drivers/Abstract/AbstractColorTest.php create mode 100644 tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php create mode 100644 tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php create mode 100644 tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/ContrastModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/DestroyModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/FillModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/FitModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/InvertModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/PixelateModifierTest.php create mode 100644 tests/Drivers/Gd/Modifiers/SharpenModifierTest.php create mode 100644 tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php create mode 100644 tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php create mode 100644 tests/Drivers/Imagick/ImageFactoryTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/FillModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/FitModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/InvertModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php create mode 100644 tests/images/blocks.png diff --git a/.gitignore b/.gitignore index 799dd9418..954837503 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .DS_Store composer.lock vendor/ -dev/ .idea/ .phpunit.result.cache \ No newline at end of file diff --git a/composer.json b/composer.json index b075794eb..dabcb3533 100644 --- a/composer.json +++ b/composer.json @@ -12,12 +12,13 @@ } ], "require": { - "php": "^7.4|^8", + "php": "^8", "intervention/gif": "dev-master", "intervention/mimesniffer": "^0.4.2" }, "require-dev": { - "phpunit/phpunit": "^9" + "phpunit/phpunit": "^9", + "mockery/mockery": "^1.4" }, "autoload": { "psr-4": { diff --git a/readme.md b/readme.md new file mode 100644 index 000000000..116625165 --- /dev/null +++ b/readme.md @@ -0,0 +1,61 @@ +# Intervention Image +## PHP Image Manipulation + +[![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) +[![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) +[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) + +Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. + +- Simple interface for +- Driver agnostic +- Support for animated images +- Framework-agnostic, will work with any project +- PSR-12 compliant + +## Code Examples + +```php +// create image manager with desired driver +$manager = new ImageManager('gd') + +// open an image file +$image = $manager->make('images/example.jpg'); + +// resize image instance +$image->resize(320, 240); + +// insert a watermark +$image->place('images/watermark.png'); + +// encode edited image +$encoded = $image->toJpg(); + +// save encoded image +$encoded->save('images/example.jpg'); +``` + +## Requirements + +- PHP >=8.0 + +## Supported Image Libraries + +- GD Library +- Imagick PHP extension + +## Installation + +```bash +composer require intervention/image +``` + +## Getting started + +Learn the [basics](https://image.intervention.io/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/). + +## License + +Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). + +Copyright 2021 Oliver Vogel diff --git a/src/Collection.php b/src/Collection.php index 996997267..5d669ae3c 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -7,20 +7,14 @@ use ArrayIterator; use Countable; use IteratorAggregate; +use RecursiveIteratorIterator; +use RecursiveArrayIterator; class Collection implements CollectionInterface, IteratorAggregate, Countable { - protected $items = []; - - /** - * Create a collection. - * - * @param array $items - * @return void - */ - public function __construct(array $items = []) + public function __construct(protected array $items = []) { - $this->items = $items; + // } /** @@ -106,15 +100,25 @@ public function last() * @param integer $key * @return mixed */ - public function get(int $key = 0) + public function get(int $key = 0, $default = null) { if (! array_key_exists($key, $this->items)) { - return null; + return $default; } return $this->items[$key]; } + public function query(string $query, $default = null) + { + $items = $this->getItemsFlat(); + if (!array_key_exists($query, $items)) { + return $default; + } + + return $items[$query]; + } + public function map(callable $callback): self { $items = array_map(function ($item) use ($callback) { @@ -136,4 +140,22 @@ public function pushEach(array $data, ?callable $callback = null): CollectionInt return $this; } + + private function getItemsFlat(): array + { + $iterator = new RecursiveIteratorIterator( + new RecursiveArrayIterator($this->items) + ); + + $items = []; + foreach ($iterator as $value) { + $keys = []; + foreach (range(0, $iterator->getDepth()) as $depth) { + $keys[] = $iterator->getSubIterator($depth)->key(); + } + $items[join('.', $keys)] = $value; + } + + return $items; + } } diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2837a3cd9..29ae8f9bf 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -5,6 +5,7 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Intervention\Image\Exceptions\NotWritableException; +use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; @@ -12,18 +13,17 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; abstract class AbstractImage { use CanResolveDriverClass; + use CanHandleInput; - protected $loops = 0; - protected $frames; - - public function __construct(Collection $frames) + public function __construct(protected Collection $frames, protected $loops = 0) { - $this->frames = $frames; + // } public function getIterator(): Collection @@ -55,14 +55,19 @@ public function setLoops(int $count): ImageInterface return $this; } - public function loops(): int + public function getLoops(): int { return $this->loops; } public function getSize(): SizeInterface { - return new Size($this->width(), $this->height()); + return new Size($this->getWidth(), $this->getHeight()); + } + + public function size(): SizeInterface + { + return $this->getSize(); } public function isAnimated(): bool @@ -80,7 +85,7 @@ public function encode(EncoderInterface $encoder): EncodedImage return $encoder->encode($this); } - public function toJpeg(?int $quality = null): EncodedImage + public function toJpeg(int $quality = 75): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\JpegEncoder', $quality) @@ -108,142 +113,152 @@ public function greyscale(): ImageInterface ); } - public function blur(int $amount): ImageInterface + public function invert(): ImageInterface { return $this->modify( - $this->resolveDriverClass('Modifiers\BlurModifier', $amount) + $this->resolveDriverClass('Modifiers\InvertModifier') ); } - public function pickColors(int $x, int $y): Collection + public function brightness(int $level): ImageInterface { - $colors = new Collection(); - foreach ($this->getFrames() as $key => $frame) { - $colors->push($this->pickColor($x, $y, $key)); - } - - return $colors; + return $this->modify( + $this->resolveDriverClass('Modifiers\BrightnessModifier', $level) + ); } - public function resize(...$arguments): ImageInterface + public function contrast(int $level): ImageInterface { - $resized = Resizer::make()->setTargetSizeByArray($arguments) - ->resize($this->getSize()); - return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) + $this->resolveDriverClass('Modifiers\ContrastModifier', $level) ); } - public function resizeDown(...$arguments): ImageInterface + public function blur(int $amount = 5): ImageInterface { - $resized = Resizer::make()->setTargetSizeByArray($arguments) - ->resizeDown($this->getSize()); - return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) + $this->resolveDriverClass('Modifiers\BlurModifier', $amount) ); } - public function scale(...$arguments): ImageInterface + public function rotate(float $angle, $background = 'ffffff'): ImageInterface { - $resized = Resizer::make()->setTargetSizeByArray($arguments) - ->scale($this->getSize()); - return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) + $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $background) ); } - public function scaleDown(...$arguments): ImageInterface + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { - $resized = Resizer::make()->setTargetSizeByArray($arguments) - ->scaleDown($this->getSize()); - return $this->modify( - $this->resolveDriverClass('Modifiers\ResizeModifier', $resized) + $this->resolveDriverClass( + 'Modifiers\PlaceModifier', + $element, + $position, + $offset_x, + $offset_y + ) ); } - public function fit(int $width, int $height, string $position = 'center'): ImageInterface + public function fill($color, ?int $x = null, ?int $y = null): ImageInterface { - // original - $imagesize = $this->getSize(); - - // crop - $crop = new Size($width, $height); - $crop = $crop->contain($imagesize)->alignPivotTo($imagesize, $position); + $color = $this->handleInput($color); + $position = (is_null($x) && is_null($y)) ? null : new Point($x, $y); - // resize - $resize = $crop->scale($width, $height); + return $this->modify( + $this->resolveDriverClass( + 'Modifiers\FillModifier', + $color, + $position + ) + ); + } + public function pixelate(int $size): ImageInterface + { return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\PixelateModifier', $size) ); } - public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface + public function sharpen(int $amount = 10): ImageInterface { - // original - $imagesize = $this->getSize(); + return $this->modify( + $this->resolveDriverClass('Modifiers\SharpenModifier', $amount) + ); + } - // crop - $crop = new Size($width, $height); - $crop = $crop->contain($imagesize)->alignPivotTo($imagesize, $position); + public function pickColors(int $x, int $y): Collection + { + $colors = new Collection(); + foreach ($this->getFrames() as $key => $frame) { + $colors->push($this->pickColor($x, $y, $key)); + } - // resize - $resize = $crop->scaleDown($width, $height); + return $colors; + } + public function resize(?int $width = null, ?int $height = null): ImageInterface + { return $this->modify( - $this->resolveDriverClass('Modifiers\FitModifier', $crop, $resize) + $this->resolveDriverClass('Modifiers\ResizeModifier', $width, $height) ); } - public function pad(int $width, int $height, string $position = 'center', $backgroundColor = 'transparent'): ImageInterface + public function resizeDown(?int $width = null, ?int $height = null): ImageInterface { - // original - $imagesize = $this->getSize(); - - $resize = new Size($width, $height); - $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); - return $this->modify( - $this->resolveDriverClass('Modifiers\PadModifier', $crop, $resize, $backgroundColor) + $this->resolveDriverClass('Modifiers\ResizeDownModifier', $width, $height) ); } - public function padDown(int $width, int $height, string $position = 'center', $backgroundColor = 'transparent'): ImageInterface + public function scale(?int $width = null, ?int $height = null): ImageInterface { - // original - $imagesize = $this->getSize(); + return $this->modify( + $this->resolveDriverClass('Modifiers\ScaleModifier', $width, $height) + ); + } - $resize = new Size($width, $height); - $resize = $resize->resizeDown($imagesize); - $crop = $imagesize->contain($resize)->alignPivotTo($resize, $position); + public function scaleDown(?int $width = null, ?int $height = null): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\ScaleDownModifier', $width, $height) + ); + } + public function fit(int $width, int $height, string $position = 'center'): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FitModifier', $width, $height, $position) + ); + } + public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface + { return $this->modify( - $this->resolveDriverClass('Modifiers\PadModifier', $crop, $resize, $backgroundColor) + $this->resolveDriverClass('Modifiers\FitDownModifier', $width, $height, $position) ); } - public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface + public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface { return $this->modify( - $this->resolveDriverClass( - 'Modifiers\PlaceModifier', - $element, - $position, - $offset_x, - $offset_y - ) + $this->resolveDriverClass('Modifiers\PadModifier', $width, $height, $background, $position) ); } - public function rotate(float $angle, $backgroundColor = 'ffffff'): ImageInterface + public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface { return $this->modify( - $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $backgroundColor) + $this->resolveDriverClass('Modifiers\PadDownModifier', $width, $height, $background, $position) + ); + } + + public function destroy(): void + { + $this->modify( + $this->resolveDriverClass('Modifiers\DestroyModifier') ); } } diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 32a8070ba..937400415 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -10,11 +10,9 @@ abstract class AbstractDecoder { - protected $successor = null; - - public function __construct(?AbstractDecoder $successor = null) + public function __construct(protected ?AbstractDecoder $successor = null) { - $this->successor = $successor; + // } final public function handle($input): null|ImageInterface|ColorInterface diff --git a/src/Drivers/Abstract/Encoders/AbstractEncoder.php b/src/Drivers/Abstract/Encoders/AbstractEncoder.php index a557311f2..726093eb3 100644 --- a/src/Drivers/Abstract/Encoders/AbstractEncoder.php +++ b/src/Drivers/Abstract/Encoders/AbstractEncoder.php @@ -8,11 +8,6 @@ abstract class AbstractEncoder { protected $quality; - public function __construct(?int $quality = null) - { - $this->quality = $quality; - } - /** * Get return value of callback through output buffer * diff --git a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php new file mode 100644 index 000000000..fa9e6b1bd --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php @@ -0,0 +1,36 @@ +getSize(); + + $crop = new Size($this->width, $this->height); + $crop = $crop->contain( + $imagesize->getWidth(), + $imagesize->getHeight() + )->alignPivotTo($imagesize, $this->position); + + return $crop; + } + + protected function getResizeSize(SizeInterface $size): SizeInterface + { + return $size->scale($this->width, $this->height); + } +} diff --git a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php new file mode 100644 index 000000000..1cd4f1ec6 --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php @@ -0,0 +1,31 @@ +getSize() + ->contain($this->width, $this->height) + ->alignPivotTo($this->getResizeSize($image), $this->position); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return new Size($this->width, $this->height); + } +} diff --git a/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php b/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php new file mode 100644 index 000000000..016211e35 --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php @@ -0,0 +1,38 @@ +angle, 360); + } + + protected function backgroundColor(): ColorInterface + { + try { + return $this->handleInput($this->background); + } catch (DecoderException $e) { + throw new TypeException("rotate(): Argument #2 must be a color value."); + } + } +} diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php index a7ef9a53c..46904b37a 100644 --- a/src/Drivers/Gd/Color.php +++ b/src/Drivers/Gd/Color.php @@ -7,21 +7,9 @@ class Color extends AbstractColor implements ColorInterface { - /** - * GD library integer value of color - * - * @var int - */ - protected $value; - - /** - * Create new color instance - * - * @param int $value - */ - public function __construct(int $value = 0) + public function __construct(protected int $value = 0) { - $this->value = $value; + // } public function red(): int diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index f60a97023..f4a36cf20 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -19,6 +19,10 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { + if (!is_string($input)) { + $this->fail(); + } + if (! $this->inputType($input)->isBinary()) { $this->fail(); } @@ -37,6 +41,8 @@ public function decode($input): ImageInterface|ColorInterface imagepalettetotruecolor($gd); } + imagesavealpha($gd, true); + return new Image(new Collection([new Frame($gd)])); } diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php index 940c327e9..2329fe50c 100644 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -11,6 +11,10 @@ class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { + if (!is_string($input)) { + $this->fail(); + } + $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; $result = preg_match($pattern, $input, $matches); diff --git a/src/Drivers/Gd/Decoders/ImageObjectDecoder.php b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php new file mode 100644 index 000000000..9dffd5b41 --- /dev/null +++ b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php @@ -0,0 +1,20 @@ +fail(); + } + + return $input; + } +} diff --git a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php new file mode 100644 index 000000000..53ded7c5c --- /dev/null +++ b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php @@ -0,0 +1,35 @@ +fail(); + } + + if (substr($input, 0, 3) !== 'rgb') { + $this->fail(); + } + + // rgb string like rgb(102, 200, 0) + $pattern = "/^rgb ?\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/i"; + if ((bool) preg_match($pattern, $input, $matches)) { + return parent::decode([$matches['r'], $matches['g'], $matches['b']]); + } + + // rgba string like "rgba(200, 10, 30, 0.5)" + $pattern = "/^rgba ?\(((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?(?P
[0-9.]{1,4})\)$/i"; + if ((bool) preg_match($pattern, $input, $matches)) { + return parent::decode([$matches['r'], $matches['g'], $matches['b'], $matches['a']]); + } + + $this->fail(); + } +} diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 99d37001a..9438e8a81 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -25,7 +25,7 @@ public function encode(ImageInterface $image): EncodedImage protected function encodeAnimated($image): EncodedImage { - $builder = GifBuilder::canvas($image->width(), $image->height(), $image->loops()); + $builder = GifBuilder::canvas($image->getWidth(), $image->getHeight(), $image->getLoops()); foreach ($image as $key => $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php index 02a3e969d..09e695e79 100644 --- a/src/Drivers/Gd/Encoders/JpegEncoder.php +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -9,6 +9,11 @@ class JpegEncoder extends AbstractEncoder implements EncoderInterface { + public function __construct(int $quality) + { + $this->quality = $quality; + } + public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { diff --git a/src/Drivers/Gd/Encoders/PngEncoder.php b/src/Drivers/Gd/Encoders/PngEncoder.php index f93afb397..bc578b47e 100644 --- a/src/Drivers/Gd/Encoders/PngEncoder.php +++ b/src/Drivers/Gd/Encoders/PngEncoder.php @@ -12,7 +12,7 @@ class PngEncoder extends AbstractEncoder implements EncoderInterface public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { - imagepng($image->getFrames()->first()->getCore()); + imagepng($image->getFrames()->first()->getCore(), null, -1); }); return new EncodedImage($data, 'image/png'); diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 7cbca2e7a..ea33a0369 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -12,44 +12,14 @@ class Frame extends AbstractFrame implements FrameInterface { - /** - * Gd image representation of frame - * - * @var GdImage - */ - protected $core; - - /** - * Delay time in seconds after next frame is shown - * - * @var float - */ - protected $delay = 0; - - /** - * Disposal method of frame - * - * @var integer - */ - protected $dispose = 1; - - /** - * Left offset in pixel - * - * @var integer - */ - protected $offset_left = 0; - - /** - * Top offset in pixel - * - * @var integer - */ - protected $offset_top = 0; - - public function __construct(GdImage $core) - { - $this->core = $core; + public function __construct( + protected GdImage $core, + protected float $delay = 0, + protected int $dispose = 1, + protected int $offset_left = 0, + protected int $offset_top = 0 + ) { + // } public function getCore(): GdImage @@ -64,6 +34,11 @@ public function setCore(GdImage $core): self return $this; } + public function unsetCore(): void + { + unset($this->core); + } + public function getSize(): SizeInterface { return new Size(imagesx($this->core), imagesy($this->core)); diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index d226269aa..a8f00b980 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -16,12 +16,12 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { - public function width(): int + public function getWidth(): int { return imagesx($this->getFrame()->getCore()); } - public function height(): int + public function getHeight(): int { return imagesy($this->getFrame()->getCore()); } diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index f68017e2e..467e0332d 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd; +use GdImage; use Intervention\Image\Collection; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -10,15 +11,20 @@ class ImageFactory implements FactoryInterface { public function newImage(int $width, int $height): ImageInterface { - $gd = imagecreatetruecolor($width, $height); - $color = imagecolorallocatealpha($gd, 0, 0, 0, 127); - imagefill($gd, 0, 0, $color); - imagesavealpha($gd, true); - return new Image( new Collection([ - new Frame($gd) + new Frame($this->newCore($width, $height)) ]) ); } + + public function newCore(int $width, int $height): GdImage + { + $core = imagecreatetruecolor($width, $height); + $color = imagecolorallocatealpha($core, 0, 0, 0, 127); + imagefill($core, 0, 0, $color); + imagesavealpha($core, true); + + return $core; + } } diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 02fabd68f..3b66efe29 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -9,13 +9,17 @@ class InputHandler extends AbstractInputHandler { protected function chain(): AbstractDecoder { - return new Decoders\ArrayColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + return new Decoders\ImageObjectDecoder( + new Decoders\ArrayColorDecoder( + new Decoders\RgbStringColorDecoder( + new Decoders\HexColorDecoder( + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) + ) ) ) ) diff --git a/src/Drivers/Gd/Modifiers/BlurModifier.php b/src/Drivers/Gd/Modifiers/BlurModifier.php index 376b364f4..508d34bdf 100644 --- a/src/Drivers/Gd/Modifiers/BlurModifier.php +++ b/src/Drivers/Gd/Modifiers/BlurModifier.php @@ -8,14 +8,9 @@ class BlurModifier implements ModifierInterface { - /** - * Create new modifier - * - * @param int $amount Blur amount (0 - 100%) - */ - public function __construct(int $amount) + public function __construct(protected int $amount) { - $this->amount = $amount; + // } public function apply(ImageInterface $image): ImageInterface diff --git a/src/Drivers/Gd/Modifiers/BrightnessModifier.php b/src/Drivers/Gd/Modifiers/BrightnessModifier.php new file mode 100644 index 000000000..ee306bf02 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/BrightnessModifier.php @@ -0,0 +1,23 @@ +getCore(), IMG_FILTER_BRIGHTNESS, ($this->level * 2.55)); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/ContrastModifier.php b/src/Drivers/Gd/Modifiers/ContrastModifier.php new file mode 100644 index 000000000..28f0107a7 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ContrastModifier.php @@ -0,0 +1,23 @@ +getCore(), IMG_FILTER_CONTRAST, ($this->level * -1)); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/DestroyModifier.php b/src/Drivers/Gd/Modifiers/DestroyModifier.php new file mode 100644 index 000000000..2ea954164 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DestroyModifier.php @@ -0,0 +1,18 @@ +unsetCore(); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php new file mode 100644 index 000000000..70e5fe2ce --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -0,0 +1,58 @@ + $frame) { + if ($this->hasPosition()) { + $this->floodFillWithColor($frame); + } else { + $this->fillAllWithColor($frame); + } + } + + return $image; + } + + protected function floodFillWithColor(Frame $frame): void + { + imagefill( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->color->toInt() + ); + } + + protected function fillAllWithColor(Frame $frame): void + { + imagealphablending($frame->getCore(), true); + imagefilledrectangle( + $frame->getCore(), + 0, + 0, + $frame->getSize()->getWidth() - 1, + $frame->getSize()->getHeight() - 1, + $this->color->toInt() + ); + } + + protected function hasPosition(): bool + { + return !empty($this->position); + } +} diff --git a/src/Drivers/Gd/Modifiers/FitDownModifier.php b/src/Drivers/Gd/Modifiers/FitDownModifier.php new file mode 100644 index 000000000..5bf007748 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FitDownModifier.php @@ -0,0 +1,13 @@ +scaleDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 35d07bdd1..194376a5f 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractFitModifier; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -9,32 +11,26 @@ use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResizeGeometrically; -class FitModifier implements ModifierInterface +class FitModifier extends AbstractFitModifier implements ModifierInterface { - protected $crop; - protected $resize; - - public function __construct(SizeInterface $crop, SizeInterface $resize) - { - $this->crop = $crop; - $this->resize = $resize; - } - public function apply(ImageInterface $image): ImageInterface { + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($crop); + foreach ($image as $frame) { - $this->modify($frame); + $this->modifyFrame($frame, $crop, $resize); } return $image; } - protected function modify(FrameInterface $frame): void + protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image $modified = imagecreatetruecolor( - $this->resize->getWidth(), - $this->resize->getHeight() + $resize->getWidth(), + $resize->getHeight() ); // get current image @@ -59,12 +55,12 @@ protected function modify(FrameInterface $frame): void $current, 0, 0, - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY(), - $this->resize->getWidth(), - $this->resize->getHeight(), - $this->crop->getWidth(), - $this->crop->getHeight() + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $resize->getWidth(), + $resize->getHeight(), + $crop->getWidth(), + $crop->getHeight() ); imagedestroy($current); diff --git a/src/Drivers/Gd/Modifiers/InvertModifier.php b/src/Drivers/Gd/Modifiers/InvertModifier.php new file mode 100644 index 000000000..654e99b1a --- /dev/null +++ b/src/Drivers/Gd/Modifiers/InvertModifier.php @@ -0,0 +1,18 @@ +getCore(), IMG_FILTER_NEGATE); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/PadDownModifier.php b/src/Drivers/Gd/Modifiers/PadDownModifier.php new file mode 100644 index 000000000..cb80248da --- /dev/null +++ b/src/Drivers/Gd/Modifiers/PadDownModifier.php @@ -0,0 +1,25 @@ +getResizeSize($image); + + return $image->getSize() + ->contain($resize->getWidth(), $resize->getHeight()) + ->alignPivotTo($resize, $this->position); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return (new Size($this->width, $this->height)) + ->resizeDown($image->getWidth(), $image->getHeight()); + } +} diff --git a/src/Drivers/Gd/Modifiers/PadModifier.php b/src/Drivers/Gd/Modifiers/PadModifier.php index e0680b774..630bb2841 100644 --- a/src/Drivers/Gd/Modifiers/PadModifier.php +++ b/src/Drivers/Gd/Modifiers/PadModifier.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -9,68 +11,54 @@ use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResizeGeometrically; -class PadModifier implements ModifierInterface +class PadModifier extends AbstractPadModifier implements ModifierInterface { use CanHandleInput; - protected $crop; - protected $resize; - protected $backgroundColor; - - public function __construct(SizeInterface $crop, SizeInterface $resize, $backgroundColor = null) - { - $this->crop = $crop; - $this->resize = $resize; - $this->backgroundColor = $backgroundColor; - } - public function apply(ImageInterface $image): ImageInterface { + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($image); + $background = $this->handleInput($this->background); + foreach ($image as $frame) { - $this->modify($frame); + $this->modify($frame, $crop, $resize, $background); } return $image; } - protected function modify(FrameInterface $frame): void - { + protected function modify( + FrameInterface $frame, + SizeInterface $crop, + SizeInterface $resize, + ColorInterface $background + ): void { // create new image $modified = imagecreatetruecolor( - $this->resize->getWidth(), - $this->resize->getHeight() + $resize->getWidth(), + $resize->getHeight() ); - $color = $this->handleInput($this->backgroundColor); - - imagefill($modified, 0, 0, $color->toInt()); + imagefill($modified, 0, 0, $background->toInt()); // get current image $current = $frame->getCore(); // 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); - } + imagealphablending($modified, false); + imagesavealpha($modified, true); // copy content from resource imagecopyresampled( $modified, $current, - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), 0, 0, - $this->crop->getWidth(), - $this->crop->getHeight(), + $crop->getWidth(), + $crop->getHeight(), $frame->getSize()->getWidth(), $frame->getSize()->getHeight() ); diff --git a/src/Drivers/Gd/Modifiers/PixelateModifier.php b/src/Drivers/Gd/Modifiers/PixelateModifier.php new file mode 100644 index 000000000..c61cb1915 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/PixelateModifier.php @@ -0,0 +1,24 @@ +getCore(), IMG_FILTER_PIXELATE, $this->size, true); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 596ee79f6..9350170af 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -12,21 +12,17 @@ class PlaceModifier implements ModifierInterface { use CanResolveDriverClass; - protected $element; - protected $position; - protected $offset_x; - protected $offset_y; - /** * Create new modifier * */ - public function __construct($element, string $position, int $offset_x, int $offset_y) - { - $this->element = $element; - $this->position = $position; - $this->offset_x = $offset_x; - $this->offset_y = $offset_y; + public function __construct( + protected $element, + protected string $position, + protected int $offset_x, + protected int $offset_y + ) { + // } public function apply(ImageInterface $image): ImageInterface @@ -43,8 +39,8 @@ public function apply(ImageInterface $image): ImageInterface $position->getY(), 0, 0, - $watermark->width(), - $watermark->height() + $watermark->getWidth(), + $watermark->getHeight() ); } diff --git a/src/Drivers/Gd/Modifiers/ResizeDownModifier.php b/src/Drivers/Gd/Modifiers/ResizeDownModifier.php new file mode 100644 index 000000000..125037582 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ResizeDownModifier.php @@ -0,0 +1,14 @@ +getSize()->resizeDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 7ad14d3eb..b030a8e38 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\Geometry\Resizer; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -10,28 +11,33 @@ class ResizeModifier implements ModifierInterface { - protected $resize; - - public function __construct(SizeInterface $resize) + public function __construct(protected ?int $width = null, protected ?int $height = null) { - $this->resize = $resize; + // } public function apply(ImageInterface $image): ImageInterface { + $resizeTo = $this->getAdjustedSize($image); + foreach ($image as $frame) { - $this->modify($frame); + $this->resizeFrame($frame, $resizeTo); } return $image; } - protected function modify(FrameInterface $frame): void + protected function getAdjustedSize(ImageInterface $image): SizeInterface + { + return $image->getSize()->resize($this->width, $this->height); + } + + protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): void { // create new image $modified = imagecreatetruecolor( - $this->resize->getWidth(), - $this->resize->getHeight() + $resizeTo->getWidth(), + $resizeTo->getHeight() ); // get current image @@ -54,12 +60,12 @@ protected function modify(FrameInterface $frame): void imagecopyresampled( $modified, $current, - $this->resize->getPivot()->getX(), - $this->resize->getPivot()->getY(), + $resizeTo->getPivot()->getX(), + $resizeTo->getPivot()->getY(), 0, 0, - $this->resize->getWidth(), - $this->resize->getHeight(), + $resizeTo->getWidth(), + $resizeTo->getHeight(), $frame->getSize()->getWidth(), $frame->getSize()->getHeight() ); diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index 1da31d1f3..b692c4744 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -2,67 +2,24 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Exceptions\TypeException; -use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Interfaces\FrameInterface; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Traits\CanHandleInput; -class RotateModifier implements ModifierInterface +class RotateModifier extends AbstractRotateModifier implements ModifierInterface { - use CanHandleInput; - - /** - * Rotation angle - * - * @var float - */ - protected $angle; - - /** - * Background color - * - * @var mixed - */ - protected $backgroundColor; - - /** - * Create new modifier - * - * @param float $angle - */ - public function __construct(float $angle, $backgroundColor) - { - $this->angle = $angle; - $this->backgroundColor = $backgroundColor; - } - public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { $frame->setCore( - imagerotate($frame->getCore(), $this->rotationAngle(), $this->backgroundColor()) + imagerotate( + $frame->getCore(), + $this->rotationAngle(), + $this->backgroundColor()->toInt() + ) ); } return $image; } - - protected function rotationAngle(): float - { - // restrict rotations beyond 360 degrees, since the end result is the same - return fmod($this->angle, 360); - } - - protected function backgroundColor(): int - { - $color = $this->handleInput($this->backgroundColor); - - if (!is_a($color, ColorInterface::class)) { - throw new TypeException("rotate(): Argument #2 must be of color value."); - } - - return $color->toInt(); - } } diff --git a/src/Drivers/Gd/Modifiers/ScaleDownModifier.php b/src/Drivers/Gd/Modifiers/ScaleDownModifier.php new file mode 100644 index 000000000..3a0e5b9a0 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ScaleDownModifier.php @@ -0,0 +1,14 @@ +getSize()->scaleDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Gd/Modifiers/ScaleModifier.php b/src/Drivers/Gd/Modifiers/ScaleModifier.php new file mode 100644 index 000000000..ff7491af3 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/ScaleModifier.php @@ -0,0 +1,14 @@ +getSize()->scale($this->width, $this->height); + } +} diff --git a/src/Drivers/Gd/Modifiers/SharpenModifier.php b/src/Drivers/Gd/Modifiers/SharpenModifier.php new file mode 100644 index 000000000..d5bedcec2 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/SharpenModifier.php @@ -0,0 +1,37 @@ +matrix(); + foreach ($image as $frame) { + imageconvolution($frame->getCore(), $matrix, 1, 0); + } + + return $image; + } + + protected function matrix(): array + { + $min = $this->amount >= 10 ? $this->amount * -0.01 : 0; + $max = $this->amount * -0.025; + $abs = ((4 * $min + 4 * $max) * -1) + 1; + + return [ + [$min, $max, $min], + [$max, $abs, $max], + [$min, $max, $min] + ]; + } +} diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php index a04e07546..bda5e232c 100644 --- a/src/Drivers/Imagick/Color.php +++ b/src/Drivers/Imagick/Color.php @@ -9,16 +9,9 @@ class Color extends AbstractColor implements ColorInterface { - /** - * Imagick pixel to represent color - * - * @var ImagickPixel - */ - protected $pixel; - - public function __construct(ImagickPixel $pixel) + public function __construct(protected ImagickPixel $pixel) { - $this->pixel = $pixel; + // } public function getPixel(): ImagickPixel diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 1b931e539..f09a50ffe 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -15,6 +15,10 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { + if (!is_string($input)) { + $this->fail(); + } + if (! $this->inputType($input)->isBinary()) { $this->fail(); } diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php index a0609b348..2eafd126b 100644 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -11,6 +11,10 @@ class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { + if (!is_string($input)) { + $this->fail(); + } + $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; $result = preg_match($pattern, $input, $matches); diff --git a/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php new file mode 100644 index 000000000..72318e2ab --- /dev/null +++ b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php @@ -0,0 +1,20 @@ +fail(); + } + + return $input; + } +} diff --git a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php new file mode 100644 index 000000000..4e9c9fa24 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php @@ -0,0 +1,34 @@ +fail(); + } + + if (substr($input, 0, 3) !== 'rgb') { + $this->fail(); + } + + try { + $pixel = new ImagickPixel($input); + } catch (ImagickPixelException $e) { + $this->fail(); + } + + return new Color($pixel); + } +} diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index f795f174d..0cfca898d 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -20,7 +20,7 @@ public function encode(ImageInterface $image): EncodedImage $gif->addImage($frame->getCore()); } - $gif->setImageIterations($image->loops()); + $gif->setImageIterations($image->getLoops()); $gif->setFormat($format); $gif->setImageFormat($format); $gif->setCompression($compression); diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index 45fd72200..0ea13e671 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -10,6 +10,11 @@ class JpegEncoder extends AbstractEncoder implements EncoderInterface { + public function __construct(int $quality) + { + $this->quality = $quality; + } + public function encode(ImageInterface $image): EncodedImage { $format = 'jpeg'; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index b787f7732..632ad2c7f 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -12,16 +12,9 @@ class Frame extends AbstractFrame implements FrameInterface { - /** - * Imagick image representation of frame - * - * @var Imagick - */ - protected $core; - - public function __construct(Imagick $core) + public function __construct(protected Imagick $core) { - $this->core = $core; + // } public function getCore(): Imagick @@ -29,6 +22,13 @@ public function getCore(): Imagick return $this->core; } + public function setCore(Imagick $core): FrameInterface + { + $this->core = $core; + + return $this; + } + public function getSize(): SizeInterface { return new Size($this->core->getImageWidth(), $this->core->getImageHeight()); diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index a0c289afc..f04d8ae34 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -14,12 +14,12 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { - public function width(): int + public function getWidth(): int { return $this->frames->first()->getCore()->getImageWidth(); } - public function height(): int + public function getHeight(): int { return $this->frames->first()->getCore()->getImageHeight(); } diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 7cd240c04..cff14438d 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -12,6 +12,15 @@ class ImageFactory implements FactoryInterface { public function newImage(int $width, int $height): ImageInterface + { + return new Image( + new Collection([ + new Frame($this->newCore($width, $height)) + ]) + ); + } + + public function newCore(int $width, int $height): Imagick { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); @@ -19,10 +28,6 @@ public function newImage(int $width, int $height): ImageInterface $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); - return new Image( - new Collection([ - new Frame($imagick) - ]) - ); + return $imagick; } } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index fa5cd2265..839d401c0 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -9,13 +9,17 @@ class InputHandler extends AbstractInputHandler { protected function chain(): AbstractDecoder { - return new Decoders\ArrayColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + return new Decoders\ImageObjectDecoder( + new Decoders\ArrayColorDecoder( + new Decoders\HexColorDecoder( + new Decoders\RgbStringColorDecoder( + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) + ) ) ) ) diff --git a/src/Drivers/Imagick/Modifiers/BlurModifier.php b/src/Drivers/Imagick/Modifiers/BlurModifier.php index eb4fefa1d..59892999f 100644 --- a/src/Drivers/Imagick/Modifiers/BlurModifier.php +++ b/src/Drivers/Imagick/Modifiers/BlurModifier.php @@ -7,14 +7,9 @@ class BlurModifier implements ModifierInterface { - /** - * Create new modifier - * - * @param int $amount Blur amount (0 - 100%) - */ - public function __construct(int $amount) + public function __construct(protected int $amount) { - $this->amount = $amount; + // } public function apply(ImageInterface $image): ImageInterface diff --git a/src/Drivers/Imagick/Modifiers/BrightnessModifier.php b/src/Drivers/Imagick/Modifiers/BrightnessModifier.php new file mode 100644 index 000000000..50b092f26 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/BrightnessModifier.php @@ -0,0 +1,23 @@ +getCore()->modulateImage(100 + $this->level, 100, 100); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/ContrastModifier.php b/src/Drivers/Imagick/Modifiers/ContrastModifier.php new file mode 100644 index 000000000..1a1bb3ded --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ContrastModifier.php @@ -0,0 +1,23 @@ +getCore()->sigmoidalContrastImage($this->level > 0, abs($this->level / 4), 0); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php b/src/Drivers/Imagick/Modifiers/CropResizeModifier.php deleted file mode 100644 index 8a7b555db..000000000 --- a/src/Drivers/Imagick/Modifiers/CropResizeModifier.php +++ /dev/null @@ -1,45 +0,0 @@ -crop = $crop; - $this->resize = $resize; - $this->position = $position; - } - - public function apply(ImageInterface $image): ImageInterface - { - $shouldCrop = $this->crop != $image->getSize(); - - foreach ($image as $frame) { - if ($shouldCrop) { - $frame->getCore()->extentImage( - $this->crop->getWidth(), - $this->crop->getHeight(), - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY() - ); - } - - $frame->getCore()->scaleImage( - $this->resize->getWidth(), - $this->resize->getHeight() - ); - } - - return $image; - } -} diff --git a/src/Drivers/Imagick/Modifiers/DestroyModifier.php b/src/Drivers/Imagick/Modifiers/DestroyModifier.php new file mode 100644 index 000000000..d8efc3079 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DestroyModifier.php @@ -0,0 +1,18 @@ +getCore()->clear(); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php new file mode 100644 index 000000000..ddcf19df6 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -0,0 +1,70 @@ +hasPosition()) { + $this->floodFillWithColor($frame); + } else { + $this->fillAllWithColor($frame); + } + } + + return $image; + } + + protected function floodFillWithColor(Frame $frame): void + { + $target = $frame->getCore()->getImagePixelColor( + $this->position->getX(), + $this->position->getY() + ); + + $frame->getCore()->floodfillPaintImage( + $this->color->getPixel(), + 100, + $target, + $this->position->getX(), + $this->position->getY(), + false, + Imagick::CHANNEL_ALL + ); + } + + protected function fillAllWithColor(Frame $frame): void + { + $draw = new ImagickDraw(); + $draw->setFillColor($this->color->getPixel()); + $draw->rectangle( + 0, + 0, + $frame->getCore()->getImageWidth(), + $frame->getCore()->getImageHeight() + ); + $frame->getCore()->drawImage($draw); + } + + protected function hasPosition(): bool + { + return !empty($this->position); + } +} diff --git a/src/Drivers/Imagick/Modifiers/FitDownModifier.php b/src/Drivers/Imagick/Modifiers/FitDownModifier.php new file mode 100644 index 000000000..73e26ef7b --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FitDownModifier.php @@ -0,0 +1,13 @@ +scaleDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php index 992c992a7..5b34f7029 100644 --- a/src/Drivers/Imagick/Modifiers/FitModifier.php +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -2,35 +2,31 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractFitModifier; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -class FitModifier implements ModifierInterface +class FitModifier extends AbstractFitModifier implements ModifierInterface { - protected $crop; - protected $resize; - - public function __construct(SizeInterface $crop, SizeInterface $resize) - { - $this->crop = $crop; - $this->resize = $resize; - } - public function apply(ImageInterface $image): ImageInterface { + $crop = $this->getCropSize($image); + $resize = $this->getResizeSize($crop); + foreach ($image as $frame) { $frame->getCore()->extentImage( - $this->crop->getWidth(), - $this->crop->getHeight(), - $this->crop->getPivot()->getX(), - $this->crop->getPivot()->getY() + $crop->getWidth(), + $crop->getHeight(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY() ); - + $frame->getCore()->scaleImage( - $this->resize->getWidth(), - $this->resize->getHeight() + $resize->getWidth(), + $resize->getHeight() ); } diff --git a/src/Drivers/Imagick/Modifiers/InvertModifier.php b/src/Drivers/Imagick/Modifiers/InvertModifier.php new file mode 100644 index 000000000..08fc3c0b7 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/InvertModifier.php @@ -0,0 +1,18 @@ +getCore()->negateImage(false); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/PadDownModifier.php b/src/Drivers/Imagick/Modifiers/PadDownModifier.php new file mode 100644 index 000000000..e0649857c --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/PadDownModifier.php @@ -0,0 +1,25 @@ +getResizeSize($image); + + return $image->getSize() + ->contain($resize->getWidth(), $resize->getHeight()) + ->alignPivotTo($resize, $this->position); + } + + protected function getResizeSize(ImageInterface $image): SizeInterface + { + return (new Size($this->width, $this->height)) + ->resizeDown($image->getWidth(), $image->getHeight()); + } +} diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index df8eb29f3..414efc975 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -2,30 +2,82 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; +use Imagick; +use ImagickDraw; +use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanBuildNewImage; +use Intervention\Image\Traits\CanHandleInput; -class PadModifier implements ModifierInterface +class PadModifier extends AbstractPadModifier implements ModifierInterface { - protected $crop; - protected $resize; - protected $backgroundColor; - - public function __construct(SizeInterface $crop, SizeInterface $resize, $backgroundColor = null) - { - $this->crop = $crop; - $this->resize = $resize; - $this->backgroundColor = $backgroundColor; - } + use CanBuildNewImage; + use CanHandleInput; public function apply(ImageInterface $image): ImageInterface { + $resize = $this->getResizeSize($image); + $crop = $this->getCropSize($image); + $background = $this->handleInput($this->background); + foreach ($image as $frame) { - // + // resize current core + $frame->getCore()->scaleImage( + $crop->getWidth(), + $crop->getHeight() + ); + + // create new canvas, to get newly emerged background color + $canvas = $this->buildBaseCanvas($crop, $resize, $background); + + // place current core onto canvas + $canvas->compositeImage( + $frame->getCore(), + Imagick::COMPOSITE_DEFAULT, + $crop->getPivot()->getX(), + $crop->getPivot()->getY() + ); + + // replace core + $frame->getCore()->destroy(); + $frame->setCore($canvas); } return $image; } + + protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, ColorInterface $background): Imagick + { + // build base canvas in target size + $canvas = $this->imageFactory()->newCore( + $resize->getWidth(), + $resize->getHeight() + ); + + // draw background color on canvas + $draw = new ImagickDraw(); + $draw->setFillColor($background->getPixel()); + $draw->rectangle(0, 0, $canvas->getImageWidth(), $canvas->getImageHeight()); + $canvas->drawImage($draw); + + // make area where image is placed transparent to keep + // transparency even if background-color is set + $draw = new ImagickDraw(); + $fill = $background->toHex('#') == '#ff0000' ? '#00ff00' : '#ff0000'; + $draw->setFillColor($fill); + $draw->rectangle( + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $crop->getPivot()->getX() + $crop->getWidth() - 1, + $crop->getPivot()->getY() + $crop->getHeight() - 1 + ); + $canvas->drawImage($draw); + $canvas->transparentPaintImage($fill, 0, 0, false); + + return $canvas; + } } diff --git a/src/Drivers/Imagick/Modifiers/PixelateModifier.php b/src/Drivers/Imagick/Modifiers/PixelateModifier.php new file mode 100644 index 000000000..770f6f330 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/PixelateModifier.php @@ -0,0 +1,37 @@ +pixelateFrame($frame); + } + + return $image; + } + + protected function pixelateFrame(Frame $frame): void + { + $size = $frame->getSize(); + + $frame->getCore()->scaleImage( + max(1, ($size->getWidth() / $this->size)), + max(1, ($size->getHeight() / $this->size)) + ); + + $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); + } +} diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index c043adf09..06f1ef090 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -13,17 +13,13 @@ class PlaceModifier implements ModifierInterface { use CanResolveDriverClass; - protected $element; - protected $position; - protected $offset_x; - protected $offset_y; - - public function __construct($element, string $position, int $offset_x, int $offset_y) - { - $this->element = $element; - $this->position = $position; - $this->offset_x = $offset_x; - $this->offset_y = $offset_y; + public function __construct( + protected $element, + protected string $position, + protected int $offset_x, + protected int $offset_y + ) { + // } public function apply(ImageInterface $image): ImageInterface diff --git a/src/Drivers/Imagick/Modifiers/ResizeDownModifier.php b/src/Drivers/Imagick/Modifiers/ResizeDownModifier.php new file mode 100644 index 000000000..068dbccb2 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ResizeDownModifier.php @@ -0,0 +1,14 @@ +getSize()->resizeDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index 057c848a0..dbf0697a8 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -9,22 +9,27 @@ class ResizeModifier implements ModifierInterface { - protected $resize; - - public function __construct(SizeInterface $resize) + public function __construct(protected ?int $width = null, protected ?int $height = null) { - $this->resize = $resize; + // } public function apply(ImageInterface $image): ImageInterface { + $resizeTo = $this->getAdjustedSize($image); + foreach ($image as $frame) { $frame->getCore()->scaleImage( - $this->resize->getWidth(), - $this->resize->getHeight() + $resizeTo->getWidth(), + $resizeTo->getHeight() ); } return $image; } + + protected function getAdjustedSize(ImageInterface $image): SizeInterface + { + return $image->getSize()->resize($this->width, $this->height); + } } diff --git a/src/Drivers/Imagick/Modifiers/RotateModifier.php b/src/Drivers/Imagick/Modifiers/RotateModifier.php new file mode 100644 index 000000000..3fde612e1 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/RotateModifier.php @@ -0,0 +1,27 @@ +getCore()->rotateImage( + $this->backgroundColor()->getPixel(), + $this->rotationAngle() + ); + } + + return $image; + } + + protected function rotationAngle(): float + { + return parent::rotationAngle() * -1; + } +} diff --git a/src/Drivers/Imagick/Modifiers/ScaleDownModifier.php b/src/Drivers/Imagick/Modifiers/ScaleDownModifier.php new file mode 100644 index 000000000..45ec70f18 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ScaleDownModifier.php @@ -0,0 +1,14 @@ +getSize()->scaleDown($this->width, $this->height); + } +} diff --git a/src/Drivers/Imagick/Modifiers/ScaleModifier.php b/src/Drivers/Imagick/Modifiers/ScaleModifier.php new file mode 100644 index 000000000..2e99fae62 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ScaleModifier.php @@ -0,0 +1,14 @@ +getSize()->scale($this->width, $this->height); + } +} diff --git a/src/Drivers/Imagick/Modifiers/SharpenModifier.php b/src/Drivers/Imagick/Modifiers/SharpenModifier.php new file mode 100644 index 000000000..0ce2e92d1 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/SharpenModifier.php @@ -0,0 +1,23 @@ +getCore()->unsharpMaskImage(1, 1, $this->amount / 6.25, 0); + } + + return $image; + } +} diff --git a/src/EncodedImage.php b/src/EncodedImage.php index 7b0858f9d..011c6f158 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -6,13 +6,11 @@ class EncodedImage { - protected $data; - protected $mimetype; - - public function __construct(string $data, string $mimetype = 'application/octet-stream') - { - $this->data = $data; - $this->mimetype = $mimetype; + public function __construct( + protected string $data, + protected string $mimetype = 'application/octet-stream' + ) { + // } public function mimetype(): string diff --git a/src/Exceptions/GeometryException.php b/src/Exceptions/GeometryException.php new file mode 100644 index 000000000..7bca9ad72 --- /dev/null +++ b/src/Exceptions/GeometryException.php @@ -0,0 +1,8 @@ +x = $x; - $this->y = $y; + // } /** diff --git a/src/Geometry/Resizer.php b/src/Geometry/Resizer.php index 5b4c72771..6a5c0edd9 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Resizer.php @@ -2,113 +2,73 @@ namespace Intervention\Image\Geometry; +use Intervention\Image\Exceptions\GeometryException; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\SizeInterface; class Resizer { - /** - * Size to be resized - * - * @var SizeInterface - */ - protected $original; - - /** - * Target size - * - * @var SizeInterface - */ - protected $target; - - /** - * Create new instance - * - * @param SizeInterface $size - */ - public function __construct() - { - $this->target = new Size(0, 0); + public function __construct( + protected ?int $width = null, + protected ?int $height = null, + ) { + // } - public static function make(callable $callback = null): self + public static function to(...$arguments): self { - $resizer = new self(); - - if (is_callable($callback)) { - $callback($resizer); - } - - return $resizer; + return new self(...$arguments); } protected function hasTargetWidth(): bool { - return $this->target->getWidth() > 0; + return is_integer($this->width); } - protected function hasTargetHeight(): bool + protected function getTargetWidth(): ?int { - return $this->target->getHeight() > 0; + return $this->hasTargetWidth() ? $this->width : null; } - public function width(int $width): self + protected function hasTargetHeight(): bool { - $this->target->setWidth($width); - - return $this; + return is_integer($this->height); } - public function height(int $height): self + protected function getTargetHeight(): ?int { - $this->target->setHeight($height); - - return $this; + return $this->hasTargetHeight() ? $this->height : null; } - public function toWidth(int $width): self + protected function getTargetSize(): SizeInterface { - return $this->width($width); - } + if (!$this->hasTargetWidth() || !$this->hasTargetHeight()) { + throw new GeometryException('Target size needs width and height.'); + } - public function toHeight(int $height): self - { - return $this->height($height); + return new Size($this->width, $this->height); } - public function setTargetSizeByArray(array $arguments): self + public function toWidth(int $width): self { - if (isset($arguments[0]) && is_callable($arguments[0])) { - $arguments[0]($this); - - return $this; - } - - if (isset($arguments[0]) && is_a($arguments[0], Size::class)) { - return $this->toSize($arguments[0]); - } - - if (isset($arguments[0]) && is_numeric($arguments[0])) { - $this->width($arguments[0]); - } - - if (isset($arguments[1]) && is_numeric($arguments[1])) { - $this->height($arguments[1]); - } + $this->width = $width; return $this; } - public function setTargetSize(SizeInterface $size): self + public function toHeight(int $height): self { - $this->target = new Size($size->getWidth(), $size->getHeight()); + $this->height = $height; return $this; } public function toSize(SizeInterface $size): self { - return $this->setTargetSize($size); + $this->width = $size->getWidth(); + $this->height = $size->getHeight(); + + return $this; } protected function getProportionalWidth(SizeInterface $size): int @@ -117,7 +77,7 @@ protected function getProportionalWidth(SizeInterface $size): int return $size->getWidth(); } - return (int) round($this->target->getHeight() * $size->getAspectRatio()); + return (int) round($this->height * $size->getAspectRatio()); } protected function getProportionalHeight(SizeInterface $size): int @@ -126,19 +86,19 @@ protected function getProportionalHeight(SizeInterface $size): int return $size->getHeight(); } - return (int) round($this->target->getWidth() / $size->getAspectRatio()); + return (int) round($this->width / $size->getAspectRatio()); } public function resize(SizeInterface $size): SizeInterface { $resized = new Size($size->getWidth(), $size->getHeight()); - if ($this->hasTargetWidth()) { - $resized->setWidth($this->target->getWidth()); + if ($width = $this->getTargetWidth()) { + $resized->setWidth($width); } - if ($this->hasTargetHeight()) { - $resized->setHeight($this->target->getHeight()); + if ($height = $this->getTargetHeight()) { + $resized->setHeight($height); } return $resized; @@ -148,15 +108,15 @@ public function resizeDown(SizeInterface $size): SizeInterface { $resized = new Size($size->getWidth(), $size->getHeight()); - if ($this->hasTargetWidth()) { + if ($width = $this->getTargetWidth()) { $resized->setWidth( - min($this->target->getWidth(), $size->getWidth()) + min($width, $size->getWidth()) ); } - if ($this->hasTargetHeight()) { + if ($height = $this->getTargetHeight()) { $resized->setHeight( - min($this->target->getHeight(), $size->getHeight()) + min($height, $size->getHeight()) ); } @@ -170,18 +130,18 @@ public function scale(SizeInterface $size): SizeInterface if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( $this->getProportionalWidth($size), - $this->target->getWidth() + $this->getTargetWidth() )); $resized->setHeight(min( $this->getProportionalHeight($size), - $this->target->getHeight() + $this->getTargetHeight() )); } elseif ($this->hasTargetWidth()) { - $resized->setWidth($this->target->getWidth()); + $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->target->getHeight()); + $resized->setHeight($this->getTargetHeight()); } return $resized; @@ -194,17 +154,17 @@ public function scaleDown(SizeInterface $size): SizeInterface if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( $this->getProportionalWidth($size), - $this->target->getWidth(), + $this->getTargetWidth(), $size->getWidth() )); $resized->setHeight(min( $this->getProportionalHeight($size), - $this->target->getHeight(), + $this->getTargetHeight(), $size->getHeight() )); } elseif ($this->hasTargetWidth()) { $resized->setWidth(min( - $this->target->getWidth(), + $this->getTargetWidth(), $size->getWidth() )); $resized->setHeight(min( @@ -217,7 +177,7 @@ public function scaleDown(SizeInterface $size): SizeInterface $size->getWidth() )); $resized->setHeight(min( - $this->target->getHeight(), + $this->getTargetHeight(), $size->getHeight() )); } @@ -236,13 +196,13 @@ public function cover(SizeInterface $size): SizeInterface $resized = new Size($size->getWidth(), $size->getHeight()); // auto height - $resized->setWidth($this->target->getWidth()); + $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); - if ($resized->fitsInto($this->target)) { + if ($resized->fitsInto($this->getTargetSize())) { // auto width $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->target->getHeight()); + $resized->setHeight($this->getTargetHeight()); } return $resized; @@ -259,13 +219,13 @@ public function contain(SizeInterface $size): SizeInterface $resized = new Size($size->getWidth(), $size->getHeight()); // auto height - $resized->setWidth($this->target->getWidth()); + $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); - if (!$resized->fitsInto($this->target)) { + if (!$resized->fitsInto($this->getTargetSize())) { // auto width $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->target->getHeight()); + $resized->setHeight($this->getTargetHeight()); } return $resized; diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 05aa9ad15..797c15bc7 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -8,14 +8,11 @@ class Size implements SizeInterface { - protected $width; - protected $height; - protected $pivot; - - public function __construct(int $width, int $height, Point $pivot = null) - { - $this->width = $width; - $this->height = $height; + public function __construct( + protected int $width, + protected int $height, + protected ?Point $pivot = null + ) { $this->pivot = $pivot ? $pivot : new Point(); } @@ -214,39 +211,36 @@ public function getRelativePositionTo(Size $size): Point protected function getResizer(...$arguments): Resizer { - $resizer = new Resizer(); - $resizer->setTargetSizeByArray($arguments[0]); - - return $resizer; + return new Resizer(...$arguments); } public function resize(...$arguments): self { - return $this->getResizer($arguments)->resize($this); + return $this->getResizer(...$arguments)->resize($this); } public function resizeDown(...$arguments): self { - return $this->getResizer($arguments)->resizeDown($this); + return $this->getResizer(...$arguments)->resizeDown($this); } public function scale(...$arguments): self { - return $this->getResizer($arguments)->scale($this); + return $this->getResizer(...$arguments)->scale($this); } public function scaleDown(...$arguments): self { - return $this->getResizer($arguments)->scaleDown($this); + return $this->getResizer(...$arguments)->scaleDown($this); } - public function cover(...$arguments): self + public function cover(int $width, int $height): self { - return $this->getResizer($arguments)->cover($this); + return $this->getResizer($width, $height)->cover($this); } - public function contain(...$arguments): self + public function contain(int $width, int $height): self { - return $this->getResizer($arguments)->contain($this); + return $this->getResizer($width, $height)->contain($this); } } diff --git a/src/ImageManager.php b/src/ImageManager.php index 428df54b4..5cd249906 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -2,53 +2,16 @@ namespace Intervention\Image; -use Exception; -use ReflectionClass; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Traits\CanResolveDriverClass; class ImageManager { - /** - * Configuration data - * - * @var array - */ - protected $config = [ - 'driver' => 'gd', - ]; + use CanResolveDriverClass; - /** - * Create new instance - * - * @param array $config - */ - public function __construct(array $config = []) + public function __construct(protected string $driver = 'gd') { - $this->configure($config); - } - - /** - * Override configuration settings - * - * @param array $config - */ - public function configure(array $config = []): self - { - $this->config = array_replace($this->config, $config); - - return $this; - } - - /** - * Return given value of configuration - * - * @param string $key - * @param mixed $default - * @return mixed - */ - public function getConfig($key, $default = null) - { - return array_key_exists($key, $this->config) ? $this->config[$key] : $default; + // } /** @@ -60,37 +23,27 @@ public function getConfig($key, $default = null) */ public function create(int $width, int $height): ImageInterface { - return $this->resolve('ImageFactory')->newImage($width, $height); + return $this->resolveDriverClass('ImageFactory')->newImage($width, $height); } /** - * Create new image instance from input + * Create new image instance from source * - * @param mixed $input + * @param mixed $source * @return ImageInterface */ - public function make($input): ImageInterface + public function make($source): ImageInterface { - return $this->resolve('InputHandler')->handle($input); + return $this->resolveDriverClass('InputHandler')->handle($source); } /** - * Resolve given classname according to current configuration + * Return id of current driver * - * @param string $classname - * @param array $arguments - * @return mixed + * @return string */ - private function resolve(string $classname, ...$arguments) + protected function getCurrentDriver(): string { - $classname = sprintf( - "Intervention\\Image\\Drivers\\%s\\%s", - ucfirst($this->config['driver']), - $classname - ); - - $reflection = new ReflectionClass($classname); - - return $reflection->newInstanceArgs($arguments); + return strtolower($this->driver); } } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 56b5ec010..7fc9e7500 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -4,17 +4,42 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; +use Intervention\Image\Interfaces\FrameInterface; +use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\SizeInterface; interface ImageInterface { + public function getIterator(): Collection; + public function getFrames(): Collection; + public function getFrame(int $key = 0): ?FrameInterface; + public function addFrame(FrameInterface $frame): ImageInterface; + public function setLoops(int $count): ImageInterface; + public function getLoops(): int; public function getSize(): SizeInterface; - public function width(): int; - public function height(): int; public function isAnimated(): bool; - public function greyscale(): ImageInterface; + public function modify(ModifierInterface $modifier): 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 toJpeg(int $quality = 75): EncodedImage; + public function toGif(): EncodedImage; + public function toPng(): EncodedImage; public function pickColors(int $x, int $y): Collection; + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + public function greyscale(): ImageInterface; + public function blur(int $amount = 5): ImageInterface; + public function rotate(float $angle, $background = 'ffffff'): ImageInterface; + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface; + public function fill($color, ?int $x = null, ?int $y = null): ImageInterface; + public function pixelate(int $size): ImageInterface; + public function resize(?int $width = null, ?int $height = null): ImageInterface; + public function resizeDown(?int $width = null, ?int $height = null): ImageInterface; + public function scale(?int $width = null, ?int $height = null): ImageInterface; + public function scaleDown(?int $width = null, ?int $height = null): ImageInterface; + public function fit(int $width, int $height, string $position = 'center'): ImageInterface; + public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface; + public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + public function getWidth(): int; + public function getHeight(): int; + public function destroy(): void; } diff --git a/src/Traits/CanBuildNewImage.php b/src/Traits/CanBuildNewImage.php new file mode 100644 index 000000000..fe67f5d2e --- /dev/null +++ b/src/Traits/CanBuildNewImage.php @@ -0,0 +1,15 @@ +resolveDriverClass('ImageFactory'); + } +} diff --git a/src/Traits/CanResolveDriverClass.php b/src/Traits/CanResolveDriverClass.php index c7447e4f8..bf86e53af 100644 --- a/src/Traits/CanResolveDriverClass.php +++ b/src/Traits/CanResolveDriverClass.php @@ -2,9 +2,10 @@ namespace Intervention\Image\Traits; +use Intervention\Image\Exceptions\MissingDriverComponentException; +use Intervention\Image\Exceptions\RuntimeException; use ReflectionClass; use ReflectionException; -use Intervention\Image\Exceptions\RuntimeException; trait CanResolveDriverClass { @@ -17,24 +18,25 @@ trait CanResolveDriverClass */ protected function resolveDriverClass(string $classname, ...$arguments) { + $driver_id = $this->getCurrentDriver(); $classname = sprintf( "Intervention\\Image\\Drivers\\%s\\%s", - ucfirst($this->getCurrentDriver()), + ucfirst($driver_id), $classname ); try { $reflection = new ReflectionClass($classname); } catch (ReflectionException $e) { - throw new RuntimeException( - 'Class (' . $classname . ') could not be resolved for current driver.' + throw new MissingDriverComponentException( + 'Class (' . $classname . ') could not be resolved with driver ' . ucfirst($driver_id) . '.' ); } return $reflection->newInstanceArgs($arguments); } - protected function getCurrentDriver() + protected function getCurrentDriver(): string { $pattern = '/Intervention\\\Image\\\Drivers\\\(?P[A-Za-z]+)/'; preg_match($pattern, get_class($this), $matches); diff --git a/tests/AbstractImageTest.php b/tests/AbstractImageTest.php new file mode 100644 index 000000000..ab2c50918 --- /dev/null +++ b/tests/AbstractImageTest.php @@ -0,0 +1,240 @@ +shouldReceive('ident')->andReturn(1); + $frame2 = Mockery::mock(FrameInterface::class); + $frame2->shouldReceive('ident')->andReturn(2); + $frame3 = Mockery::mock(FrameInterface::class); + $frame3->shouldReceive('ident')->andReturn(3); + + $collection = new Collection([$frame1, $frame2, $frame3]); + + $mock = Mockery::mock(AbstractImage::class, ImageInterface::class, [$collection]) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $mock->shouldReceive('getWidth')->andReturn(300); + $mock->shouldReceive('getHeight')->andReturn(200); + + return $mock; + } + + public function testGetIterator(): void + { + $this->assertInstanceOf(Collection::class, $this->abstractImageMock()->getIterator()); + } + + public function testGetFrames(): void + { + $this->assertInstanceOf(Collection::class, $this->abstractImageMock()->getFrames()); + } + + public function testGetFrame(): void + { + $img = $this->abstractImageMock(); + + $this->assertInstanceOf(FrameInterface::class, $img->getFrame()); + $this->assertEquals(1, $img->getFrame()->ident()); + + $this->assertInstanceOf(FrameInterface::class, $img->getFrame(1)); + $this->assertEquals(2, $img->getFrame(1)->ident()); + + $this->assertInstanceOf(FrameInterface::class, $img->getFrame(2)); + $this->assertEquals(3, $img->getFrame(2)->ident()); + } + + public function testAddFrame(): void + { + $img = $this->abstractImageMock(); + $this->assertEquals(3, $img->getFrames()->count()); + $result = $img->addFrame(Mockery::mock(FrameInterface::class)); + $this->assertInstanceOf(AbstractImage::class, $result); + $this->assertEquals(4, $img->getFrames()->count()); + } + + public function testSetGetLoops(): void + { + $img = $this->abstractImageMock(); + $this->assertEquals(0, $img->getLoops()); + $result = $img->setLoops(10); + $this->assertEquals(10, $img->getLoops()); + $this->assertInstanceOf(AbstractImage::class, $result); + } + + public function testGetSize(): void + { + $img = $this->abstractImageMock(); + $this->assertInstanceOf(Size::class, $img->getSize()); + $this->assertEquals(300, $img->getSize()->getWidth()); + $this->assertEquals(200, $img->getSize()->getHeight()); + } + + public function testIsAnimated(): void + { + $img = Mockery::mock(AbstractImage::class, [new Collection()])->makePartial(); + $this->assertFalse($img->isAnimated()); + + $collection = new Collection([ + Mockery::mock(FrameInterface::class), + Mockery::mock(FrameInterface::class), + ]); + $img = Mockery::mock(AbstractImage::class, [$collection])->makePartial(); + $this->assertTrue($img->isAnimated()); + } + + public function testModify(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + $result = $img->modify($modifier); + $this->assertInstanceOf(ImageInterface::class, $img); + } + + public function testEncode(): void + { + $img = $this->abstractImageMock(); + + $encoder = Mockery::mock(EncoderInterface::class); + $encoded = Mockery::mock(EncodedImage::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + $result = $img->encode($encoder); + $this->assertInstanceOf(ImageInterface::class, $img); + } + + public function testToJpeg(): void + { + $img = $this->abstractImageMock(); + + $encoded = Mockery::mock(EncodedImage::class); + $encoder = Mockery::mock(EncoderInterface::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + + $img->shouldReceive('resolveDriverClass') + ->with('Encoders\JpegEncoder', 45) + ->andReturn($encoder); + + $result = $img->toJpeg(45); + $this->assertInstanceOf(EncodedImage::class, $result); + } + + public function testToGif(): void + { + $img = $this->abstractImageMock(); + + $encoded = Mockery::mock(EncodedImage::class); + $encoder = Mockery::mock(EncoderInterface::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + + $img->shouldReceive('resolveDriverClass') + ->with('Encoders\GifEncoder') + ->andReturn($encoder); + + $result = $img->toGif(); + $this->assertInstanceOf(EncodedImage::class, $result); + } + + public function testToPng(): void + { + $img = $this->abstractImageMock(); + + $encoded = Mockery::mock(EncodedImage::class); + $encoder = Mockery::mock(EncoderInterface::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + + $img->shouldReceive('resolveDriverClass') + ->with('Encoders\PngEncoder') + ->andReturn($encoder); + + $result = $img->toPng(); + $this->assertInstanceOf(EncodedImage::class, $result); + } + + public function testGreyscale(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\GreyscaleModifier') + ->andReturn($modifier); + + $result = $img->greyscale(); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testBlur(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\BlurModifier', 3) + ->andReturn($modifier); + + $result = $img->blur(3); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testRotate(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\RotateModifier', 3, 'cccccc') + ->andReturn($modifier); + + $result = $img->rotate(3, 'cccccc'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPlace(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\PlaceModifier', 'el', 'top-left', 0, 0) + ->andReturn($modifier); + + $result = $img->place('el', 'top-left', 0, 0); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPickColors(): void + { + $color = Mockery::mock(ColorInterface::class); + $img = $this->abstractImageMock(); + $img->shouldReceive('pickColor')->times(3)->andReturn($color); + $result = $img->pickColors(1, 2); + $this->assertInstanceOf(Collection::class, $result); + } +} diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index f02d18439..7a22ce27f 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -80,6 +80,7 @@ public function testGet() $this->assertEquals('bar', $collection->get(1)); $this->assertEquals('baz', $collection->get(2)); $this->assertNull($collection->get(3)); + $this->assertEquals('test', $collection->get(3, 'test')); } public function testToArray() @@ -99,4 +100,27 @@ public function testMap(): void $this->assertEquals(['FOO', 'BAR', 'BAZ'], $collection->toArray()); $this->assertEquals(['foo', 'bar', 'baz'], $mapped->toArray()); } + + public function testQuery(): void + { + $collection = new Collection([ + 'foo' => 'FOO', + 'bar' => 'BAR', + 'baz' => [ + 'test1' => '1', + 'test2' => '2', + 'test3' => [ + 'example' => 'value' + ] + ] + ]); + + $this->assertEquals('FOO', $collection->query('foo')); + $this->assertEquals('BAR', $collection->query('bar')); + $this->assertEquals('1', $collection->query('baz.test1')); + $this->assertEquals('2', $collection->query('baz.test2')); + $this->assertEquals('value', $collection->query('baz.test3.example')); + $this->assertEquals('value', $collection->query('baz.test3.example', 'default')); + $this->assertEquals('default', $collection->query('baz.test3.no', 'default')); + } } diff --git a/tests/Drivers/Abstract/AbstractColorTest.php b/tests/Drivers/Abstract/AbstractColorTest.php new file mode 100644 index 000000000..4db7614ef --- /dev/null +++ b/tests/Drivers/Abstract/AbstractColorTest.php @@ -0,0 +1,36 @@ +makePartial(); + $color->shouldReceive('red')->andReturn(255); + $color->shouldReceive('green')->andReturn(0); + $color->shouldReceive('blue')->andReturn(0); + + $this->assertEquals('ff0000', $color->toHex()); + $this->assertEquals('#ff0000', $color->toHex('#')); + } + + public function testIsGreyscale(): void + { + $color = Mockery::mock(AbstractColor::class)->makePartial(); + $color->shouldReceive('red')->andReturn(255); + $color->shouldReceive('green')->andReturn(0); + $color->shouldReceive('blue')->andReturn(0); + $this->assertFalse($color->isGreyscale()); + + $color = Mockery::mock(AbstractColor::class)->makePartial(); + $color->shouldReceive('red')->andReturn(100); + $color->shouldReceive('green')->andReturn(100); + $color->shouldReceive('blue')->andReturn(100); + $this->assertTrue($color->isGreyscale()); + } +} diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index 3ccb4f4a8..42b3b20e8 100644 --- a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -13,8 +13,8 @@ public function testDecodePng(): void $decoder = new BinaryImageDecoder(); $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); $this->assertInstanceOf(Image::class, $image); - $this->assertEquals(16, $image->width()); - $this->assertEquals(16, $image->height()); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); $this->assertCount(1, $image); } @@ -23,8 +23,8 @@ public function testDecodeGif(): void $decoder = new BinaryImageDecoder(); $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); $this->assertInstanceOf(Image::class, $image); - $this->assertEquals(16, $image->width()); - $this->assertEquals(16, $image->height()); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); $this->assertCount(1, $image); } @@ -33,8 +33,8 @@ public function testDecodeAnimatedGif(): void $decoder = new BinaryImageDecoder(); $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); $this->assertInstanceOf(Image::class, $image); - $this->assertEquals(75, $image->width()); - $this->assertEquals(50, $image->height()); + $this->assertEquals(75, $image->getWidth()); + $this->assertEquals(50, $image->getHeight()); $this->assertCount(4, $image); } } diff --git a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php new file mode 100644 index 000000000..573aa4a77 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php @@ -0,0 +1,20 @@ +decode($this->createTestImage('blue.gif')); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php new file mode 100644 index 000000000..19c2a26f1 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -0,0 +1,32 @@ +decode('rgb(181, 55, 23)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(1, $color->alpha()); + } + + public function testDecodeRgba(): void + { + $decoder = new RgbStringColorDecoder(); + $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(.5, $color->alpha()); + } +} diff --git a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php index 17af9db1b..231f0aab3 100644 --- a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php @@ -22,8 +22,8 @@ protected function getTestImage(): Image public function testEncode(): void { $image = $this->getTestImage(); - $encoder = new JpegEncoder(); + $encoder = new JpegEncoder(75); $result = $encoder->encode($image); - $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg)); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg())); } -} \ No newline at end of file +} diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 20bc9abd8..9f3197414 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; +use GdImage; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; @@ -14,4 +15,11 @@ public function testNewImage(): void $image = $factory->newImage(3, 2); $this->assertInstanceOf(Image::class, $image); } + + public function testNewCore(): void + { + $factory = new ImageFactory(); + $core = $factory->newCore(3, 2); + $this->assertInstanceOf(GdImage::class, $core); + } } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 6553bd032..1916587ba 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -62,9 +62,9 @@ public function testAddFrame(): void public function testSetGetLoops(): void { - $this->assertEquals(0, $this->image->loops()); + $this->assertEquals(0, $this->image->getLoops()); $result = $this->image->setLoops(12); - $this->assertEquals(12, $this->image->loops()); + $this->assertEquals(12, $this->image->getLoops()); $this->assertInstanceOf(Image::class, $result); } @@ -75,12 +75,12 @@ public function testIsAnimated(): void public function testWidth(): void { - $this->assertEquals(3, $this->image->width()); + $this->assertEquals(3, $this->image->getWidth()); } public function testHeight(): void { - $this->assertEquals(2, $this->image->height()); + $this->assertEquals(2, $this->image->getHeight()); } public function testGetSize(): void diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php new file mode 100644 index 000000000..e0e410250 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new BrightnessModifier(30)); + $this->assertEquals('4cfaff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php new file mode 100644 index 000000000..7ec44484c --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new ContrastModifier(30)); + $this->assertEquals('00ceff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php new file mode 100644 index 000000000..52b48fc1d --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertInstanceOf(GdImage::class, $image->getFrame()->getCore()); + $image->modify(new DestroyModifier()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php new file mode 100644 index 000000000..a1f8c049a --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -0,0 +1,34 @@ +createTestImage('blocks.png'); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $image->modify(new FillModifier(new Color(13421772), new Point(540, 400))); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + } + + public function testFillAllColor(): void + { + $image = $this->createTestImage('blocks.png'); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $image->modify(new FillModifier(new Color(13421772))); + $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php new file mode 100644 index 000000000..890560421 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -0,0 +1,29 @@ +createTestImage('blocks.png'); + $this->assertEquals(640, $image->getWidth()); + $this->assertEquals(480, $image->getHeight()); + $image->modify(new FitModifier(100, 100, 'center')); + $this->assertEquals(100, $image->getWidth()); + $this->assertEquals(100, $image->getHeight()); + $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertTransparency($image->pickColor(90, 30)); + } +} diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php new file mode 100644 index 000000000..6429f3fbd --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -0,0 +1,23 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); + $image->modify(new InvertModifier()); + $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php new file mode 100644 index 000000000..0fc4a892f --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -0,0 +1,23 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new PixelateModifier(10)); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('6aaa8b', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index f6bbb016e..b1ebe5708 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -12,13 +12,17 @@ class ResizeModifierTest extends TestCase { use CanCreateGdTestImage; - public function testColorChange(): void + public function testModify(): void { - $image = $this->createTestImage('trim.png'); - $this->assertEquals(50, $image->width()); - $this->assertEquals(50, $image->height()); - $image->modify(new ResizeModifier(new Size(300, 100))); - $this->assertEquals(300, $image->width()); - $this->assertEquals(100, $image->height()); + $image = $this->createTestImage('blocks.png'); + $this->assertEquals(640, $image->getWidth()); + $this->assertEquals(480, $image->getHeight()); + $image->modify(new ResizeModifier(200, 100)); + $this->assertEquals(200, $image->getWidth()); + $this->assertEquals(100, $image->getHeight()); + $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); + $this->assertTransparency($image->pickColor(150, 45)); } } diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php new file mode 100644 index 000000000..83d52090b --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); + $image->modify(new SharpenModifier(10)); + $this->assertEquals('4daba7', $image->pickColor(15, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php new file mode 100644 index 000000000..1f3ad8ec9 --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php @@ -0,0 +1,20 @@ +decode($this->createTestImage('blue.gif')); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php new file mode 100644 index 000000000..07f8e652b --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php @@ -0,0 +1,32 @@ +decode('rgb(181, 55, 23)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(1, $color->alpha()); + } + + public function testDecodeRgba(): void + { + $decoder = new RgbStringColorDecoder(); + $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(181, $color->red()); + $this->assertEquals(55, $color->green()); + $this->assertEquals(23, $color->blue()); + $this->assertEquals(.5, $color->alpha()); + } +} diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php index 7a01f54a7..6c6f09671 100644 --- a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -26,8 +26,8 @@ protected function getTestImage(): Image public function testEncode(): void { $image = $this->getTestImage(); - $encoder = new JpegEncoder(); + $encoder = new JpegEncoder(75); $result = $encoder->encode($image); - $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg)); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageJpeg())); } -} \ No newline at end of file +} diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php new file mode 100644 index 000000000..d23cb4437 --- /dev/null +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -0,0 +1,25 @@ +newImage(3, 2); + $this->assertInstanceOf(Image::class, $image); + } + + public function testNewCore(): void + { + $factory = new ImageFactory(); + $core = $factory->newCore(3, 2); + $this->assertInstanceOf(Imagick::class, $core); + } +} diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index a70666dca..17c7d706b 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -36,12 +36,12 @@ public function testIterator(): void public function testWidth(): void { - $this->assertEquals(3, $this->image->width()); + $this->assertEquals(3, $this->image->getWidth()); } public function testHeight(): void { - $this->assertEquals(2, $this->image->height()); + $this->assertEquals(2, $this->image->getHeight()); } public function testGetSize(): void diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php new file mode 100644 index 000000000..f0e3dfc75 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new BrightnessModifier(30)); + $this->assertEquals('39c9ff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php new file mode 100644 index 000000000..042c3f088 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new ContrastModifier(30)); + $this->assertEquals('00fcff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php new file mode 100644 index 000000000..6b9d3efe6 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertInstanceOf(Imagick::class, $image->getFrame()->getCore()); + $image->modify(new DestroyModifier()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php new file mode 100644 index 000000000..4c5a8d154 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -0,0 +1,36 @@ +createTestImage('blocks.png'); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')), new Point(540, 400))); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + } + + public function testFillAllColor(): void + { + $image = $this->createTestImage('blocks.png'); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')))); + $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php new file mode 100644 index 000000000..aeb4ef314 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -0,0 +1,29 @@ +createTestImage('blocks.png'); + $this->assertEquals(640, $image->getWidth()); + $this->assertEquals(480, $image->getHeight()); + $image->modify(new FitModifier(100, 100, 'center')); + $this->assertEquals(100, $image->getWidth()); + $this->assertEquals(100, $image->getHeight()); + $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertTransparency($image->pickColor(90, 30)); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php new file mode 100644 index 000000000..c167542c4 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -0,0 +1,23 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); + $image->modify(new InvertModifier()); + $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php new file mode 100644 index 000000000..d0d9d9931 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -0,0 +1,23 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new PixelateModifier(10)); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('6bab8c', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 488ba23b6..7f2bb2011 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Drivers\Imagick\Modifiers\CropResizeModifier; +use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; @@ -12,13 +12,17 @@ class CropResizeModifierTest extends TestCase { use CanCreateImagickTestImage; - public function testColorChange(): void + public function testModify(): void { - $image = $this->createTestImage('trim.png'); - $this->assertEquals(50, $image->width()); - $this->assertEquals(50, $image->height()); - $image->modify(new CropResizeModifier(new Size(50, 50), new Size(30, 20))); - $this->assertEquals(30, $image->width()); - $this->assertEquals(20, $image->height()); + $image = $this->createTestImage('blocks.png'); + $this->assertEquals(640, $image->getWidth()); + $this->assertEquals(480, $image->getHeight()); + $image->modify(new ResizeModifier(200, 100)); + $this->assertEquals(200, $image->getWidth()); + $this->assertEquals(100, $image->getHeight()); + $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); + $this->assertTransparency($image->pickColor(150, 45)); } } diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php new file mode 100644 index 000000000..f2e23bd1f --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); + $image->modify(new SharpenModifier(10)); + $this->assertEquals('4faca6', $image->pickColor(15, 14)->toHex()); + } +} diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index fb7b37e6e..9397d44d8 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -11,81 +11,31 @@ class ResizerTest extends TestCase { public function testMake(): void { - $result = Resizer::make(); - $this->assertInstanceOf(Resizer::class, $result); - } - - public function testSetTargetWidth(): void - { - $resizer = new Resizer(); - $result = $resizer->width(100); - $this->assertInstanceOf(Resizer::class, $result); - $result = $resizer->toWidth(100); - $this->assertInstanceOf(Resizer::class, $result); - } - - public function testSetTargetHeight(): void - { - $resizer = new Resizer(); - $result = $resizer->height(100); - $this->assertInstanceOf(Resizer::class, $result); - $result = $resizer->toHeight(100); - $this->assertInstanceOf(Resizer::class, $result); - } - - public function testSetTargetSizeByArray() - { - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([800, 600]); + $resizer = Resizer::to(); $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(800, $resizer->resize($size)->getWidth()); - $this->assertEquals(600, $resizer->resize($size)->getHeight()); - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([800]); + $resizer = Resizer::to(height: 100); $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(800, $resizer->resize($size)->getWidth()); - $this->assertEquals(200, $resizer->resize($size)->getHeight()); - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([function ($size) { - $size->width(80); - $size->height(40); - }]); + $resizer = Resizer::to(100); $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(80, $resizer->resize($size)->getWidth()); - $this->assertEquals(40, $resizer->resize($size)->getHeight()); - $size = new Size(300, 200); - $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([function ($size) { - $size->width(80); - }]); + $resizer = Resizer::to(100, 100); $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(80, $resizer->resize($size)->getWidth()); - $this->assertEquals(200, $resizer->resize($size)->getHeight()); + } - $size = new Size(300, 200); + public function testToWidth(): void + { $resizer = new Resizer(); - $resizer = $resizer->setTargetSizeByArray([function ($size) { - $size->height(10); - }]); - $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(300, $resizer->resize($size)->getWidth()); - $this->assertEquals(10, $resizer->resize($size)->getHeight()); + $result = $resizer->toWidth(100); + $this->assertInstanceOf(Resizer::class, $result); } - public function testSetTargetSize(): void + public function testToHeight(): void { - $size = new Size(300, 200); $resizer = new Resizer(); - $resizer = $resizer->setTargetSize(new Size(200, 100)); - $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(200, $resizer->resize($size)->getWidth()); - $this->assertEquals(100, $resizer->resize($size)->getHeight()); + $result = $resizer->toHeight(100); + $this->assertInstanceOf(Resizer::class, $result); } public function testToSize(): void @@ -94,23 +44,33 @@ public function testToSize(): void $resizer = new Resizer(); $resizer = $resizer->toSize(new Size(200, 100)); $this->assertInstanceOf(Resizer::class, $resizer); - $this->assertEquals(200, $resizer->resize($size)->getWidth()); - $this->assertEquals(100, $resizer->resize($size)->getHeight()); } public function testResize() { $size = new Size(300, 200); $resizer = new Resizer(); - $resizer->width(150); + $resizer->toWidth(150); $result = $resizer->resize($size); $this->assertEquals(150, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); $size = new Size(300, 200); $resizer = new Resizer(); - $resizer->width(20); - $resizer->height(10); + $resizer->toWidth(20); + $resizer->toHeight(10); + $result = $resizer->resize($size); + $this->assertEquals(20, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); + + $size = new Size(300, 200); + $resizer = new Resizer(width: 150); + $result = $resizer->resize($size); + $this->assertEquals(150, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); + + $size = new Size(300, 200); + $resizer = new Resizer(height: 10, width: 20); $result = $resizer->resize($size); $this->assertEquals(20, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); @@ -121,8 +81,8 @@ public function testResizeDown() // 800x600 > 1000x2000 = 800x600 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(2000); + $resizer->toWidth(1000); + $resizer->toHeight(2000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); @@ -130,8 +90,8 @@ public function testResizeDown() // 800x600 > 400x1000 = 400x600 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(1000); + $resizer->toWidth(400); + $resizer->toHeight(1000); $result = $resizer->resizeDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); @@ -139,8 +99,8 @@ public function testResizeDown() // 800x600 > 1000x400 = 800x400 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(400); + $resizer->toWidth(1000); + $resizer->toHeight(400); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(400, $result->getHeight()); @@ -148,8 +108,8 @@ public function testResizeDown() // 800x600 > 400x300 = 400x300 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(300); + $resizer->toWidth(400); + $resizer->toHeight(300); $result = $resizer->resizeDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); @@ -157,7 +117,7 @@ public function testResizeDown() // 800x600 > 1000xnull = 800x600 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); + $resizer->toWidth(1000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); @@ -165,7 +125,7 @@ public function testResizeDown() // 800x600 > nullx1000 = 800x600 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(1000); + $resizer->toHeight(1000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); @@ -176,8 +136,8 @@ public function testScale() // 800x600 > 1000x2000 = 1000x750 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(2000); + $resizer->toWidth(1000); + $resizer->toHeight(2000); $result = $resizer->scale($size); $this->assertEquals(1000, $result->getWidth()); $this->assertEquals(750, $result->getHeight()); @@ -185,8 +145,8 @@ public function testScale() // 800x600 > 2000x1000 = 1333x1000 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(2000); - $resizer->height(1000); + $resizer->toWidth(2000); + $resizer->toHeight(1000); $result = $resizer->scale($size); $this->assertEquals(1333, $result->getWidth()); $this->assertEquals(1000, $result->getHeight()); @@ -194,7 +154,7 @@ public function testScale() // // 800x600 > nullx3000 = 4000x3000 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(3000); + $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(3000, $result->getHeight()); @@ -202,7 +162,7 @@ public function testScale() // // 800x600 > 8000xnull = 8000x6000 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(8000); + $resizer->toWidth(8000); $result = $resizer->scale($size); $this->assertEquals(8000, $result->getWidth()); $this->assertEquals(6000, $result->getHeight()); @@ -210,8 +170,8 @@ public function testScale() // // 800x600 > 100x400 = 100x75 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(100); - $resizer->height(400); + $resizer->toWidth(100); + $resizer->toHeight(400); $result = $resizer->scale($size); $this->assertEquals(100, $result->getWidth()); $this->assertEquals(75, $result->getHeight()); @@ -219,8 +179,8 @@ public function testScale() // // 800x600 > 400x100 = 133x100 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(100); + $resizer->toWidth(400); + $resizer->toHeight(100); $result = $resizer->scale($size); $this->assertEquals(133, $result->getWidth()); $this->assertEquals(100, $result->getHeight()); @@ -228,7 +188,7 @@ public function testScale() // // 800x600 > nullx300 = 400x300 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(300); + $resizer->toHeight(300); $result = $resizer->scale($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); @@ -236,7 +196,7 @@ public function testScale() // // 800x600 > 80xnull = 80x60 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(80); + $resizer->toWidth(80); $result = $resizer->scale($size); $this->assertEquals(80, $result->getWidth()); $this->assertEquals(60, $result->getHeight()); @@ -244,7 +204,7 @@ public function testScale() // // 640x480 > 225xnull = 225x169 $size = new Size(640, 480); $resizer = new Resizer(); - $resizer->width(225); + $resizer->toWidth(225); $result = $resizer->scale($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(169, $result->getHeight()); @@ -252,7 +212,7 @@ public function testScale() // // 640x480 > 223xnull = 223x167 $size = new Size(640, 480); $resizer = new Resizer(); - $resizer->width(223); + $resizer->toWidth(223); $result = $resizer->scale($size); $this->assertEquals(223, $result->getWidth()); $this->assertEquals(167, $result->getHeight()); @@ -260,8 +220,8 @@ public function testScale() // // 600x800 > 300x300 = 225x300 $size = new Size(600, 800); $resizer = new Resizer(); - $resizer->width(300); - $resizer->height(300); + $resizer->toWidth(300); + $resizer->toHeight(300); $result = $resizer->scale($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); @@ -269,8 +229,8 @@ public function testScale() // // 800x600 > 400x10 = 13x10 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(10); + $resizer->toWidth(400); + $resizer->toHeight(10); $result = $resizer->scale($size); $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); @@ -278,32 +238,32 @@ public function testScale() // // 800x600 > 1000x1200 = 1000x750 $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(1200); + $resizer->toWidth(1000); + $resizer->toHeight(1200); $result = $resizer->scale($size); $this->assertEquals(1000, $result->getWidth()); $this->assertEquals(750, $result->getHeight()); $size = new Size(12000, 12); $resizer = new Resizer(); - $resizer->width(4000); - $resizer->height(3000); + $resizer->toWidth(4000); + $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(4, $result->getHeight()); $size = new Size(12, 12000); $resizer = new Resizer(); - $resizer->width(4000); - $resizer->height(3000); + $resizer->toWidth(4000); + $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(3, $result->getWidth()); $this->assertEquals(3000, $result->getHeight()); $size = new Size(12000, 6000); $resizer = new Resizer(); - $resizer->width(4000); - $resizer->height(3000); + $resizer->toWidth(4000); + $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(4000, $result->getWidth()); $this->assertEquals(2000, $result->getHeight()); @@ -313,91 +273,91 @@ public function testScaleDown() { $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(2000); + $resizer->toWidth(1000); + $resizer->toHeight(2000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(600); + $resizer->toWidth(1000); + $resizer->toHeight(600); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); - $resizer->height(300); + $resizer->toWidth(1000); + $resizer->toHeight(300); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(1000); + $resizer->toWidth(400); + $resizer->toHeight(1000); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); + $resizer->toWidth(400); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(300); + $resizer->toHeight(300); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(1000); + $resizer->toWidth(1000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->height(1000); + $resizer->toHeight(1000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(100); + $resizer->toWidth(100); $result = $resizer->scaleDown($size); $this->assertEquals(100, $result->getWidth()); $this->assertEquals(75, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(300); - $resizer->height(200); + $resizer->toWidth(300); + $resizer->toHeight(200); $result = $resizer->scaleDown($size); $this->assertEquals(267, $result->getWidth()); $this->assertEquals(200, $result->getHeight()); $size = new Size(600, 800); $resizer = new Resizer(); - $resizer->width(300); - $resizer->height(300); + $resizer->toWidth(300); + $resizer->toHeight(300); $result = $resizer->scaleDown($size); $this->assertEquals(225, $result->getWidth()); $this->assertEquals(300, $result->getHeight()); $size = new Size(800, 600); $resizer = new Resizer(); - $resizer->width(400); - $resizer->height(10); + $resizer->toWidth(400); + $resizer->toHeight(10); $result = $resizer->scaleDown($size); $this->assertEquals(13, $result->getWidth()); $this->assertEquals(10, $result->getHeight()); diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 0e0622a0d..6ae5f22c8 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -261,9 +261,7 @@ public function testResize(): void $this->assertInstanceOf(Size::class, $result); $size = new Size(300, 200); - $result = $size->resize(function ($resizer) { - $resizer->toWidth(100); - }); + $result = $size->resize(height: 100); $this->assertInstanceOf(Size::class, $result); } @@ -274,9 +272,7 @@ public function testResizeDown(): void $this->assertInstanceOf(Size::class, $result); $size = new Size(300, 200); - $result = $size->resizeDown(function ($resizer) { - $resizer->toWidth(100); - }); + $result = $size->resizeDown(height: 100); $this->assertInstanceOf(Size::class, $result); } @@ -287,9 +283,7 @@ public function testScale(): void $this->assertInstanceOf(Size::class, $result); $size = new Size(300, 200); - $result = $size->scale(function ($resizer) { - $resizer->toWidth(100); - }); + $result = $size->scale(height: 100); $this->assertInstanceOf(Size::class, $result); } @@ -300,9 +294,7 @@ public function testScaleDown(): void $this->assertInstanceOf(Size::class, $result); $size = new Size(300, 200); - $result = $size->scaleDown(function ($resizer) { - $resizer->toWidth(100); - }); + $result = $size->scaleDown(height: 100); $this->assertInstanceOf(Size::class, $result); } @@ -311,12 +303,6 @@ public function testCover(): void $size = new Size(300, 200); $result = $size->cover(120, 150); $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->cover(function ($resizer) { - $resizer->toWidth(100); - }); - $this->assertInstanceOf(Size::class, $result); } public function testContain(): void @@ -326,11 +312,5 @@ public function testContain(): void $this->assertInstanceOf(Size::class, $result); $this->assertEquals(600, $result->getWidth()); $this->assertEquals(600, $result->getHeight()); - - $size = new Size(300, 200); - $result = $size->contain(function ($resizer) { - $resizer->toWidth(100); - }); - $this->assertInstanceOf(Size::class, $result); } } diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index 2bdd2c6e0..d05fb12ee 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -9,37 +9,34 @@ class ImageManagerTest extends TestCase { public function testConstructor() { - $manager = new ImageManager(['foo' => 'bar']); + $manager = new ImageManager('foo'); $this->assertInstanceOf(ImageManager::class, $manager); - $this->assertEquals('gd', $manager->getConfig('driver')); - $this->assertEquals('bar', $manager->getConfig('foo')); } - public function testConfigure() + public function testCreateGd() { - $manager = new ImageManager(['foo' => 'bar']); - $manager->configure(['foo' => 'baz', 'driver' => 'foo']); - $this->assertEquals('foo', $manager->getConfig('driver')); - $this->assertEquals('baz', $manager->getConfig('foo')); + $manager = new ImageManager('gd'); + $image = $manager->create(5, 4); + $this->assertInstanceOf(ImageInterface::class, $image); } - public function testGetConfig() + public function testMakeGd() { - $manager = new ImageManager(['foo' => 'bar']); - $this->assertEquals('gd', $manager->getConfig('driver')); - $this->assertEquals('bar', $manager->getConfig('foo')); + $manager = new ImageManager('gd'); + $image = $manager->make(__DIR__ . '/images/red.gif'); + $this->assertInstanceOf(ImageInterface::class, $image); } - public function testCreateGd() + public function testCreateImagick() { - $manager = new ImageManager(['driver' => 'gd']); + $manager = new ImageManager('imagick'); $image = $manager->create(5, 4); $this->assertInstanceOf(ImageInterface::class, $image); } - public function testMakeGd() + public function testMakeImagick() { - $manager = new ImageManager(['driver' => 'gd']); + $manager = new ImageManager('imagick'); $image = $manager->make(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } diff --git a/tests/TestCase.php b/tests/TestCase.php index f229c084f..d9b53621b 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,14 +4,20 @@ use Intervention\Image\Interfaces\ColorInterface; use PHPUnit\Framework\TestCase as PHPUnitTestCase; +use Mockery\Adapter\Phpunit\MockeryTestCase; -abstract class TestCase extends PHPUnitTestCase +abstract class TestCase extends MockeryTestCase { protected function assertColor($r, $g, $b, $a, ColorInterface $color) { - $this->assertEquals($r, $color->getRgbRed()); - $this->assertEquals($g, $color->getRgbGreen()); - $this->assertEquals($b, $color->getRgbBlue()); - $this->assertEquals($a, $color->getOpacity()); + $this->assertEquals($r, $color->red()); + $this->assertEquals($g, $color->green()); + $this->assertEquals($b, $color->blue()); + $this->assertEquals($a, $color->alpha()); + } + + protected function assertTransparency(ColorInterface $color) + { + $this->assertEquals(0, $color->alpha()); } } diff --git a/tests/images/blocks.png b/tests/images/blocks.png new file mode 100644 index 0000000000000000000000000000000000000000..d57fabe3e4bb1b339bc7a6263082c9886614acff GIT binary patch literal 467 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&D3>5L$Z&D7V7>k44ofvPP)Tsw@I14-? ziy0WWg+Z8+Vb&Z8pde3xPlzi6!+#(XNC2q?LBfxLY?hKBzhI!i|H~VSrT`5J@N{tu zshIQjoFgxT0uRfEnMYLA91qv(MPAZ+;d8aKw7zL5f!j^nUmay(9XQ}D&ts^W|2#3p P0~GL{u6{1-oD!M Date: Sun, 19 Dec 2021 16:26:23 +0100 Subject: [PATCH 0224/1667] Added License file --- LICENSE | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..ca3cb7c2d --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2021 Oliver Vogel + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From d9aafc2b2997ec3cf8ff9d5358438f9a9d90110d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 16:34:23 +0100 Subject: [PATCH 0225/1667] Github workflows "Run tests" --- .github/workflows/run-tests.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/run-tests.yml diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 000000000..379dfa622 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,24 @@ +name: run-tests + +on: [push] + +jobs: + run: + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: ['8.0'] + name: Testing on PHP ${{ matrix.php-versions }} + steps: + - name: checkout + uses: actions/checkout@v2 + - name: setup + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: imagick,gd,mbstring,bcmath + tools: phpunit,composer + - name: install deps + run: composer install -o -q + - name: run phpunit + run: vendor/bin/phpunit \ No newline at end of file From c59d0100d732555a76356584077592ae8c4db6cf Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 16:37:08 +0100 Subject: [PATCH 0226/1667] CI --- .github/workflows/run-tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 379dfa622..412578989 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,15 +10,15 @@ jobs: php-versions: ['8.0'] name: Testing on PHP ${{ matrix.php-versions }} steps: - - name: checkout + - name: 'Checkout Project' uses: actions/checkout@v2 - - name: setup + - name: 'Setup Environment' uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} extensions: imagick,gd,mbstring,bcmath tools: phpunit,composer - - name: install deps + - name: 'Install Dependencies' run: composer install -o -q - - name: run phpunit + - name: 'Run PHPUnit' run: vendor/bin/phpunit \ No newline at end of file From 6752d3019e877e80948fde4b00bd7d664fc8e42b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 16:42:07 +0100 Subject: [PATCH 0227/1667] CI --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 412578989..ed1ea7a7a 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -4,7 +4,7 @@ on: [push] jobs: run: - runs-on: ubuntu-latest + runs-on: macos-latest strategy: matrix: php-versions: ['8.0'] From 9f9e9e16ab830dee1800a43b63c111fdf4dfa0eb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 16:45:00 +0100 Subject: [PATCH 0228/1667] Revert "CI" This reverts commit 6752d3019e877e80948fde4b00bd7d664fc8e42b. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ed1ea7a7a..412578989 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -4,7 +4,7 @@ on: [push] jobs: run: - runs-on: macos-latest + runs-on: ubuntu-latest strategy: matrix: php-versions: ['8.0'] From c070d7d7af7a29840dd00a3170de50310afcf08a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:23:02 +0100 Subject: [PATCH 0229/1667] Test --- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index b1ebe5708..7e7d297d7 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -23,6 +23,8 @@ public function testModify(): void $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $this->assertTransparency($image->pickColor(150, 45)); + $transparent = $image->pickColor(150, 45); + $this->assertTransparency($transparent); + $this->assertEquals(2130706432, $transparent->toInt()); } } From e6ef61ee02fae706f037cad34afef8d611519b8f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:28:08 +0100 Subject: [PATCH 0230/1667] Test --- .github/workflows/run-tests.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 412578989..070121410 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,13 +10,29 @@ jobs: php-versions: ['8.0'] name: Testing on PHP ${{ matrix.php-versions }} steps: + - name: Run PHP code + shell: php {0} + run: | + > 24) & 0xFF; + $r = ($value >> 16) & 0xFF; + $g = ($value >> 8) & 0xFF; + $b = $value & 0xFF; + $a1 = (float) round(1 - $a / 127, 2); + $a2 = round(1 - $a / 127, 2); + $a3 = 1 - $a / 127; + var_dump($a); + var_dump($a1); + var_dump($a2); + var_dump($a3); - name: 'Checkout Project' uses: actions/checkout@v2 - name: 'Setup Environment' uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} - extensions: imagick,gd,mbstring,bcmath + extensions: imagick,gd tools: phpunit,composer - name: 'Install Dependencies' run: composer install -o -q From b3e41c8c1b3341dcb3572d3a7fee2cc969a3edfc Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:29:13 +0100 Subject: [PATCH 0231/1667] Test --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 070121410..65c1c5040 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -14,7 +14,7 @@ jobs: shell: php {0} run: | > 24) & 0xFF; $r = ($value >> 16) & 0xFF; $g = ($value >> 8) & 0xFF; From 692b140528f09b951f9409b032896e5978f88b4c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:31:54 +0100 Subject: [PATCH 0232/1667] Test --- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index 7e7d297d7..d8b03872a 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -24,7 +24,7 @@ public function testModify(): void $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); $transparent = $image->pickColor(150, 45); - $this->assertTransparency($transparent); $this->assertEquals(2130706432, $transparent->toInt()); + $this->assertTransparency($transparent); } } From b3a041a3ee5e911e6327f050cd207c36bfb002e7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 19 Dec 2021 17:37:16 +0100 Subject: [PATCH 0233/1667] Test --- .github/workflows/run-tests.yml | 16 ---------------- .../Drivers/Gd/Modifiers/ResizeModifierTest.php | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 65c1c5040..5d7ae107f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,22 +10,6 @@ jobs: php-versions: ['8.0'] name: Testing on PHP ${{ matrix.php-versions }} steps: - - name: Run PHP code - shell: php {0} - run: | - > 24) & 0xFF; - $r = ($value >> 16) & 0xFF; - $g = ($value >> 8) & 0xFF; - $b = $value & 0xFF; - $a1 = (float) round(1 - $a / 127, 2); - $a2 = round(1 - $a / 127, 2); - $a3 = 1 - $a / 127; - var_dump($a); - var_dump($a1); - var_dump($a2); - var_dump($a3); - name: 'Checkout Project' uses: actions/checkout@v2 - name: 'Setup Environment' diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index d8b03872a..ad4a4a9c9 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -23,7 +23,7 @@ public function testModify(): void $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $transparent = $image->pickColor(150, 45); + $transparent = $image->pickColor(170, 30); $this->assertEquals(2130706432, $transparent->toInt()); $this->assertTransparency($transparent); } From f173895ac23fc04ebc783cbf391543c0a22aa75a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 20 Dec 2021 19:45:51 +0100 Subject: [PATCH 0234/1667] Replaced test image --- tests/images/blocks.png | Bin 467 -> 4338 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/images/blocks.png b/tests/images/blocks.png index d57fabe3e4bb1b339bc7a6263082c9886614acff..69ffc0f786162f5f35a07301d6187f13d868f216 100644 GIT binary patch literal 4338 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&A1{5*9c;^X_Vk{1FcVfJGQl}os;Vkfo zEM{Qf76xHPhFNnY7#IYmd%8G=RLpsM?P4dZp#by2llCw7a`@FdJqygzYB{)X_j~4s z`^jmEubEjJ*o?WOgW<=ij<-oY$;F&|$*tr;Z)5Nyf1u$e7Y~Jv-k;$QXX6ssh z5eBIJj$1Hzy85}Sb4q9e01?_Lg8%>k literal 467 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&D3>5L$Z&D7V7>k44ofvPP)Tsw@I14-? ziy0WWg+Z8+Vb&Z8pde3xPlzi6!+#(XNC2q?LBfxLY?hKBzhI!i|H~VSrT`5J@N{tu zshIQjoFgxT0uRfEnMYLA91qv(MPAZ+;d8aKw7zL5f!j^nUmay(9XQ}D&ts^W|2#3p P0~GL{u6{1-oD!M Date: Mon, 20 Dec 2021 19:47:55 +0100 Subject: [PATCH 0235/1667] Revert "Replaced test image" This reverts commit f173895ac23fc04ebc783cbf391543c0a22aa75a. --- tests/images/blocks.png | Bin 4338 -> 467 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/images/blocks.png b/tests/images/blocks.png index 69ffc0f786162f5f35a07301d6187f13d868f216..d57fabe3e4bb1b339bc7a6263082c9886614acff 100644 GIT binary patch literal 467 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&D3>5L$Z&D7V7>k44ofvPP)Tsw@I14-? ziy0WWg+Z8+Vb&Z8pde3xPlzi6!+#(XNC2q?LBfxLY?hKBzhI!i|H~VSrT`5J@N{tu zshIQjoFgxT0uRfEnMYLA91qv(MPAZ+;d8aKw7zL5f!j^nUmay(9XQ}D&ts^W|2#3p P0~GL{u6{1-oD!MgW<=ij<-oY$;F&|$*tr;Z)5Nyf1u$e7Y~Jv-k;$QXX6ssh z5eBIJj$1Hzy85}Sb4q9e01?_Lg8%>k From 43108ce1ce4fd12037b5e590feb15744d8fef7cb Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 11:28:44 +0100 Subject: [PATCH 0236/1667] GD & ImageMagick tests require extension to run --- tests/Drivers/Gd/ColorTest.php | 3 +++ tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/HexColorDecoderTest.php | 5 ++++- tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/GifEncoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/JpegEncoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/PngEncoderTest.php | 5 ++++- tests/Drivers/Gd/FrameTest.php | 3 +++ tests/Drivers/Gd/ImageFactoryTest.php | 3 +++ tests/Drivers/Gd/ImageTest.php | 3 +++ tests/Drivers/Gd/InputHandlerTest.php | 6 +++--- tests/Drivers/Gd/Modifiers/BlurModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/ContrastModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/DestroyModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/FillModifierTest.php | 3 +++ tests/Drivers/Gd/Modifiers/FitModifierTest.php | 6 +++--- tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php | 6 ++++-- tests/Drivers/Gd/Modifiers/InvertModifierTest.php | 6 ++++-- tests/Drivers/Gd/Modifiers/PixelateModifierTest.php | 4 +++- tests/Drivers/Gd/Modifiers/PlaceModifierTest.php | 5 +++-- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 5 +++-- tests/Drivers/Gd/Modifiers/SharpenModifierTest.php | 6 ++++-- tests/Drivers/Imagick/ColorTest.php | 3 +++ tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php | 3 +++ tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php | 3 +++ .../Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php | 3 +++ .../Imagick/Decoders/TransparentColorDecoderTest.php | 3 +++ tests/Drivers/Imagick/Encoders/GifEncoderTest.php | 3 +++ tests/Drivers/Imagick/Encoders/JpegEncoderTest.php | 3 +++ tests/Drivers/Imagick/FrameTest.php | 3 +++ tests/Drivers/Imagick/ImageFactoryTest.php | 3 +++ tests/Drivers/Imagick/ImageTest.php | 3 +++ tests/Drivers/Imagick/InputHandlerTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/BlurModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/FillModifierTest.php | 4 +++- tests/Drivers/Imagick/Modifiers/FitModifierTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/InvertModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php | 4 +++- tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php | 5 +++-- tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php | 7 ++++--- tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php | 6 ++++-- tests/ImageManagerTest.php | 4 ++++ 53 files changed, 173 insertions(+), 50 deletions(-) diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php index 989fad85c..767fbccc2 100644 --- a/tests/Drivers/Gd/ColorTest.php +++ b/tests/Drivers/Gd/ColorTest.php @@ -5,6 +5,9 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class ColorTest extends TestCase { protected function getTestColor($r = 0, $g = 0, $b = 0, $a = 0): Color diff --git a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php index 585d864ac..4750f87dd 100644 --- a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class ArrayColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php index ae92fed28..dcddfa537 100644 --- a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class Base64ImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index 42b3b20e8..b4e63620b 100644 --- a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class BinaryImageDecoderTest extends TestCase { public function testDecodePng(): void diff --git a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php index fccc146d5..cc31e4afc 100644 --- a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class DataUriImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php index 840063b2b..e0f86b66f 100644 --- a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class FilePathImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php index 377321171..c70a6db69 100644 --- a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php @@ -2,10 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class HexColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php index 573aa4a77..4ae717fa7 100644 --- a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class ImageObjectDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php index 19c2a26f1..db1a5bb9e 100644 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class RgbStringColorDecoderTest extends TestCase { public function testDecodeRgb(): void diff --git a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php index d0864d48a..29777036d 100644 --- a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Decoders\TransparentColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class TransparentColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Encoders/GifEncoderTest.php b/tests/Drivers/Gd/Encoders/GifEncoderTest.php index e668ce229..4a08a8dda 100644 --- a/tests/Drivers/Gd/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/GifEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; +/** + * @requires extension gd + */ class GifEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php index 231f0aab3..7e46e48d8 100644 --- a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageJpeg; +/** + * @requires extension gd + */ class JpegEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Gd/Encoders/PngEncoderTest.php b/tests/Drivers/Gd/Encoders/PngEncoderTest.php index 065aa4d63..97991d0e7 100644 --- a/tests/Drivers/Gd/Encoders/PngEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/PngEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImagePng; +/** + * @requires extension gd + */ class PngEncoderTest extends TestCase { protected function getTestImage(): Image @@ -26,4 +29,4 @@ public function testEncode(): void $result = $encoder->encode($image); $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImagePng)); } -} \ No newline at end of file +} diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index 2497139d1..ff862d4bb 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -8,6 +8,9 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class FrameTest extends TestCase { protected function getTestFrame(): Frame diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 9f3197414..f2283fce9 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class ImageFactoryTest extends TestCase { public function testNewImage(): void diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 1916587ba..74ea010c4 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -9,6 +9,9 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class ImageTest extends TestCase { protected Image $image; diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 22fa5dc88..e137b283a 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,15 +2,15 @@ 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\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension gd + */ class InputHandlerTest extends TestCase { public function testHandleEmptyString(): void diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php index 9062eda45..b1b70855f 100644 --- a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\BlurModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class BlurModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php index e0e410250..2a80ba6a3 100644 --- a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\BrightnessModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class BrightnessModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php index 7ec44484c..e95a81922 100644 --- a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\ContrastModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class ContrastModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php index 52b48fc1d..753f45aa0 100644 --- a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php @@ -3,11 +3,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; use GdImage; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\DestroyModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class DestroyModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index a1f8c049a..d3fb6205c 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -8,6 +8,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class FillModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index 890560421..8e02343b7 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -2,13 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\FitModifier; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class FitModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php index 9302d460c..c2c035f5c 100644 --- a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class GreyscaleModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php index 6429f3fbd..275422749 100644 --- a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\InvertModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class InvertModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php index 0fc4a892f..be9458040 100644 --- a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\PixelateModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class PixelateModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php index bd1d3e0b0..16a0f06ad 100644 --- a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -2,12 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\PlaceModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class PlaceModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index ad4a4a9c9..7094a1e70 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -2,12 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class ResizeModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php index 83d52090b..051ad0278 100644 --- a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\SharpenModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @requires extension gd + */ class SharpenModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php index bf889f0cc..52e8779ec 100644 --- a/tests/Drivers/Imagick/ColorTest.php +++ b/tests/Drivers/Imagick/ColorTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class ColorTest extends TestCase { protected function getTestColor(int $r = 0, int $g = 0, int $b = 0, float $a = 1): Color diff --git a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php index e0e16800e..c3c5e17ac 100644 --- a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class ArrayColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php index 1f3ad8ec9..175886ad1 100644 --- a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class ImageObjectDecoderTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php index 07f8e652b..f2ac61e7d 100644 --- a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class RgbStringColorDecoderTest extends TestCase { public function testDecodeRgb(): void diff --git a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php index 915842bd6..82dcf1889 100644 --- a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Decoders\TransparentColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class TransparentColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php index 9e51820ed..fd7f40e42 100644 --- a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php @@ -12,6 +12,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; +/** + * @requires extension imagick + */ class GifEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php index 6c6f09671..5e2d0eccd 100644 --- a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -12,6 +12,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageJpeg; +/** + * @requires extension imagick + */ class JpegEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index e57642766..659b4db6d 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -9,6 +9,9 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class FrameTest extends TestCase { protected function getTestFrame(): Frame diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php index d23cb4437..237d9c4d3 100644 --- a/tests/Drivers/Imagick/ImageFactoryTest.php +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Imagick\ImageFactory; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class ImageFactoryTest extends TestCase { public function testNewImage(): void diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 17c7d706b..572837dbe 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class ImageTest extends TestCase { protected Image $image; diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index 6bc689ba5..9d6659459 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,15 +2,15 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @requires extension imagick + */ class InputHandlerTest extends TestCase { public function testHandleEmptyString(): void diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php index a1ff3750f..e001c0f8e 100644 --- a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\BlurModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class BlurModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php index f0e3dfc75..a4def4c56 100644 --- a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\BrightnessModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class BrightnessModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php index 042c3f088..856ea3a8a 100644 --- a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\ContrastModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class ContrastModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php index 6b9d3efe6..ac61bd84f 100644 --- a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php @@ -3,11 +3,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use Imagick; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\DestroyModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class DestroyModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index 4c5a8d154..b1a74f27f 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -4,12 +4,14 @@ use ImagickPixel; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\FillModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class FillModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index aeb4ef314..a745da01b 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -2,13 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\FitModifier; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class FitModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php index 64b5be616..5af3dfe5e 100644 --- a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\GreyscaleModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class GreyscaleModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php index c167542c4..8c97aa463 100644 --- a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\InvertModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class InvertModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index d0d9d9931..cc4973ce3 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\PixelateModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class PixelateModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index e6b66dd5b..21f3619cd 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -2,12 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\PlaceModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class PlaceModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 7f2bb2011..c55d45758 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -2,13 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; -class CropResizeModifierTest extends TestCase +/** + * @requires extension imagick + */ +class ResizeModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php index f2e23bd1f..2f2dd8c95 100644 --- a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\SharpenModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @requires extension imagick + */ class SharpenModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index d05fb12ee..833b0ef0e 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -13,6 +13,7 @@ public function testConstructor() $this->assertInstanceOf(ImageManager::class, $manager); } + /** @requires extension gd */ public function testCreateGd() { $manager = new ImageManager('gd'); @@ -20,6 +21,7 @@ public function testCreateGd() $this->assertInstanceOf(ImageInterface::class, $image); } + /** @requires extension gd */ public function testMakeGd() { $manager = new ImageManager('gd'); @@ -27,6 +29,7 @@ public function testMakeGd() $this->assertInstanceOf(ImageInterface::class, $image); } + /** @requires extension imagick */ public function testCreateImagick() { $manager = new ImageManager('imagick'); @@ -34,6 +37,7 @@ public function testCreateImagick() $this->assertInstanceOf(ImageInterface::class, $image); } + /** @requires extension imagick */ public function testMakeImagick() { $manager = new ImageManager('imagick'); From 8ba4b91f56e831c620ef8d7fd55aaf354bd5323e Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 12:13:21 +0100 Subject: [PATCH 0237/1667] Webp encoders for GD & ImageMagick --- src/Drivers/Gd/Encoders/WebpEncoder.php | 25 ++++++++++++ src/Drivers/Imagick/Encoders/WebpEncoder.php | 36 ++++++++++++++++++ tests/Drivers/Gd/Encoders/WebpEncoderTest.php | 34 +++++++++++++++++ .../Imagick/Encoders/WebpEncoderTest.php | 38 +++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 src/Drivers/Gd/Encoders/WebpEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/WebpEncoder.php create mode 100644 tests/Drivers/Gd/Encoders/WebpEncoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/WebpEncoderTest.php diff --git a/src/Drivers/Gd/Encoders/WebpEncoder.php b/src/Drivers/Gd/Encoders/WebpEncoder.php new file mode 100644 index 000000000..748db2a2f --- /dev/null +++ b/src/Drivers/Gd/Encoders/WebpEncoder.php @@ -0,0 +1,25 @@ +quality = $quality; + } + + public function encode(ImageInterface $image): EncodedImage + { + $data = $this->getBuffered(function () use ($image) { + imagewebp($image->getFrames()->first()->getCore(), null, $this->quality); + }); + + return new EncodedImage($data, 'image/webp'); + } +} diff --git a/src/Drivers/Imagick/Encoders/WebpEncoder.php b/src/Drivers/Imagick/Encoders/WebpEncoder.php new file mode 100644 index 000000000..0f7cb9f92 --- /dev/null +++ b/src/Drivers/Imagick/Encoders/WebpEncoder.php @@ -0,0 +1,36 @@ +quality = $quality; + } + + public function encode(ImageInterface $image): EncodedImage + { + $format = 'webp'; + $compression = Imagick::COMPRESSION_ZIP; + + $imagick = $image->getFrames()->first()->getCore(); + $imagick->setImageBackgroundColor(new ImagickPixel('transparent')); + + $imagick = $imagick->mergeImageLayers(Imagick::LAYERMETHOD_MERGE); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick->setImageCompressionQuality($this->quality); + + return new EncodedImage($imagick->getImagesBlob(), 'image/webp'); + } +} diff --git a/tests/Drivers/Gd/Encoders/WebpEncoderTest.php b/tests/Drivers/Gd/Encoders/WebpEncoderTest.php new file mode 100644 index 000000000..994395bd7 --- /dev/null +++ b/tests/Drivers/Gd/Encoders/WebpEncoderTest.php @@ -0,0 +1,34 @@ +getTestImage(); + $encoder = new WebpEncoder(75); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString((string) $result)->matches(new ImageWebp())); + } +} diff --git a/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php b/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php new file mode 100644 index 000000000..dcced39bb --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php @@ -0,0 +1,38 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + $frame = new Frame($imagick); + + return new Image(new Collection([$frame])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new WebpEncoder(75); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString((string) $result)->matches(new ImageWebp())); + } +} From 4952544a525eed872a4c6cd4c641bb18fd773811 Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 12:13:48 +0100 Subject: [PATCH 0238/1667] ImageMagick PNG encoder test --- .../Imagick/Encoders/PngEncoderTest.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/Drivers/Imagick/Encoders/PngEncoderTest.php diff --git a/tests/Drivers/Imagick/Encoders/PngEncoderTest.php b/tests/Drivers/Imagick/Encoders/PngEncoderTest.php new file mode 100644 index 000000000..30b41fa8c --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/PngEncoderTest.php @@ -0,0 +1,38 @@ +newImage(3, 2, new ImagickPixel('red'), 'jpg'); + $frame = new Frame($imagick); + + return new Image(new Collection([$frame])); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new PngEncoder(75); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString((string) $result)->matches(new ImagePng())); + } +} From 270ec4bc5bbc3876289a6da06d184cab3cf518eb Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 12:14:27 +0100 Subject: [PATCH 0239/1667] Enable PHPUnit code coverage --- phpunit.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/phpunit.xml b/phpunit.xml index 422eeac61..63454441b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -14,4 +14,10 @@ ./tests/
+ + + + src + + From 66d744f5fb8868ff3cd7424e6fd0798321e73fe0 Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 12:41:59 +0100 Subject: [PATCH 0240/1667] Add toWebp() method to ImageInterface & AbstractImage --- src/Drivers/Abstract/AbstractImage.php | 9 +++++++-- src/Interfaces/ImageInterface.php | 4 +--- tests/AbstractImageTest.php | 21 ++++++++++++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 29ae8f9bf..4d11c373a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -4,9 +4,7 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; -use Intervention\Image\Exceptions\NotWritableException; use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; @@ -92,6 +90,13 @@ public function toJpeg(int $quality = 75): EncodedImage ); } + public function toWebp(int $quality = 75): EncodedImage + { + return $this->encode( + $this->resolveDriverClass('Encoders\WebpEncoder', $quality) + ); + } + public function toGif(): EncodedImage { return $this->encode( diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 7fc9e7500..182a76dc8 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -4,9 +4,6 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; -use Intervention\Image\Interfaces\FrameInterface; -use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\SizeInterface; interface ImageInterface { @@ -21,6 +18,7 @@ public function isAnimated(): bool; public function modify(ModifierInterface $modifier): ImageInterface; public function encode(EncoderInterface $encoder): EncodedImage; public function toJpeg(int $quality = 75): EncodedImage; + public function toWebp(int $quality = 75): EncodedImage; public function toGif(): EncodedImage; public function toPng(): EncodedImage; public function pickColors(int $x, int $y): Collection; diff --git a/tests/AbstractImageTest.php b/tests/AbstractImageTest.php index ab2c50918..167fd200d 100644 --- a/tests/AbstractImageTest.php +++ b/tests/AbstractImageTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Abstract\AbstractFrame; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Size; @@ -130,13 +129,29 @@ public function testToJpeg(): void $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); $img->shouldReceive('resolveDriverClass') - ->with('Encoders\JpegEncoder', 45) - ->andReturn($encoder); + ->with('Encoders\JpegEncoder', 45) + ->andReturn($encoder); $result = $img->toJpeg(45); $this->assertInstanceOf(EncodedImage::class, $result); } + public function testToWebp(): void + { + $img = $this->abstractImageMock(); + + $encoded = Mockery::mock(EncodedImage::class); + $encoder = Mockery::mock(EncoderInterface::class); + $encoder->shouldReceive('encode')->with($img)->andReturn($encoded); + + $img->shouldReceive('resolveDriverClass') + ->with('Encoders\WebpEncoder', 45) + ->andReturn($encoder); + + $result = $img->toWebp(45); + $this->assertInstanceOf(EncodedImage::class, $result); + } + public function testToGif(): void { $img = $this->abstractImageMock(); From 5dcd6e820f9f1249d4bfc9f4e9187cc73ebf0c8c Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Wed, 22 Dec 2021 14:34:28 +0100 Subject: [PATCH 0241/1667] Add @covers annotations to tests --- tests/AbstractImageTest.php | 4 +++- tests/CollectionTest.php | 3 +++ tests/Drivers/Abstract/AbstractColorTest.php | 5 ++++- tests/Drivers/Gd/ColorTest.php | 3 +++ tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/HexColorDecoderTest.php | 5 ++++- tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/GifEncoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/JpegEncoderTest.php | 3 +++ tests/Drivers/Gd/Encoders/PngEncoderTest.php | 5 ++++- tests/Drivers/Gd/FrameTest.php | 3 +++ tests/Drivers/Gd/ImageFactoryTest.php | 3 +++ tests/Drivers/Gd/ImageTest.php | 3 +++ tests/Drivers/Gd/InputHandlerTest.php | 6 +++--- tests/Drivers/Gd/Modifiers/BlurModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/ContrastModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/DestroyModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/FillModifierTest.php | 4 ++++ tests/Drivers/Gd/Modifiers/FitModifierTest.php | 7 ++++--- tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php | 7 +++++-- tests/Drivers/Gd/Modifiers/InvertModifierTest.php | 7 +++++-- tests/Drivers/Gd/Modifiers/PixelateModifierTest.php | 5 ++++- tests/Drivers/Gd/Modifiers/PlaceModifierTest.php | 6 ++++-- tests/Drivers/Gd/Modifiers/ResizeModifierTest.php | 6 ++++-- tests/Drivers/Gd/Modifiers/SharpenModifierTest.php | 7 +++++-- tests/Drivers/Imagick/ColorTest.php | 3 +++ tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php | 3 +++ tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php | 3 +++ .../Imagick/Decoders/RgbStringColorDecoderTest.php | 3 +++ .../Imagick/Decoders/TransparentColorDecoderTest.php | 3 +++ tests/Drivers/Imagick/Encoders/GifEncoderTest.php | 3 +++ tests/Drivers/Imagick/Encoders/JpegEncoderTest.php | 3 +++ tests/Drivers/Imagick/FrameTest.php | 3 +++ tests/Drivers/Imagick/ImageFactoryTest.php | 3 +++ tests/Drivers/Imagick/ImageTest.php | 3 +++ tests/Drivers/Imagick/InputHandlerTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/BlurModifierTest.php | 6 ++++-- .../Drivers/Imagick/Modifiers/BrightnessModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/FillModifierTest.php | 4 +++- tests/Drivers/Imagick/Modifiers/FitModifierTest.php | 6 +++--- tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/InvertModifierTest.php | 6 ++++-- tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php | 4 +++- tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php | 5 +++-- tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php | 7 ++++--- tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php | 6 ++++-- tests/EncodedImageTest.php | 3 +++ tests/Geometry/PointTest.php | 3 +++ tests/Geometry/ResizerTest.php | 3 +++ tests/Geometry/SizeTest.php | 8 ++++---- tests/ImageManagerTest.php | 3 +++ 60 files changed, 207 insertions(+), 56 deletions(-) diff --git a/tests/AbstractImageTest.php b/tests/AbstractImageTest.php index ab2c50918..340d0c23f 100644 --- a/tests/AbstractImageTest.php +++ b/tests/AbstractImageTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Abstract\AbstractFrame; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Size; @@ -14,6 +13,9 @@ use Intervention\Image\Interfaces\ModifierInterface; use Mockery; +/** + * @covers \Intervention\Image\Drivers\Abstract\AbstractImage + */ class AbstractImageTest extends TestCase { protected function abstractImageMock(): AbstractImage diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 7a22ce27f..b4a6738b7 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -4,6 +4,9 @@ use Intervention\Image\Collection; +/** + * @covers \Intervention\Image\Collection + */ class CollectionTest extends TestCase { public function testConstructor() diff --git a/tests/Drivers/Abstract/AbstractColorTest.php b/tests/Drivers/Abstract/AbstractColorTest.php index 4db7614ef..d419c1576 100644 --- a/tests/Drivers/Abstract/AbstractColorTest.php +++ b/tests/Drivers/Abstract/AbstractColorTest.php @@ -2,10 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Abstract; -use Mockery; use Intervention\Image\Drivers\Abstract\AbstractColor; use Intervention\Image\Tests\TestCase; +use Mockery; +/** + * @covers \Intervention\Image\Drivers\Abstract\AbstractColor + */ class AbstractColorTest extends TestCase { public function testToHex(): void diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php index 989fad85c..95307580e 100644 --- a/tests/Drivers/Gd/ColorTest.php +++ b/tests/Drivers/Gd/ColorTest.php @@ -5,6 +5,9 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Color + */ class ColorTest extends TestCase { protected function getTestColor($r = 0, $g = 0, $b = 0, $a = 0): Color diff --git a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php index 585d864ac..cb0a1fefd 100644 --- a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder + */ class ArrayColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php index ae92fed28..ddeb604fc 100644 --- a/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/Base64ImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\Base64ImageDecoder + */ class Base64ImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index 42b3b20e8..cf9acd0dc 100644 --- a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder + */ class BinaryImageDecoderTest extends TestCase { public function testDecodePng(): void diff --git a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php index fccc146d5..b0b947d2b 100644 --- a/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/DataUriImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\DataUriImageDecoder + */ class DataUriImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php index 840063b2b..8f11c73ed 100644 --- a/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/FilePathImageDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder + */ class FilePathImageDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php index 377321171..6d0fe0276 100644 --- a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php @@ -2,10 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder + */ class HexColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php index 573aa4a77..58b1e351f 100644 --- a/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/ImageObjectDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder + */ class ImageObjectDecoderTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php index 19c2a26f1..064eb4865 100644 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder + */ class RgbStringColorDecoderTest extends TestCase { public function testDecodeRgb(): void diff --git a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php index d0864d48a..be55b5897 100644 --- a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Gd\Decoders\TransparentColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Decoders\TransparentColorDecoder + */ class TransparentColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Gd/Encoders/GifEncoderTest.php b/tests/Drivers/Gd/Encoders/GifEncoderTest.php index e668ce229..71fcc3ace 100644 --- a/tests/Drivers/Gd/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/GifEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; +/** + * @covers \Intervention\Image\Drivers\Gd\Encoders\GifEncoder + */ class GifEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php index 231f0aab3..b8424df2a 100644 --- a/tests/Drivers/Gd/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/JpegEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageJpeg; +/** + * @covers \Intervention\Image\Drivers\Gd\Encoders\JpegEncoder + */ class JpegEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Gd/Encoders/PngEncoderTest.php b/tests/Drivers/Gd/Encoders/PngEncoderTest.php index 065aa4d63..189f6041a 100644 --- a/tests/Drivers/Gd/Encoders/PngEncoderTest.php +++ b/tests/Drivers/Gd/Encoders/PngEncoderTest.php @@ -10,6 +10,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImagePng; +/** + * @covers \Intervention\Image\Drivers\Gd\Encoders\PngEncoder + */ class PngEncoderTest extends TestCase { protected function getTestImage(): Image @@ -26,4 +29,4 @@ public function testEncode(): void $result = $encoder->encode($image); $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImagePng)); } -} \ No newline at end of file +} diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index 2497139d1..ef02e6fbe 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -8,6 +8,9 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Frame + */ class FrameTest extends TestCase { protected function getTestFrame(): Frame diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 9f3197414..abe2bb8b7 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\ImageFactory + */ class ImageFactoryTest extends TestCase { public function testNewImage(): void diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 1916587ba..4b09cf9c2 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -9,6 +9,9 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\Image + */ class ImageTest extends TestCase { protected Image $image; diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 22fa5dc88..4ef55d4e2 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,15 +2,15 @@ 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\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Gd\InputHandler + */ class InputHandlerTest extends TestCase { public function testHandleEmptyString(): void diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php index 9062eda45..b57e3e0ac 100644 --- a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\BlurModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\BlurModifier + */ class BlurModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php index e0e410250..426ab3569 100644 --- a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\BrightnessModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\BrightnessModifier + */ class BrightnessModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php index 7ec44484c..949a2b49d 100644 --- a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\ContrastModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\ContrastModifier + */ class ContrastModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php index 52b48fc1d..66bafabf4 100644 --- a/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DestroyModifierTest.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; use GdImage; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\DestroyModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\DestroyModifier + */ class DestroyModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index a1f8c049a..ae219b540 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -8,6 +8,10 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\FillModifier + */ class FillModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index 890560421..ffdc6831f 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -2,13 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\FitModifier; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\FitModifier + */ class FitModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php index 9302d460c..83155ea1e 100644 --- a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier + */ class GreyscaleModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php index 6429f3fbd..089db30f1 100644 --- a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\InvertModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\InvertModifier + */ class InvertModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php index 0fc4a892f..2f83c07cb 100644 --- a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\PixelateModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\PixelateModifier + */ class PixelateModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php index bd1d3e0b0..ec37c78ca 100644 --- a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -2,12 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\PlaceModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\PlaceModifier + */ class PlaceModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index ad4a4a9c9..642e3667a 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -2,12 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier + */ class ResizeModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php index 83d52090b..2966c0c18 100644 --- a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -2,11 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\Modifiers\SharpenModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateGdTestImage; + +/** + * @covers \Intervention\Image\Drivers\Gd\Modifiers\SharpenModifier + */ class SharpenModifierTest extends TestCase { use CanCreateGdTestImage; diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php index bf889f0cc..2b981b41c 100644 --- a/tests/Drivers/Imagick/ColorTest.php +++ b/tests/Drivers/Imagick/ColorTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Color + */ class ColorTest extends TestCase { protected function getTestColor(int $r = 0, int $g = 0, int $b = 0, float $a = 1): Color diff --git a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php index e0e16800e..ec63dc467 100644 --- a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder + */ class ArrayColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php index 1f3ad8ec9..3039a4a00 100644 --- a/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder + */ class ImageObjectDecoderTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php index 07f8e652b..60c6152d2 100644 --- a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Decoders\RgbStringColorDecoder + */ class RgbStringColorDecoderTest extends TestCase { public function testDecodeRgb(): void diff --git a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php index 915842bd6..ddc0659d0 100644 --- a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php @@ -6,6 +6,9 @@ use Intervention\Image\Drivers\Imagick\Decoders\TransparentColorDecoder; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Decoders\TransparentColorDecoder + */ class TransparentColorDecoderTest extends TestCase { public function testDecode(): void diff --git a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php index 9e51820ed..ecda07f42 100644 --- a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php @@ -12,6 +12,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; +/** + * @covers \Intervention\Image\Drivers\Imagick\Encoders\GifEncoder + */ class GifEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php index 6c6f09671..704e06d9b 100644 --- a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -12,6 +12,9 @@ use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageJpeg; +/** + * @covers \Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder + */ class JpegEncoderTest extends TestCase { protected function getTestImage(): Image diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index e57642766..1eaae1f29 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -9,6 +9,9 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Frame + */ class FrameTest extends TestCase { protected function getTestFrame(): Frame diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php index d23cb4437..d023182d2 100644 --- a/tests/Drivers/Imagick/ImageFactoryTest.php +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Drivers\Imagick\ImageFactory; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\ImageFactory + */ class ImageFactoryTest extends TestCase { public function testNewImage(): void diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 17c7d706b..95d8c060c 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -10,6 +10,9 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\Image + */ class ImageTest extends TestCase { protected Image $image; diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index 6bc689ba5..89dbcb9ca 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,15 +2,15 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Drivers\Imagick\InputHandler + */ class InputHandlerTest extends TestCase { public function testHandleEmptyString(): void diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php index a1ff3750f..4aab8279d 100644 --- a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\BlurModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\BlurModifier + */ class BlurModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php index f0e3dfc75..65e9cc798 100644 --- a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\BrightnessModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\BrightnessModifier + */ class BrightnessModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php index 042c3f088..5f9be5570 100644 --- a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\ContrastModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\ContrastModifier + */ class ContrastModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php index 6b9d3efe6..8df1066ef 100644 --- a/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DestroyModifierTest.php @@ -3,11 +3,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; use Imagick; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\DestroyModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\DestroyModifier + */ class DestroyModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index 4c5a8d154..a112386a1 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -4,12 +4,14 @@ use ImagickPixel; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\FillModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\FillModifier + */ class FillModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index aeb4ef314..3be86f64d 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -2,13 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\FitModifier; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\FitModifier + */ class FitModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php index 64b5be616..a46f3d7cf 100644 --- a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\GreyscaleModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\GreyscaleModifier + */ class GreyscaleModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php index c167542c4..2fdf28d52 100644 --- a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\InvertModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\InvertModifier + */ class InvertModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index d0d9d9931..a1a1de231 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\PixelateModifier; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\PixelateModifier + */ class PixelateModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index e6b66dd5b..de1862310 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -2,12 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\PlaceModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\PlaceModifier + */ class PlaceModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 7f2bb2011..27b358de7 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -2,13 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; -class CropResizeModifierTest extends TestCase +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier + */ +class ResizeModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php index f2e23bd1f..cab62e245 100644 --- a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -2,11 +2,13 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use Intervention\Image\Tests\TestCase; -use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\Modifiers\SharpenModifier; +use Intervention\Image\Tests\TestCase; use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; +/** + * @covers \Intervention\Image\Drivers\Imagick\Modifiers\SharpenModifier + */ class SharpenModifierTest extends TestCase { use CanCreateImagickTestImage; diff --git a/tests/EncodedImageTest.php b/tests/EncodedImageTest.php index 20c87ee42..a848ee953 100644 --- a/tests/EncodedImageTest.php +++ b/tests/EncodedImageTest.php @@ -4,6 +4,9 @@ use Intervention\Image\EncodedImage; +/** + * @covers \Intervention\Image\EncodedImage + */ class EncodedImageTest extends TestCase { public function testConstructor() diff --git a/tests/Geometry/PointTest.php b/tests/Geometry/PointTest.php index a1d6423db..8f95d81d6 100644 --- a/tests/Geometry/PointTest.php +++ b/tests/Geometry/PointTest.php @@ -5,6 +5,9 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; +/** + * @covers \Intervention\Image\Geometry\Point + */ class PointTest extends TestCase { public function testConstructor() diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index 9397d44d8..f1ac5edbf 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -7,6 +7,9 @@ use Intervention\Image\Geometry\Size; use PHPUnit\Framework\TestCase; +/** + * @covers \Intervention\Image\Geometry\Resizer + */ class ResizerTest extends TestCase { public function testMake(): void diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 6ae5f22c8..68dbe2cd0 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -2,12 +2,12 @@ namespace Intervention\Image\Tests\Geometry; +use Intervention\Image\Geometry\{Point, Size,}; use Intervention\Image\Tests\TestCase; -use Intervention\Image\Geometry\{ - Size, - Point, -}; +/** + * @covers \Intervention\Image\Geometry\Size + */ class SizeTest extends TestCase { public function testConstructor() diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index d05fb12ee..1c2e6db29 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -5,6 +5,9 @@ use Intervention\Image\ImageManager; use Intervention\Image\Interfaces\ImageInterface; +/** + * @covers \Intervention\Image\ImageManager + */ class ImageManagerTest extends TestCase { public function testConstructor() From f031c026ef9843b2f2ff59f840beaa2fcc23a44c Mon Sep 17 00:00:00 2001 From: Gwendolen Lynch Date: Thu, 23 Dec 2021 07:04:00 +0100 Subject: [PATCH 0242/1667] Add & update abstract class tests --- .../Abstract}/AbstractImageTest.php | 240 +++++++++++++++++- .../Abstract/AbstractInputHandlerTest.php | 44 ++++ .../Abstract/Decoders/AbstractDecoderTest.php | 46 ++++ .../Abstract/Encoders/AbstractEncoderTest.php | 48 ++++ .../Modifiers/AbstractFitModifierTest.php | 66 +++++ .../Modifiers/AbstractPadModifierTest.php | 67 +++++ .../Modifiers/AbstractRotateModifierTest.php | 80 ++++++ 7 files changed, 590 insertions(+), 1 deletion(-) rename tests/{ => Drivers/Abstract}/AbstractImageTest.php (51%) create mode 100644 tests/Drivers/Abstract/AbstractInputHandlerTest.php create mode 100644 tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php create mode 100644 tests/Drivers/Abstract/Encoders/AbstractEncoderTest.php create mode 100644 tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php create mode 100644 tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php create mode 100644 tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php diff --git a/tests/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php similarity index 51% rename from tests/AbstractImageTest.php rename to tests/Drivers/Abstract/AbstractImageTest.php index 340d0c23f..bac5df9e5 100644 --- a/tests/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -1,6 +1,6 @@ assertEquals(200, $img->getSize()->getHeight()); } + public function testSizeAlias(): void + { + $img = $this->abstractImageMock(); + $this->assertInstanceOf(Size::class, $img->getSize()); + $this->assertEquals(300, $img->size()->getWidth()); + $this->assertEquals(200, $img->size()->getHeight()); + } + public function testIsAnimated(): void { $img = Mockery::mock(AbstractImage::class, [new Collection()])->makePartial(); @@ -186,6 +195,51 @@ public function testGreyscale(): void $this->assertInstanceOf(ImageInterface::class, $result); } + public function testInvert(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\InvertModifier') + ->andReturn($modifier); + + $result = $img->invert(); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testBrightness(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\BrightnessModifier', 5) + ->andReturn($modifier); + + $result = $img->brightness(5); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testContrast(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ContrastModifier', 5) + ->andReturn($modifier); + + $result = $img->contrast(5); + $this->assertInstanceOf(ImageInterface::class, $result); + } + public function testBlur(): void { $img = $this->abstractImageMock(); @@ -231,6 +285,56 @@ public function testPlace(): void $this->assertInstanceOf(ImageInterface::class, $result); } + public function testFill(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $color = Mockery::mock(ColorInterface::class); + + $img->shouldReceive('handleInput') + ->with('abcdef') + ->andReturn($color); + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\FillModifier', $color, null) + ->andReturn($modifier); + + $result = $img->fill('abcdef'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPixelate(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\PixelateModifier', 42) + ->andReturn($modifier); + + $result = $img->pixelate(42); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testSharpen(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\SharpenModifier', 7) + ->andReturn($modifier); + + $result = $img->sharpen(7); + $this->assertInstanceOf(ImageInterface::class, $result); + } + public function testPickColors(): void { $color = Mockery::mock(ColorInterface::class); @@ -239,4 +343,138 @@ public function testPickColors(): void $result = $img->pickColors(1, 2); $this->assertInstanceOf(Collection::class, $result); } + + public function testResize(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ResizeModifier', 200, 100) + ->andReturn($modifier); + + $result = $img->resize(200, 100); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testResizeDown(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ResizeDownModifier', 200, 100) + ->andReturn($modifier); + + $result = $img->resizeDown(200, 100); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testScale(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ScaleModifier', 200, 100) + ->andReturn($modifier); + + $result = $img->scale(200, 100); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testScaleDown(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\ScaleDownModifier', 200, 100) + ->andReturn($modifier); + + $result = $img->scaleDown(200, 100); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testFit(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\FitModifier', 200, 100, 'center') + ->andReturn($modifier); + + $result = $img->fit(200, 100, 'center'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testFitDown(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\FitDownModifier', 200, 100, 'center') + ->andReturn($modifier); + + $result = $img->fitDown(200, 100, 'center'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPad(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\PadModifier', 200, 100, 'ffffff', 'center') + ->andReturn($modifier); + + $result = $img->pad(200, 100, 'ffffff', 'center'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testPadDown(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\PadDownModifier', 200, 100, 'ffffff', 'center') + ->andReturn($modifier); + + $result = $img->padDown(200, 100, 'ffffff', 'center'); + $this->assertInstanceOf(ImageInterface::class, $result); + } + + public function testDestroy(): void + { + $img = $this->abstractImageMock(); + + $modifier = Mockery::mock(ModifierInterface::class); + $modifier->shouldReceive('apply')->with($img)->andReturn($img); + + $img->shouldReceive('resolveDriverClass') + ->with('Modifiers\DestroyModifier') + ->andReturn($modifier); + + $img->destroy(); + } } diff --git a/tests/Drivers/Abstract/AbstractInputHandlerTest.php b/tests/Drivers/Abstract/AbstractInputHandlerTest.php new file mode 100644 index 000000000..ee40bb97a --- /dev/null +++ b/tests/Drivers/Abstract/AbstractInputHandlerTest.php @@ -0,0 +1,44 @@ +shouldReceive('handle')->with('test image')->andReturn($image); + $chain->shouldReceive('decode')->with('test image')->andReturn(Mockery::mock(ImageInterface::class)); + + $modifier = $this->getModifier($chain); + $modifier->handle('test image'); + } + + private function getModifier(AbstractDecoder $chain): AbstractInputHandler + { + return new class ($chain) extends AbstractInputHandler { + public function __construct(private AbstractDecoder $chain) + { + // + } + + protected function chain(): AbstractDecoder + { + return $this->chain; + } + }; + } +} diff --git a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php new file mode 100644 index 000000000..157c3474f --- /dev/null +++ b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php @@ -0,0 +1,46 @@ +makePartial(); + $decoder->shouldReceive('decode')->with('input string')->andReturn(null); + + $decoder->handle('input string'); + } + + public function testHandleFail(): void + { + $decoder = Mockery::mock(AbstractDecoder::class, [])->makePartial()->shouldAllowMockingProtectedMethods(); + $decoder->shouldReceive('decode')->with('input string')->andThrow(DecoderException::class); + + $this->expectException(DecoderException::class); + $this->expectExceptionMessage('Unable to decode given input.'); + + $decoder->handle('input string'); + } + + public function testHandleFailWithSuccessor(): void + { + $successor = Mockery::mock(AbstractDecoder::class)->makePartial(); + $successor->shouldReceive('decode')->with('input string')->andReturn(null); + + $decoder = Mockery::mock(AbstractDecoder::class, [$successor])->makePartial()->shouldAllowMockingProtectedMethods(); + $decoder->shouldReceive('decode')->with('input string')->andThrow(DecoderException::class); + + $decoder->handle('input string'); + } +} diff --git a/tests/Drivers/Abstract/Encoders/AbstractEncoderTest.php b/tests/Drivers/Abstract/Encoders/AbstractEncoderTest.php new file mode 100644 index 000000000..09626609b --- /dev/null +++ b/tests/Drivers/Abstract/Encoders/AbstractEncoderTest.php @@ -0,0 +1,48 @@ +getAbstractEncoder()->getBuffered($callback)); + } + + public function testSetGetQuality(): void + { + $encoder = $this->getAbstractEncoder(); + $encoder->setQuality(55); + + static::assertSame(55, $encoder->getQuality()); + } + + private function getAbstractEncoder(): AbstractEncoder + { + return new class () extends AbstractEncoder implements EncoderInterface { + public function getBuffered(callable $callback): string + { + return parent::getBuffered($callback); + } + + public function encode(ImageInterface $image): EncodedImage + { + } + }; + } +} diff --git a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php new file mode 100644 index 000000000..f8678f96e --- /dev/null +++ b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php @@ -0,0 +1,66 @@ + [150, 100, 50, 100, 50, 0]; + yield '100x150' => [100, 150, 75, 150, 13, 0]; + } + + /** @dataProvider providerCropSize */ + public function testGetCropSize(int $width, int $height, int $expectedWidth, int $expectedHeight, int $expectedX, int $expectedY): void + { + $modifier = $this->getModifier(100, 200, 'center'); + + $image = (new ImageFactory())->newImage($width, $height); + $size = $modifier->getCropSize($image); + + static::assertSame($expectedWidth, $size->getWidth()); + static::assertSame($expectedHeight, $size->getHeight()); + static::assertSame($expectedX, $size->getPivot()->getX()); + static::assertSame($expectedY, $size->getPivot()->getY()); + } + + public function testGetResizeSize(): void + { + $modifier = $this->getModifier(200, 100, 'center'); + + $image = (new ImageFactory())->newImage(300, 200); + $size = $modifier->getCropSize($image); + $resize = $modifier->getResizeSize($size); + + static::assertSame(200, $resize->getWidth()); + static::assertSame(100, $resize->getHeight()); + static::assertSame(0, $resize->getPivot()->getX()); + static::assertSame(0, $resize->getPivot()->getY()); + } + + private function getModifier(int $width, int $height, string $position): AbstractFitModifier + { + return new class ($width, $height, $position) extends AbstractFitModifier { + public function getCropSize(ImageInterface $image): SizeInterface + { + return parent::getCropSize($image); + } + + public function getResizeSize(SizeInterface $size): SizeInterface + { + return parent::getResizeSize($size); + } + }; + } +} diff --git a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php new file mode 100644 index 000000000..4308a3bf5 --- /dev/null +++ b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php @@ -0,0 +1,67 @@ + [150, 100, 100, 67, 0, 67]; + yield '100x150' => [100, 150, 100, 150, 0, 25]; + } + + /** @dataProvider providerCropSize */ + public function testGetCropSize(int $width, int $height, int $expectedWidth, int $expectedHeight, int $expectedX, int $expectedY): void + { + $modifier = $this->getModifier(100, 200, 'ffffff', 'center'); + + $image = (new ImageFactory())->newImage($width, $height); + $size = $modifier->getCropSize($image); + + static::assertSame($expectedWidth, $size->getWidth()); + static::assertSame($expectedHeight, $size->getHeight()); + static::assertSame($expectedX, $size->getPivot()->getX()); + static::assertSame($expectedY, $size->getPivot()->getY()); + } + + public function testGetResizeSize(): void + { + $modifier = $this->getModifier(200, 100, 'ffffff', 'center'); + + $image = (new ImageFactory())->newImage(300, 200); + $resize = $modifier->getResizeSize($image); + + static::assertSame(200, $resize->getWidth()); + static::assertSame(100, $resize->getHeight()); + static::assertSame(0, $resize->getPivot()->getX()); + static::assertSame(0, $resize->getPivot()->getY()); + } + + private function getModifier(int $width, int $height, $background, string $position): AbstractPadModifier + { + return new class($width, $height, $background, $position) extends AbstractPadModifier { + public function getCropSize(ImageInterface $image): SizeInterface + { + return parent::getCropSize($image); + } + + public function getResizeSize(ImageInterface $image): SizeInterface + { + return parent::getResizeSize($image); + } + }; + } +} diff --git a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php new file mode 100644 index 000000000..42de945b5 --- /dev/null +++ b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php @@ -0,0 +1,80 @@ + [0.0, 0]; + yield '90 degrees' => [90.0, 90]; + yield '180 degrees' => [180.0, 180]; + yield '270 degrees' => [270.0, 270]; + yield '360 degrees' => [0.0, 360]; + } + + /** @dataProvider providerRotationAngle */ + public function testRotationAngle(float $expected, int $angle): void + { + $modifier = $this->getModifier($angle, 'abcdef'); + + static::assertSame($expected, $modifier->rotationAngle()); + } + + public function testBackgroundColor(): void + { + $modifier = $this->getModifier(90, 'abcdef'); + $color = $modifier->backgroundColor(); + + static::assertSame(255, $color->red()); + } + + public function testBackgroundColorInvalidValueThrowsException(): void + { + $this->expectException(TypeException::class); + $this->expectExceptionMessage('Argument #2 must be a color value'); + + $modifier = $this->getModifier(90, 'bad value'); + $modifier->backgroundColor(); + } + + private function getModifier(float $angle, $background): AbstractRotateModifier + { + return new class ($angle, $background) extends AbstractRotateModifier { + public function rotationAngle(): float + { + return parent::rotationAngle(); + } + + public function backgroundColor(): ColorInterface + { + return parent::backgroundColor(); + } + + public function handleInput($input): ImageInterface|ColorInterface + { + if ($this->background === 'bad value') { + throw new DecoderException(); + } + + $color = Mockery::mock(ColorInterface::class); + $color->shouldReceive('red')->andReturn(255); + + return $color; + } + }; + } +} From d47e83fc0ed7b47f3c3512732ab95fcd5bd7228b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 2 Jan 2022 10:21:10 +0100 Subject: [PATCH 0243/1667] Version contraint for intervention/gif --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index dabcb3533..f26b1279d 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ ], "require": { "php": "^8", - "intervention/gif": "dev-master", + "intervention/gif": "^3.0", "intervention/mimesniffer": "^0.4.2" }, "require-dev": { From 87267764b9d3f067419316fc06cf0f2e30bfcb6f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 2 Jan 2022 10:25:40 +0100 Subject: [PATCH 0244/1667] Added minimum-stability --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f26b1279d..b8f724509 100644 --- a/composer.json +++ b/composer.json @@ -29,5 +29,6 @@ "psr-4": { "Intervention\\Image\\Tests\\": "tests" } - } + }, + "minimum-stability": "alpha" } From ef854bc98a490603ee6c92c5df740534beb5616d Mon Sep 17 00:00:00 2001 From: mostafa hesham Date: Sat, 8 Jan 2022 20:17:58 +0200 Subject: [PATCH 0245/1667] fixed typo --- src/Intervention/Image/Imagick/Color.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Imagick/Color.php b/src/Intervention/Image/Imagick/Color.php index a41ff3a6e..1e109b171 100644 --- a/src/Intervention/Image/Imagick/Color.php +++ b/src/Intervention/Image/Imagick/Color.php @@ -265,7 +265,7 @@ public function getPixel() } /** - * Calculates RGA integer alpha value into float value + * Calculates RGBA integer alpha value into float value * * @param int $value * @return float From 5a4e677b827623649c6d14362f883642302958d8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 16 Jan 2022 18:13:56 +0100 Subject: [PATCH 0246/1667] Added EncodedImage::toString() --- src/EncodedImage.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/EncodedImage.php b/src/EncodedImage.php index 011c6f158..a0461d844 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -33,8 +33,13 @@ public function toDataUri(): string return sprintf('data:%s;base64,%s', $this->mimetype, base64_encode($this->data)); } - public function __toString(): string + public function toString(): string { return $this->data; } + + public function __toString(): string + { + return $this->toString(); + } } From 879653c1c689b266e803afa817ccdca38ac85342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20K=C3=B6hler?= Date: Mon, 17 Jan 2022 16:57:25 +0100 Subject: [PATCH 0247/1667] Update broken README documentation hyperlinks Fix the hyperlinks by updating the URL that they are pointing to on the intervention.io website. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 350f471cf..37f940723 100755 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ Intervention Image is a **PHP image handling and manipulation** library providin ## Getting started -- [Installation](http://image.intervention.io/getting_started/installation) -- [Laravel Framework Integration](http://image.intervention.io/getting_started/installation#laravel) -- [Basic Usage](http://image.intervention.io/use/basics) +- [Installation](https://image.intervention.io/v2/introduction/installation) +- [Laravel Framework Integration](https://image.intervention.io/v2/introduction/installation#integration-in-laravel) +- [Basic Usage](https://image.intervention.io/v2/usage/overview#basic-usage) ## Code Examples From 4dfa8f242ec8cc436fd79e5988e64e3faed1309c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 22 Jan 2022 08:27:34 +0100 Subject: [PATCH 0248/1667] Changed url and email --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e750b406b..6d4d360be 100644 --- a/composer.json +++ b/composer.json @@ -7,8 +7,8 @@ "authors": [ { "name": "Oliver Vogel", - "email": "oliver@olivervogel.com", - "homepage": "http://olivervogel.com/" + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" } ], "require": { From 53dd28ef267573fd519b077c391c48d6c07a45eb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 22 Jan 2022 08:50:13 +0100 Subject: [PATCH 0249/1667] Changed funding link --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 612406cbc..5dd8eeebc 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ github: [Intervention] -custom: https://www.paypal.me/interventionphp +custom: https://paypal.me/interventionio From 685308730443c4e5b97c54facd505f192793c90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20J=C3=B3=C5=BAwiak?= Date: Fri, 28 Jan 2022 09:21:23 +0100 Subject: [PATCH 0250/1667] Update User-Agent to latest Chrome --- src/Intervention/Image/AbstractDecoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 743cc5c36..e51d4da3c 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -71,7 +71,7 @@ public function initFromUrl($url) 'method'=>"GET", 'protocol_version'=>1.1, // force use HTTP 1.1 for service mesh environment with envoy 'header'=>"Accept-language: en\r\n". - "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36\r\n" ] ]; From 4fc4e317176da0c9652f6e76df201642d061c669 Mon Sep 17 00:00:00 2001 From: Thomas Picquet Date: Mon, 31 Jan 2022 11:20:45 -0800 Subject: [PATCH 0251/1667] PHP 8.1 deprecation fix --- src/Collection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 5d669ae3c..90fbefaf1 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -31,9 +31,9 @@ public static function create(array $items = []): self /** * Returns Iterator * - * @return array + * @return \Traversable */ - public function getIterator() + public function getIterator(): \Traversable { return new ArrayIterator($this->items); } From 66bb8a4b0f3c316e1407d36196556b841f3a23a3 Mon Sep 17 00:00:00 2001 From: Thomas Picquet Date: Mon, 31 Jan 2022 11:20:59 -0800 Subject: [PATCH 0252/1667] Fixed implicit converstion from float to int --- src/Drivers/Gd/Modifiers/BrightnessModifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Modifiers/BrightnessModifier.php b/src/Drivers/Gd/Modifiers/BrightnessModifier.php index ee306bf02..443a41b6a 100644 --- a/src/Drivers/Gd/Modifiers/BrightnessModifier.php +++ b/src/Drivers/Gd/Modifiers/BrightnessModifier.php @@ -15,7 +15,7 @@ public function __construct(protected int $level) public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { - imagefilter($frame->getCore(), IMG_FILTER_BRIGHTNESS, ($this->level * 2.55)); + imagefilter($frame->getCore(), IMG_FILTER_BRIGHTNESS, intval($this->level * 2.55)); } return $image; From 51e4fdb7e67576978d6e9899f65807f5dbd5d521 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 10 Feb 2022 16:08:47 +0000 Subject: [PATCH 0253/1667] Added modifiers to mirror images horizontally & vertically --- src/Drivers/Abstract/AbstractImage.php | 24 +++++++++++++ src/Drivers/Gd/Modifiers/FlipModifier.php | 18 ++++++++++ src/Drivers/Gd/Modifiers/FlopModifier.php | 18 ++++++++++ .../Imagick/Modifiers/FlipModifier.php | 18 ++++++++++ .../Imagick/Modifiers/FlopModifier.php | 18 ++++++++++ .../Gd/Modifiers/FlipFlopModifierTest.php | 34 +++++++++++++++++++ .../Modifiers/FlipFlopModifierTest.php | 34 +++++++++++++++++++ 7 files changed, 164 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/FlipModifier.php create mode 100644 src/Drivers/Gd/Modifiers/FlopModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/FlipModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/FlopModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 4d11c373a..55c3be56d 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -153,6 +153,30 @@ public function rotate(float $angle, $background = 'ffffff'): ImageInterface ); } + /** + * Creates a vertical mirror image + * + * @return ImageInterface + */ + public function flip(): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FlipModifier') + ); + } + + /** + * Creates a horizontal mirror image + * + * @return ImageInterface + */ + public function flop(): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\FlopModifier') + ); + } + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/FlipModifier.php b/src/Drivers/Gd/Modifiers/FlipModifier.php new file mode 100644 index 000000000..a7eee5676 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FlipModifier.php @@ -0,0 +1,18 @@ +getCore(), IMG_FLIP_VERTICAL); + } + + return $image; + } +} diff --git a/src/Drivers/Gd/Modifiers/FlopModifier.php b/src/Drivers/Gd/Modifiers/FlopModifier.php new file mode 100644 index 000000000..e926e3e0d --- /dev/null +++ b/src/Drivers/Gd/Modifiers/FlopModifier.php @@ -0,0 +1,18 @@ +getCore(), IMG_FLIP_HORIZONTAL); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/FlipModifier.php b/src/Drivers/Imagick/Modifiers/FlipModifier.php new file mode 100644 index 000000000..1c9163935 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FlipModifier.php @@ -0,0 +1,18 @@ +getCore()->flipImage(); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/FlopModifier.php b/src/Drivers/Imagick/Modifiers/FlopModifier.php new file mode 100644 index 000000000..cfb7160d0 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/FlopModifier.php @@ -0,0 +1,18 @@ +getCore()->flopImage(); + } + + return $image; + } +} diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php new file mode 100644 index 000000000..0305f6a82 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -0,0 +1,34 @@ +createTestImage('tile.png'); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $image->modify(new FlipModifier()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + } + + public function testFlopImage(): void + { + $image = $this->createTestImage('tile.png'); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $image->modify(new FlopModifier()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php new file mode 100644 index 000000000..10ecab694 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php @@ -0,0 +1,34 @@ +createTestImage('tile.png'); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $image->modify(new FlipModifier()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + } + + public function testFlopImage(): void + { + $image = $this->createTestImage('tile.png'); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $image->modify(new FlopModifier()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + } +} From a1a07ac1e731cfed771feeb72624250226d5c293 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 11 Feb 2022 19:17:15 +0100 Subject: [PATCH 0254/1667] Added gamma modifier --- src/Drivers/Abstract/AbstractImage.php | 7 ++++++ src/Drivers/Gd/Modifiers/GammaModifier.php | 23 ++++++++++++++++++ .../Imagick/Modifiers/GammaModifier.php | 23 ++++++++++++++++++ .../Gd/Modifiers/GammaModifierTest.php | 24 +++++++++++++++++++ .../Imagick/Modifiers/GammaModifierTest.php | 24 +++++++++++++++++++ 5 files changed, 101 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/GammaModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/GammaModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/GammaModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/GammaModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 55c3be56d..2b1b4d6a5 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -139,6 +139,13 @@ public function contrast(int $level): ImageInterface ); } + public function gamma(float $gamma): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\GammaModifier', $gamma) + ); + } + public function blur(int $amount = 5): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/GammaModifier.php b/src/Drivers/Gd/Modifiers/GammaModifier.php new file mode 100644 index 000000000..f92087390 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/GammaModifier.php @@ -0,0 +1,23 @@ +getCore(), 1, $this->gamma); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/GammaModifier.php b/src/Drivers/Imagick/Modifiers/GammaModifier.php new file mode 100644 index 000000000..ad813ace7 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/GammaModifier.php @@ -0,0 +1,23 @@ +getCore()->gammaImage($this->gamma); + } + + return $image; + } +} diff --git a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php new file mode 100644 index 000000000..dec4de93c --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $image->modify(new GammaModifier(2.1)); + $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php new file mode 100644 index 000000000..c3af78c02 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $image->modify(new GammaModifier(2.1)); + $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); + } +} From 9d2318d828ce28c53d43d86e704401a7e7f0471b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 11 Feb 2022 19:33:29 +0100 Subject: [PATCH 0255/1667] Added ModifierStack class --- src/ModifierStack.php | 28 ++++++++++++++++++++++++++++ tests/ModiferStackTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/ModifierStack.php create mode 100644 tests/ModiferStackTest.php diff --git a/src/ModifierStack.php b/src/ModifierStack.php new file mode 100644 index 000000000..13eac80a3 --- /dev/null +++ b/src/ModifierStack.php @@ -0,0 +1,28 @@ +modifiers as $modifier) { + $modifier->apply($image); + } + } + + public function push(ModifierInterface $modifier): self + { + $this->modifiers[] = $modifier; + + return $this; + } +} diff --git a/tests/ModiferStackTest.php b/tests/ModiferStackTest.php new file mode 100644 index 000000000..8577d3c1d --- /dev/null +++ b/tests/ModiferStackTest.php @@ -0,0 +1,25 @@ +assertInstanceOf(ModifierStack::class, $stack); + } + + public function testPush(): void + { + $stack = new ModifierStack([]); + $result = $stack->push(new GreyscaleModifier()); + $this->assertInstanceOf(ModifierStack::class, $result); + } +} From 03431a215576a5ecb9e6dee66aa06bb84f98b3ce Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 11 Feb 2022 19:54:13 +0100 Subject: [PATCH 0256/1667] Fixed bug in ModifierStack, Added tests for apply method --- src/ModifierStack.php | 2 ++ tests/ModiferStackTest.php | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/ModifierStack.php b/src/ModifierStack.php index 13eac80a3..109fbfc9b 100644 --- a/src/ModifierStack.php +++ b/src/ModifierStack.php @@ -17,6 +17,8 @@ public function apply(ImageInterface $image): ImageInterface foreach ($this->modifiers as $modifier) { $modifier->apply($image); } + + return $image; } public function push(ModifierInterface $modifier): self diff --git a/tests/ModiferStackTest.php b/tests/ModiferStackTest.php index 8577d3c1d..afe992349 100644 --- a/tests/ModiferStackTest.php +++ b/tests/ModiferStackTest.php @@ -3,7 +3,9 @@ namespace Intervention\Image\Tests; use Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier; +use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\ModifierStack; +use Mockery; /** * @covers \Intervention\Image\ModifierStack @@ -22,4 +24,19 @@ public function testPush(): void $result = $stack->push(new GreyscaleModifier()); $this->assertInstanceOf(ModifierStack::class, $result); } + + public function testApply(): void + { + $image = Mockery::mock(ImageInterface::class); + + $modifier1 = Mockery::mock(AbstractColor::class)->makePartial(); + $modifier1->shouldReceive('apply')->once()->with($image); + + $modifier2 = Mockery::mock(AbstractColor::class)->makePartial(); + $modifier2->shouldReceive('apply')->once()->with($image); + + $stack = new ModifierStack([$modifier1, $modifier2]); + $result = $stack->apply($image); + $this->assertInstanceOf(ImageInterface::class, $image); + } } From 9828c9310c3db0cec0d498e279499c3c0556d5ed Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 19 Feb 2022 09:03:08 +0100 Subject: [PATCH 0257/1667] Removed unused code --- src/Drivers/Gd/Image.php | 7 ------- src/Drivers/Imagick/Image.php | 5 ----- 2 files changed, 12 deletions(-) diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index a8f00b980..38c429812 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -2,16 +2,9 @@ namespace Intervention\Image\Drivers\Gd; -use GdImage; -use Intervention\Image\Collection; 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; use IteratorAggregate; class Image extends AbstractImage implements ImageInterface, IteratorAggregate diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index f04d8ae34..f6507eec7 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -2,14 +2,9 @@ namespace Intervention\Image\Drivers\Imagick; -use Imagick; -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; class Image extends AbstractImage implements ImageInterface, IteratorAggregate From 70a9a556a51b936fb7a305956fa93758d2e01e86 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:00:58 +0300 Subject: [PATCH 0258/1667] Update run-tests.yml update the name. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 5d7ae107f..81bd97054 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,4 +1,4 @@ -name: run-tests +name: Tests on: [push] From d52630338b694808ce7e9ad1cbb50db799acdcd5 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:01:51 +0300 Subject: [PATCH 0259/1667] Update run-tests.yml add run on pull requests. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 81bd97054..3ccb05577 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,6 +1,6 @@ name: Tests -on: [push] +on: [push, pull_request] jobs: run: From 47d0ada7d49612dd25e767b5e57cbf664949d0f9 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:02:49 +0300 Subject: [PATCH 0260/1667] Update run-tests.yml add the fail fast key. --- .github/workflows/run-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3ccb05577..d86d55e68 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,6 +6,7 @@ jobs: run: runs-on: ubuntu-latest strategy: + fail-fast: true matrix: php-versions: ['8.0'] name: Testing on PHP ${{ matrix.php-versions }} From 488f82398745a723148668038b225b4d20b807c2 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:03:13 +0300 Subject: [PATCH 0261/1667] Update run-tests.yml update matrix with php and stability keys. --- .github/workflows/run-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d86d55e68..42f001eb3 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,7 +8,9 @@ jobs: strategy: fail-fast: true matrix: - php-versions: ['8.0'] + php: [ 8.0, 8.1 ] + stability: [ prefer-lowest, prefer-stable ] + name: Testing on PHP ${{ matrix.php-versions }} steps: - name: 'Checkout Project' From 2395773bb64482e3e94a16c912c38d7d4b5b9d08 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:03:40 +0300 Subject: [PATCH 0262/1667] Update run-tests.yml update the name key. --- .github/workflows/run-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 42f001eb3..7227ecfe5 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,7 +11,8 @@ jobs: php: [ 8.0, 8.1 ] stability: [ prefer-lowest, prefer-stable ] - name: Testing on PHP ${{ matrix.php-versions }} + name: P${{ matrix.php }} - ${{ matrix.stability }} + steps: - name: 'Checkout Project' uses: actions/checkout@v2 From d1caaadfa5b1a474051b00881be4d62d8ee0a2d4 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:06:02 +0300 Subject: [PATCH 0263/1667] Update run-tests.yml update the checkout block. --- .github/workflows/run-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 7227ecfe5..ca59e66fa 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -14,8 +14,9 @@ jobs: name: P${{ matrix.php }} - ${{ matrix.stability }} steps: - - name: 'Checkout Project' + - name: Checkout project uses: actions/checkout@v2 + - name: 'Setup Environment' uses: shivammathur/setup-php@v2 with: From 896a57a891b816c1d70cc7ba6bb5be6b89a65a0c Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:06:50 +0300 Subject: [PATCH 0264/1667] Update run-tests.yml update the setup block. --- .github/workflows/run-tests.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ca59e66fa..35bded2d1 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -17,12 +17,13 @@ jobs: - name: Checkout project uses: actions/checkout@v2 - - name: 'Setup Environment' + - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-versions }} - extensions: imagick,gd - tools: phpunit,composer + php-version: ${{ matrix.php }} + extensions: mbstring, gd, imagick + coverage: none + - name: 'Install Dependencies' run: composer install -o -q - name: 'Run PHPUnit' From dce69bad28063f410c0d0966852ac358bf6c0777 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:07:20 +0300 Subject: [PATCH 0265/1667] Update run-tests.yml update the install dependencies block. --- .github/workflows/run-tests.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 35bded2d1..cc6ce83b9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,7 +24,8 @@ jobs: extensions: mbstring, gd, imagick coverage: none - - name: 'Install Dependencies' - run: composer install -o -q + - name: Install dependencies + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: 'Run PHPUnit' - run: vendor/bin/phpunit \ No newline at end of file + run: vendor/bin/phpunit From b19697b67cd7b8d4c3b63fa7edaba1a4892de472 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:10:57 +0300 Subject: [PATCH 0266/1667] Update run-tests.yml update the execute block. --- .github/workflows/run-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index cc6ce83b9..9f8012c45 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -27,5 +27,5 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction - - name: 'Run PHPUnit' - run: vendor/bin/phpunit + - name: Execute tests + run: vendor/bin/phpunit --no-coverage From 0846dd592d103bb23a738084b0afb1132eaab982 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:22:53 +0300 Subject: [PATCH 0267/1667] Update CanCreateImagickTestImage.php fix the create with decoder method name. --- tests/Traits/CanCreateImagickTestImage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Traits/CanCreateImagickTestImage.php b/tests/Traits/CanCreateImagickTestImage.php index 0ff426753..c097c50ee 100644 --- a/tests/Traits/CanCreateImagickTestImage.php +++ b/tests/Traits/CanCreateImagickTestImage.php @@ -11,12 +11,12 @@ trait CanCreateImagickTestImage { public function createTestImage($filename = 'test.jpg'): Image { - return $this->testImageDecoder()->handle( + return $this->createWithImageDecoder()->handle( sprintf('%s/../images/%s', __DIR__, $filename) ); } - protected function testImageDecoder(): FilePathImageDecoder + protected function createWithImageDecoder(): FilePathImageDecoder { return new FilePathImageDecoder(); } From 8a4f8e40c508708f841ea4fd59a0b9301c7f13a2 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:23:44 +0300 Subject: [PATCH 0268/1667] Update CanCreateGdTestImage.php fix the create with decoder method name. --- tests/Traits/CanCreateGdTestImage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Traits/CanCreateGdTestImage.php b/tests/Traits/CanCreateGdTestImage.php index e2e5d81ae..9d12ed2b7 100644 --- a/tests/Traits/CanCreateGdTestImage.php +++ b/tests/Traits/CanCreateGdTestImage.php @@ -21,7 +21,7 @@ public function getTestImageData($filename = 'test.jpg'): string public function createTestImage($filename = 'test.jpg'): Image { - return $this->testImageDecoder()->handle( + return $this->createWithImageDecoder()->handle( $this->getTestImagePath($filename) ); } @@ -41,7 +41,7 @@ public function createTestAnimation(): Image ])); } - protected function testImageDecoder(): FilePathImageDecoder + protected function createWithImageDecoder(): FilePathImageDecoder { return new FilePathImageDecoder(); } From 66af2ff050d850317fc1869bd11ca64a74983c4f Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 02:25:05 +0300 Subject: [PATCH 0269/1667] Update run-tests.yml add cache composer dependencies. --- .github/workflows/run-tests.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9f8012c45..f3bd3eb8c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,6 +24,17 @@ jobs: extensions: mbstring, gd, imagick coverage: none + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ matrix.stability }}-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ matrix.stability }}- + - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction From 2dc7f019c228ba56ab444278c4600287a40e32a2 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 09:52:05 +0300 Subject: [PATCH 0270/1667] Update Update run-tests.yml update the fail fast key. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f3bd3eb8c..d68dce9e9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,7 +6,7 @@ jobs: run: runs-on: ubuntu-latest strategy: - fail-fast: true + fail-fast: false matrix: php: [ 8.0, 8.1 ] stability: [ prefer-lowest, prefer-stable ] From 9bf8840318a5ba46096beb75105ab2e7398fd021 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 10:43:15 +0300 Subject: [PATCH 0271/1667] Add phpunit.xml.dist file. --- phpunit.xml.dist | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 phpunit.xml.dist diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 000000000..63454441b --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,23 @@ + + + + + ./tests/ + + + + + + src + + + From 30980a24c8b4275ac0d2c475f4fadf4405609461 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 10:44:02 +0300 Subject: [PATCH 0272/1667] Update .gitignore add build folder pattern. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 954837503..8367435f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.idea/ +build/ +vendor/ .DS_Store composer.lock vendor/ From 428d98241dcea45a0d43622748ad354a0f93aa01 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 10:44:25 +0300 Subject: [PATCH 0273/1667] Update .gitignore add phpunit.xml pattern. --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8367435f5..3e4ac796d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,4 @@ build/ vendor/ .DS_Store composer.lock -vendor/ -.idea/ -.phpunit.result.cache \ No newline at end of file +phpunit.xml From e69b01dfe8fcffffcf56ee3fb5017cc25b9e6e3f Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 10:45:41 +0300 Subject: [PATCH 0274/1667] Update .gitignore add .phpunit.result pattern. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3e4ac796d..c7fe04e67 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ build/ vendor/ .DS_Store +.phpunit.result.cache composer.lock phpunit.xml From ca2a3c7d9fb6946eb9429efa405dae3431a6c6a5 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 15:52:39 +0300 Subject: [PATCH 0275/1667] Style FitModifier.php fix a typo in the comment. --- src/Drivers/Gd/Modifiers/FitModifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 194376a5f..0278416ef 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -65,7 +65,7 @@ protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeI imagedestroy($current); - // set new content as recource + // set new content as resource $frame->setCore($modified); } } From b626022fa77016fc64d50ce2a156dd3b9241a61c Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Wed, 23 Mar 2022 16:11:38 +0300 Subject: [PATCH 0276/1667] Update GD\Color.php update transparency extraction. --- src/Drivers/Gd/Color.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php index 46904b37a..d4cde7209 100644 --- a/src/Drivers/Gd/Color.php +++ b/src/Drivers/Gd/Color.php @@ -34,7 +34,7 @@ public function alpha(): float public function toArray(): array { - $a = ($this->value >> 24) & 0xFF; + $a = ($this->value >> 24) & 0x7F; $r = ($this->value >> 16) & 0xFF; $g = ($this->value >> 8) & 0xFF; $b = $this->value & 0xFF; From adffed38814b80a7c6393c21851a8d73ee44f197 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Thu, 24 Mar 2022 01:53:37 +0300 Subject: [PATCH 0277/1667] Update GD\ResizeModifier.php add transparency restore to pass the tests. --- src/Drivers/Gd/Modifiers/ResizeModifier.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index b030a8e38..7646a22f8 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -44,15 +44,14 @@ protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): $current = $frame->getCore(); // preserve transparency + imagealphablending($modified, false); $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); } @@ -72,6 +71,22 @@ protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): imagedestroy($current); + if ($transIndex != -1) { // @todo refactor because of duplication + imagecolortransparent($modified, $transIndex); + for ($y = 0; $y < $resizeTo->getHeight(); ++$y) { + for ($x = 0; $x < $resizeTo->getWidth(); ++$x) { + if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { + imagesetpixel( + $modified, + $x, + $y, + $transIndex + ); + } + } + } + } + // set new content as recource $frame->setCore($modified); } From ad0ad160420ac1a441b20c8c38e69f1b9457cbc6 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Thu, 24 Mar 2022 01:54:13 +0300 Subject: [PATCH 0278/1667] Update GD\FitModifier.php add transparency restore to pass the tests. --- src/Drivers/Gd/Modifiers/FitModifier.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 0278416ef..de0e9725d 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -37,15 +37,14 @@ protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeI $current = $frame->getCore(); // preserve transparency + imagealphablending($modified, false); $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); } @@ -65,6 +64,22 @@ protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeI imagedestroy($current); + if ($transIndex != -1) { // @todo refactor because of duplication + imagecolortransparent($modified, $transIndex); + for ($y = 0; $y < $resize->getHeight(); ++$y) { + for ($x = 0; $x < $resize->getWidth(); ++$x) { + if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { + imagesetpixel( + $modified, + $x, + $y, + $transIndex + ); + } + } + } + } + // set new content as resource $frame->setCore($modified); } From 161a3229496de8df9e836e4c573830977c50969a Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Thu, 24 Mar 2022 01:59:46 +0300 Subject: [PATCH 0279/1667] Fix SizeInterface.php add the resize method to fit ResizeModifier usage. --- src/Interfaces/SizeInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index 0e8ea82d8..fd641fbe1 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -9,6 +9,7 @@ public function getHeight(): int; public function getPivot(): PointInterface; public function setWidth(int $width): SizeInterface; public function setHeight(int $height): SizeInterface; + public function resize(?int $width = null, ?int $height = null): SizeInterface; public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; From decdbd0bfa6b9df16228c55421563a7a574ef358 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Thu, 24 Mar 2022 02:19:46 +0300 Subject: [PATCH 0280/1667] Update run-tests.yml some experiments. --- .github/workflows/run-tests.yml | 62 +++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d68dce9e9..57136c659 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,9 +6,16 @@ jobs: run: runs-on: ubuntu-latest strategy: - fail-fast: false + fail-fast: true matrix: - php: [ 8.0, 8.1 ] + php: [ '8.0', '8.1' ] + imagemagick: [ + '6.9.12-43', + '7.1.0-28' + ] + imagick: [ + '3.7.0' + ] stability: [ prefer-lowest, prefer-stable ] name: P${{ matrix.php }} - ${{ matrix.stability }} @@ -21,9 +28,55 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: mbstring, gd, imagick + extensions: mbstring, gd coverage: none + - name: Prepare environament for Imagemagick + run: | + sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev + sudo apt-get update + sudo apt-get install -y libjpeg62-dev + sudo apt-get install -y libgif-dev + sudo apt-get install -y libtiff-dev + sudo apt-get install -y libpng-dev + sudo apt-get install -y libwebp-dev + sudo apt-get install -y libmagickwand-dev + + - name: Cache ImageMagick + uses: actions/cache@v2 + env: + cache-name: cache-ImageMagick + with: + path: ~/im/imagemagick-${{ matrix.imagemagick }} + key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} + + - name: Install ImageMagick + run: | + curl -o /tmp/ImageMagick.tar.gz -sL https://www.imagemagick.org/download/ImageMagick-${{ matrix.imagemagick }}.tar.gz + ( + cd /tmp || exit 1 + tar xf ImageMagick.tar.gz + cd ImageMagick-${{ matrix.imagemagick }} + sudo ./configure --prefix=/opt/imagemagick + sudo make -j$(nproc) + sudo make install + ) + + - name: Install PHP ImageMagick extension + run: | + curl -o /tmp/imagick.tgz -sL http://pecl.php.net/get/imagick-${{ matrix.imagick }}.tgz + ( + cd /tmp || exit 1 + tar -xzf imagick.tgz + cd imagick-${{ matrix.imagick }} + phpize + sudo ./configure --with-imagick=/opt/imagemagick + sudo make -j$(nproc) + sudo make install + ) + sudo bash -c 'echo "extension=imagick.so" >> /etc/php/${{ matrix.php }}/cli/php.ini' + php --ri imagick; + - name: Get composer cache directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" @@ -38,5 +91,8 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: Which Imagick Version + run: php -r 'var_dump(Imagick::getVersion());' + - name: Execute tests run: vendor/bin/phpunit --no-coverage From d959e37d89edec25130b9c97de8be73d193044c2 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 10:55:07 +0300 Subject: [PATCH 0281/1667] Update run-tests.yml rename imagemagick cache name. --- .github/workflows/run-tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 57136c659..81c048110 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -44,8 +44,7 @@ jobs: - name: Cache ImageMagick uses: actions/cache@v2 - env: - cache-name: cache-ImageMagick + id: cache-imagemagick with: path: ~/im/imagemagick-${{ matrix.imagemagick }} key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} From 80dcb4cbd1d5c539ecbd74100349c4235994ab72 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 10:56:26 +0300 Subject: [PATCH 0282/1667] Update run-tests.yml style file. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 81c048110..455ee9662 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,6 +1,6 @@ name: Tests -on: [push, pull_request] +on: [ push, pull_request ] jobs: run: From e988c46b6fe9b10993cfa20500ffca71192d31ac Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 10:56:55 +0300 Subject: [PATCH 0283/1667] Update run-tests.yml update the fail fast key. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 455ee9662..abd925115 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,7 +6,7 @@ jobs: run: runs-on: ubuntu-latest strategy: - fail-fast: true + fail-fast: false matrix: php: [ '8.0', '8.1' ] imagemagick: [ From 81c9b6be7fec5e3850a4760431f2929e1a540d3d Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:08:41 +0300 Subject: [PATCH 0284/1667] Update run-tests.yml change the configure path. --- .github/workflows/run-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index abd925115..ece51ac49 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -56,7 +56,7 @@ jobs: cd /tmp || exit 1 tar xf ImageMagick.tar.gz cd ImageMagick-${{ matrix.imagemagick }} - sudo ./configure --prefix=/opt/imagemagick + sudo ./configure --prefix=~/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) @@ -69,7 +69,7 @@ jobs: tar -xzf imagick.tgz cd imagick-${{ matrix.imagick }} phpize - sudo ./configure --with-imagick=/opt/imagemagick + sudo ./configure --with-imagick=~/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) From d2b1cfe00f379b4ac200247cbc8d9f79c2dbc1b8 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:11:32 +0300 Subject: [PATCH 0285/1667] Update run-tests.yml add cache hit check. --- .github/workflows/run-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ece51ac49..76543ad36 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,6 +50,7 @@ jobs: key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} - name: Install ImageMagick + if: steps.cache-imagemagick.outputs.cache-hit != 'true' run: | curl -o /tmp/ImageMagick.tar.gz -sL https://www.imagemagick.org/download/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( From 3fbce7577f5f44bdfc7c3ce548e81715811fc3cd Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:19:57 +0300 Subject: [PATCH 0286/1667] Fix run-tests.yml update the configure path absolute. --- .github/workflows/run-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 76543ad36..740bf49e1 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -46,7 +46,7 @@ jobs: uses: actions/cache@v2 id: cache-imagemagick with: - path: ~/im/imagemagick-${{ matrix.imagemagick }} + path: ${HOME}/im/imagemagick-${{ matrix.imagemagick }} key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} - name: Install ImageMagick @@ -57,7 +57,7 @@ jobs: cd /tmp || exit 1 tar xf ImageMagick.tar.gz cd ImageMagick-${{ matrix.imagemagick }} - sudo ./configure --prefix=~/im/imagemagick-${{ matrix.imagemagick }} + sudo ./configure --prefix=${HOME}/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) @@ -70,7 +70,7 @@ jobs: tar -xzf imagick.tgz cd imagick-${{ matrix.imagick }} phpize - sudo ./configure --with-imagick=~/im/imagemagick-${{ matrix.imagemagick }} + sudo ./configure --with-imagick=${HOME}/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) From e2107fa351bdc8714e4e567469f8178928c57a72 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:28:25 +0300 Subject: [PATCH 0287/1667] Update run-tests.yml style file. --- .github/workflows/run-tests.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 740bf49e1..e456c2eee 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,13 +9,8 @@ jobs: fail-fast: false matrix: php: [ '8.0', '8.1' ] - imagemagick: [ - '6.9.12-43', - '7.1.0-28' - ] - imagick: [ - '3.7.0' - ] + imagemagick: [ '6.9.12-43', '7.1.0-28' ] + imagick: [ '3.7.0' ] stability: [ prefer-lowest, prefer-stable ] name: P${{ matrix.php }} - ${{ matrix.stability }} From 64183b8379f3c8845cb4fe31a2a7856908e0db54 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:44:31 +0300 Subject: [PATCH 0288/1667] Update run-tests.yml update cache imagemagick section. --- .github/workflows/run-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index e456c2eee..c2c6c7873 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -42,7 +42,8 @@ jobs: id: cache-imagemagick with: path: ${HOME}/im/imagemagick-${{ matrix.imagemagick }} - key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }} + key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }}-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }}- - name: Install ImageMagick if: steps.cache-imagemagick.outputs.cache-hit != 'true' From 4e63fb9eefd0ca60eafc5162384dcbab2a1a0e37 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:51:03 +0300 Subject: [PATCH 0289/1667] Update run-tests.yml improve prepare environment section. --- .github/workflows/run-tests.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c2c6c7873..61b57807c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,11 +30,7 @@ jobs: run: | sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev sudo apt-get update - sudo apt-get install -y libjpeg62-dev - sudo apt-get install -y libgif-dev - sudo apt-get install -y libtiff-dev - sudo apt-get install -y libpng-dev - sudo apt-get install -y libwebp-dev + sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev sudo apt-get install -y libmagickwand-dev - name: Cache ImageMagick From 7cf7ef5e9ac1443b8aad4b8ebb7417bc2a518d76 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:51:51 +0300 Subject: [PATCH 0290/1667] Fix run-tests.yml fix a typo in the prepare environment section. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 61b57807c..16124b989 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -26,7 +26,7 @@ jobs: extensions: mbstring, gd coverage: none - - name: Prepare environament for Imagemagick + - name: Prepare environment for Imagemagick run: | sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev sudo apt-get update From 976b9ae09af5487c1985011b51dfed0e4acd048d Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 11:52:38 +0300 Subject: [PATCH 0291/1667] Update run-tests.yml add check of the cache folder. --- .github/workflows/run-tests.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 16124b989..490705406 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -37,19 +37,25 @@ jobs: uses: actions/cache@v2 id: cache-imagemagick with: - path: ${HOME}/im/imagemagick-${{ matrix.imagemagick }} + path: /home/runner/im/imagemagick-${{ matrix.imagemagick }} key: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }}-${{ hashFiles('**/composer.json') }} restore-keys: ${{ runner.os }}-ImageMagick-${{ matrix.imagemagick }}- + - name: Check ImageMagick cache exists + uses: andstor/file-existence-action@v1 + id: cache-imagemagick-exists + with: + files: /home/runner/im/imagemagick-${{ matrix.imagemagick }} + - name: Install ImageMagick - if: steps.cache-imagemagick.outputs.cache-hit != 'true' + if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | curl -o /tmp/ImageMagick.tar.gz -sL https://www.imagemagick.org/download/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( cd /tmp || exit 1 tar xf ImageMagick.tar.gz cd ImageMagick-${{ matrix.imagemagick }} - sudo ./configure --prefix=${HOME}/im/imagemagick-${{ matrix.imagemagick }} + sudo ./configure --prefix=/home/runner/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) @@ -62,7 +68,7 @@ jobs: tar -xzf imagick.tgz cd imagick-${{ matrix.imagick }} phpize - sudo ./configure --with-imagick=${HOME}/im/imagemagick-${{ matrix.imagemagick }} + sudo ./configure --with-imagick=/home/runner/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) sudo make install ) From 7a8a02e8c885a602746d6130cffe641d9a2823ea Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 18:41:46 +0300 Subject: [PATCH 0292/1667] Update TestCase.php remove a redundant import. --- tests/TestCase.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index d9b53621b..8985b4e80 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests; use Intervention\Image\Interfaces\ColorInterface; -use PHPUnit\Framework\TestCase as PHPUnitTestCase; use Mockery\Adapter\Phpunit\MockeryTestCase; abstract class TestCase extends MockeryTestCase From e9655672aa624cb9128db5d9172f7ee450de24d6 Mon Sep 17 00:00:00 2001 From: Sergey Kudashev Date: Sat, 26 Mar 2022 18:43:26 +0300 Subject: [PATCH 0293/1667] Fix tests fix ModifierStackTest file name. --- tests/{ModiferStackTest.php => ModifierStackTest.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{ModiferStackTest.php => ModifierStackTest.php} (100%) diff --git a/tests/ModiferStackTest.php b/tests/ModifierStackTest.php similarity index 100% rename from tests/ModiferStackTest.php rename to tests/ModifierStackTest.php From f68c409526c9260312be4e0f90330af3af3fab6b Mon Sep 17 00:00:00 2001 From: PaolaRuby <79208489+PaolaRuby@users.noreply.github.com> Date: Wed, 30 Mar 2022 09:54:22 -0500 Subject: [PATCH 0294/1667] Update .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 880a0889b..50b2c2bd4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ * text=auto +/.github export-ignore /tests export-ignore /.gitattributes export-ignore /.gitignore export-ignore From 67d8ab1ab782e3ee768ecb4fd0b33f3323b17f7e Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 4 Apr 2022 08:30:36 +1200 Subject: [PATCH 0295/1667] Explicitly convert to integer --- src/Intervention/Image/Gd/Commands/ResizeCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/ResizeCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCommand.php index 382d9709e..e8ef7c753 100644 --- a/src/Intervention/Image/Gd/Commands/ResizeCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResizeCommand.php @@ -44,7 +44,7 @@ public function execute($image) protected function modify($image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) { // create new image - $modified = imagecreatetruecolor($dst_w, $dst_h); + $modified = imagecreatetruecolor(intval($dst_w), intval($dst_h)); // get current image $resource = $image->getCore(); @@ -70,8 +70,8 @@ protected function modify($image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h $dst_y, $src_x, $src_y, - $dst_w, - $dst_h, + intval($dst_w), + intval($dst_h), $src_w, $src_h ); From e78d2d75f38aa90dd53b48b5699ce5a9cb264309 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Apr 2022 19:20:13 +0200 Subject: [PATCH 0296/1667] Changed scheme of website address --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index b8f724509..29c17fe2a 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,14 @@ { "name": "intervention/image", "description": "PHP image manipulation", - "homepage": "http://image.intervention.io/", + "homepage": "https://image.intervention.io/", "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail"], "license": "MIT", "authors": [ { "name": "Oliver Vogel", "email": "oliver@olivervogel.com", - "homepage": "http://intervention.io/" + "homepage": "https://intervention.io/" } ], "require": { From 41363222f990cf2e128c7bdf0cde8aa2502493ff Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Apr 2022 19:37:59 +0200 Subject: [PATCH 0297/1667] Fixed Imagemagick download URI for tests --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 490705406..7fcdf2545 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,7 +50,7 @@ jobs: - name: Install ImageMagick if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | - curl -o /tmp/ImageMagick.tar.gz -sL https://www.imagemagick.org/download/ImageMagick-${{ matrix.imagemagick }}.tar.gz + curl -o /tmp/ImageMagick.tar.gz -sL https://download.imagemagick.org/ImageMagick/download/releases/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( cd /tmp || exit 1 tar xf ImageMagick.tar.gz From 977dbd0734a9772656748268c529b13714c25fff Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Apr 2022 19:50:26 +0200 Subject: [PATCH 0298/1667] Changed email address --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 29c17fe2a..933820c7c 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "authors": [ { "name": "Oliver Vogel", - "email": "oliver@olivervogel.com", + "email": "oliver@intervention.io", "homepage": "https://intervention.io/" } ], From fe280df77996c8b45c0ec8139a025b211b4bec96 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Apr 2022 19:59:39 +0200 Subject: [PATCH 0299/1667] Update .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 880a0889b..f5ee500b8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ * text=auto /tests export-ignore +/.github export-ignore /.gitattributes export-ignore /.gitignore export-ignore /.travis.yml export-ignore From 2287ed124c3e79c03ed65a1cc175732bd2dca5d3 Mon Sep 17 00:00:00 2001 From: Emanuele Coppola Date: Sat, 7 May 2022 02:21:18 +0200 Subject: [PATCH 0300/1667] Update phpdoc from radius to diameter --- src/Intervention/Image/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 7d5a59b7d..3b26ea2a9 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -13,7 +13,7 @@ * @method \Intervention\Image\Image brightness(int $level) Changes the brightness of the current image by the given level. Use values between -100 for min. brightness. 0 for no change and +100 for max. brightness. * @method \Intervention\Image\Image cache(\Closure $callback, int $lifetime = null, boolean $returnObj = false) Method to create a new cached image instance from a Closure callback. Pass a lifetime in minutes for the callback and decide whether you want to get an Intervention Image instance as return value or just receive the image stream. * @method \Intervention\Image\Image canvas(int $width, int $height, mixed $bgcolor = null) Factory method to create a new empty image instance with given width and height. You can define a background-color optionally. By default the canvas background is transparent. - * @method \Intervention\Image\Image circle(int $radius, int $x, int $y, \Closure $callback = null) Draw a circle at given x, y, coordinates with given radius. You can define the appearance of the circle by an optional closure callback. + * @method \Intervention\Image\Image circle(int $diameter, int $x, int $y, \Closure $callback = null) Draw a circle at given x, y, coordinates with given diameter. You can define the appearance of the circle by an optional closure callback. * @method \Intervention\Image\Image colorize(int $red, int $green, int $blue) Change the RGB color values of the current image on the given channels red, green and blue. The input values are normalized so you have to include parameters from 100 for maximum color value. 0 for no change and -100 to take out all the certain color on the image. * @method \Intervention\Image\Image contrast(int $level) Changes the contrast of the current image by the given level. Use values between -100 for min. contrast 0 for no change and +100 for max. contrast. * @method \Intervention\Image\Image crop(int $width, int $height, int $x = null, int $y = null) Cut out a rectangular part of the current image with given width and height. Define optional x,y coordinates to move the top-left corner of the cutout to a certain position. From 54d22c76a2976b12886231ea9063af2bd9d6e1f5 Mon Sep 17 00:00:00 2001 From: ibrainventures Date: Fri, 20 May 2022 19:52:01 +0200 Subject: [PATCH 0301/1667] [fix] Size.php php8.1 Implicit conversion from float --- src/Intervention/Image/Size.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php index f8b6dc8e7..b1f26e405 100644 --- a/src/Intervention/Image/Size.php +++ b/src/Intervention/Image/Size.php @@ -71,7 +71,7 @@ public function setPivot(Point $point) */ public function getWidth() { - return $this->width; + return intval($this->width); } /** @@ -81,7 +81,7 @@ public function getWidth() */ public function getHeight() { - return $this->height; + return intval($this->height); } /** From dcd370e1caea1377d89cb1c6d4c79bb1ac2904d3 Mon Sep 17 00:00:00 2001 From: ibrainventures Date: Fri, 20 May 2022 19:56:08 +0200 Subject: [PATCH 0302/1667] Update PixelateCommand.php --- src/Intervention/Image/Imagick/Commands/PixelateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php b/src/Intervention/Image/Imagick/Commands/PixelateCommand.php index 6fe794913..66a3205da 100644 --- a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php +++ b/src/Intervention/Image/Imagick/Commands/PixelateCommand.php @@ -19,7 +19,7 @@ public function execute($image) $width = $image->getWidth(); $height = $image->getHeight(); - $image->getCore()->scaleImage(max(1, ($width / $size)), max(1, ($height / $size))); + $image->getCore()->scaleImage(max(1, intval($width / $size)), max(1, intval($height / $size))); $image->getCore()->scaleImage($width, $height); return true; From 1cfbdf8d24f8720b73be5d6fe420e85448d940ad Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 May 2022 20:04:33 +0200 Subject: [PATCH 0303/1667] Add phpstan to dev-dependencies --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 933820c7c..e121a8510 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ }, "require-dev": { "phpunit/phpunit": "^9", - "mockery/mockery": "^1.4" + "mockery/mockery": "^1.4", + "phpstan/phpstan": "^1" }, "autoload": { "psr-4": { From 4847f2c5fcae1b91bd178b6e23b8423b1cfedd76 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 10:53:42 +0200 Subject: [PATCH 0304/1667] Change return type of AbstractDecoder::handle() --- .../Abstract/Decoders/AbstractDecoder.php | 8 +++---- .../Abstract/Decoders/AbstractDecoderTest.php | 22 ++++++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 937400415..953ba942c 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -15,16 +15,16 @@ public function __construct(protected ?AbstractDecoder $successor = null) // } - final public function handle($input): null|ImageInterface|ColorInterface + final public function handle($input): ImageInterface|ColorInterface { try { $decoded = $this->decode($input); } catch (DecoderException $e) { - if ($this->hasSuccessor()) { - return $this->successor->handle($input); + if (!$this->hasSuccessor()) { + $this->fail(); } - $this->fail(); + return $this->successor->handle($input); } return $decoded; diff --git a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php index 157c3474f..7d5673443 100644 --- a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php +++ b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php @@ -6,6 +6,7 @@ use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Tests\TestCase; use Mockery; @@ -16,31 +17,36 @@ class AbstractDecoderTest extends TestCase { public function testHandle(): void { + $result = Mockery::mock(ColorInterface::class); $decoder = Mockery::mock(AbstractDecoder::class)->makePartial(); - $decoder->shouldReceive('decode')->with('input string')->andReturn(null); + $decoder->shouldReceive('decode')->with('test input')->andReturn($result); - $decoder->handle('input string'); + $decoder->handle('test input'); } public function testHandleFail(): void { $decoder = Mockery::mock(AbstractDecoder::class, [])->makePartial()->shouldAllowMockingProtectedMethods(); - $decoder->shouldReceive('decode')->with('input string')->andThrow(DecoderException::class); + $decoder->shouldReceive('decode')->with('test input')->andThrow(DecoderException::class); $this->expectException(DecoderException::class); $this->expectExceptionMessage('Unable to decode given input.'); - $decoder->handle('input string'); + $decoder->handle('test input'); } public function testHandleFailWithSuccessor(): void { + $result = Mockery::mock(ColorInterface::class); $successor = Mockery::mock(AbstractDecoder::class)->makePartial(); - $successor->shouldReceive('decode')->with('input string')->andReturn(null); + $successor->shouldReceive('decode')->with('test input')->andReturn($result); - $decoder = Mockery::mock(AbstractDecoder::class, [$successor])->makePartial()->shouldAllowMockingProtectedMethods(); - $decoder->shouldReceive('decode')->with('input string')->andThrow(DecoderException::class); + $decoder = Mockery::mock( + AbstractDecoder::class, + [$successor] + )->makePartial()->shouldAllowMockingProtectedMethods(); + $decoder->shouldReceive('decode')->with('test input')->andThrow(DecoderException::class); - $decoder->handle('input string'); + $decoder->handle('test input'); } } From 3b8629c54ea214a127367543ea93a8de8448332a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 10:55:06 +0200 Subject: [PATCH 0305/1667] Add DecoderInterface implementation to AbstractDecoder --- src/Drivers/Abstract/Decoders/AbstractDecoder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 953ba942c..a40e02644 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -4,11 +4,12 @@ use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\AbstractType; -abstract class AbstractDecoder +abstract class AbstractDecoder implements DecoderInterface { public function __construct(protected ?AbstractDecoder $successor = null) { From 6dd434c2949bca912927a8dc960c1958061f88d8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 10:59:38 +0200 Subject: [PATCH 0306/1667] Add exception message to AbstractDecoder::fail() --- src/Drivers/Abstract/Decoders/AbstractDecoder.php | 6 +++--- tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index a40e02644..a1ee53eac 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -22,7 +22,7 @@ final public function handle($input): ImageInterface|ColorInterface $decoded = $this->decode($input); } catch (DecoderException $e) { if (!$this->hasSuccessor()) { - $this->fail(); + $this->fail($e->getMessage()); } return $this->successor->handle($input); @@ -36,9 +36,9 @@ protected function hasSuccessor(): bool return $this->successor !== null; } - protected function fail(): void + protected function fail(string $message = 'Unable to decode given input.'): void { - throw new DecoderException("Unable to decode given input."); + throw new DecoderException($message); } protected function inputType($input): AbstractType diff --git a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php index 7d5673443..b6d7f2615 100644 --- a/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php +++ b/tests/Drivers/Abstract/Decoders/AbstractDecoderTest.php @@ -30,7 +30,6 @@ public function testHandleFail(): void $decoder->shouldReceive('decode')->with('test input')->andThrow(DecoderException::class); $this->expectException(DecoderException::class); - $this->expectExceptionMessage('Unable to decode given input.'); $decoder->handle('test input'); } From fdb6de62984aa8d441c9021d10ced288fceef64e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:03:50 +0200 Subject: [PATCH 0307/1667] Add methods to SizeInterface --- src/Geometry/Size.php | 2 +- src/Interfaces/SizeInterface.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 797c15bc7..1d1e44e30 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -182,7 +182,7 @@ public function alignPivot(string $position, int $offset_x = 0, int $offset_y = return $this; } - public function alignPivotTo(Size $size, string $position): self + public function alignPivotTo(SizeInterface $size, string $position): self { $reference = new Size($size->getWidth(), $size->getHeight()); $reference->alignPivot($position); diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index fd641fbe1..92ba66747 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -14,4 +14,6 @@ public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; public function isPortrait(): bool; + public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; + public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; } From a9e5c57fb76193b765b3a24d0e0386dcba29723e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:12:28 +0200 Subject: [PATCH 0308/1667] Wrap code line --- src/Drivers/Abstract/AbstractImage.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2b1b4d6a5..7bf797613 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -14,7 +14,7 @@ use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; -abstract class AbstractImage +abstract class AbstractImage implements ImageInterface { use CanResolveDriverClass; use CanHandleInput; @@ -284,8 +284,12 @@ public function pad(int $width, int $height, $background = 'ffffff', string $pos ); } - public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface - { + public function padDown( + int $width, + int $height, + $background = 'ffffff', + string $position = 'center' + ): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\PadDownModifier', $width, $height, $background, $position) ); From 425d4515008153d2c70fbb2e704088135fc147ee Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:18:27 +0200 Subject: [PATCH 0309/1667] Adjust method signatures to interface --- src/Geometry/Size.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 1d1e44e30..d05d04179 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -209,29 +209,29 @@ public function getRelativePositionTo(Size $size): Point return new Point($x, $y); } - protected function getResizer(...$arguments): Resizer + protected function getResizer(?int $width = null, ?int $height = null): Resizer { - return new Resizer(...$arguments); + return new Resizer($width, $height); } - public function resize(...$arguments): self + public function resize(?int $width = null, ?int $height = null): self { - return $this->getResizer(...$arguments)->resize($this); + return $this->getResizer($width, $height)->resize($this); } - public function resizeDown(...$arguments): self + public function resizeDown(?int $width = null, ?int $height = null): self { - return $this->getResizer(...$arguments)->resizeDown($this); + return $this->getResizer($width, $height)->resizeDown($this); } - public function scale(...$arguments): self + public function scale(?int $width = null, ?int $height = null): self { - return $this->getResizer(...$arguments)->scale($this); + return $this->getResizer($width, $height)->scale($this); } - public function scaleDown(...$arguments): self + public function scaleDown(?int $width = null, ?int $height = null): self { - return $this->getResizer(...$arguments)->scaleDown($this); + return $this->getResizer($width, $height)->scaleDown($this); } public function cover(int $width, int $height): self From 72bd013b87fac2702f0c69701e30ef3ea70ed07f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:18:52 +0200 Subject: [PATCH 0310/1667] Remove unnecessary code --- src/Drivers/Gd/Modifiers/ResizeModifier.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 7646a22f8..512491f5d 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -2,12 +2,10 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Geometry\Resizer; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Traits\CanResizeGeometrically; class ResizeModifier implements ModifierInterface { From 35444b249e7481b940af0c6635f78792e4006819 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:25:08 +0200 Subject: [PATCH 0311/1667] Change signature of Frame::setCore --- src/Drivers/Abstract/AbstractFrame.php | 17 ++++++++++++++++- src/Drivers/Gd/Frame.php | 7 ------- src/Drivers/Imagick/Frame.php | 7 ------- src/Interfaces/FrameInterface.php | 1 + 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFrame.php b/src/Drivers/Abstract/AbstractFrame.php index c83c8af33..223a20726 100644 --- a/src/Drivers/Abstract/AbstractFrame.php +++ b/src/Drivers/Abstract/AbstractFrame.php @@ -4,5 +4,20 @@ abstract class AbstractFrame { - // + /** + * Set the frame core + * + * Input is losely typed and depending on the driver. + * Might be GdImage or Imagick but should be open to + * add more drivers. + * + * @param mixed $core + * @return AbstractFrame + */ + public function setCore($core): self + { + $this->core = $core; + + return $this; + } } diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index ea33a0369..6f43945df 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -27,13 +27,6 @@ public function getCore(): GdImage return $this->core; } - public function setCore(GdImage $core): self - { - $this->core = $core; - - return $this; - } - public function unsetCore(): void { unset($this->core); diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 632ad2c7f..94cc9c680 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -22,13 +22,6 @@ public function getCore(): Imagick return $this->core; } - public function setCore(Imagick $core): FrameInterface - { - $this->core = $core; - - return $this; - } - public function getSize(): SizeInterface { return new Size($this->core->getImageWidth(), $this->core->getImageHeight()); diff --git a/src/Interfaces/FrameInterface.php b/src/Interfaces/FrameInterface.php index 3c27aea9c..b2fd12307 100644 --- a/src/Interfaces/FrameInterface.php +++ b/src/Interfaces/FrameInterface.php @@ -6,6 +6,7 @@ interface FrameInterface { public function toImage(): ImageInterface; public function getCore(); + public function setCore($core): FrameInterface; public function getSize(): SizeInterface; public function getDelay(): float; public function setDelay(float $delay): FrameInterface; From f2a32bd349fdd949919f773a6dcb7e17ad6f5343 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 11:37:56 +0200 Subject: [PATCH 0312/1667] Fix type hinting --- src/Drivers/Abstract/AbstractFrame.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFrame.php b/src/Drivers/Abstract/AbstractFrame.php index 223a20726..8e89c3ef8 100644 --- a/src/Drivers/Abstract/AbstractFrame.php +++ b/src/Drivers/Abstract/AbstractFrame.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Drivers\Abstract; -abstract class AbstractFrame +use Intervention\Image\Interfaces\FrameInterface; + +abstract class AbstractFrame implements FrameInterface { /** * Set the frame core @@ -12,9 +14,9 @@ abstract class AbstractFrame * add more drivers. * * @param mixed $core - * @return AbstractFrame + * @return FrameInterface */ - public function setCore($core): self + public function setCore($core): FrameInterface { $this->core = $core; From a937e5a94d07635b3d98c3db9e3b735f53bb404d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 17:23:07 +0200 Subject: [PATCH 0313/1667] Add ColorInterface implementation to AbstractColor --- src/Drivers/Abstract/AbstractColor.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Drivers/Abstract/AbstractColor.php b/src/Drivers/Abstract/AbstractColor.php index f8ea601a3..fe6bdc0f0 100644 --- a/src/Drivers/Abstract/AbstractColor.php +++ b/src/Drivers/Abstract/AbstractColor.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Drivers\Abstract; -abstract class AbstractColor +use Intervention\Image\Interfaces\ColorInterface; + +abstract class AbstractColor implements ColorInterface { /** * Format color to hexadecimal color code From 190f46fc12b9b920f02c4932c13f7d18c574a3d9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 17:26:16 +0200 Subject: [PATCH 0314/1667] Remove AbstractFrame::class --- src/Drivers/Abstract/AbstractFrame.php | 25 ------------------------- src/Drivers/Gd/Frame.php | 10 ++++++++-- src/Drivers/Imagick/Frame.php | 10 ++++++++-- 3 files changed, 16 insertions(+), 29 deletions(-) delete mode 100644 src/Drivers/Abstract/AbstractFrame.php diff --git a/src/Drivers/Abstract/AbstractFrame.php b/src/Drivers/Abstract/AbstractFrame.php deleted file mode 100644 index 8e89c3ef8..000000000 --- a/src/Drivers/Abstract/AbstractFrame.php +++ /dev/null @@ -1,25 +0,0 @@ -core = $core; - - return $this; - } -} diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 6f43945df..885934c4b 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -4,13 +4,12 @@ use GdImage; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Abstract\AbstractFrame; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; -class Frame extends AbstractFrame implements FrameInterface +class Frame implements FrameInterface { public function __construct( protected GdImage $core, @@ -22,6 +21,13 @@ public function __construct( // } + public function setCore($core): FrameInterface + { + $this->core = $core; + + return $this; + } + public function getCore(): GdImage { return $this->core; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 94cc9c680..ef4bd57b7 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -4,19 +4,25 @@ use Imagick; use Intervention\Image\Collection; -use Intervention\Image\Drivers\Abstract\AbstractFrame; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; -class Frame extends AbstractFrame implements FrameInterface +class Frame implements FrameInterface { public function __construct(protected Imagick $core) { // } + public function setCore($core): FrameInterface + { + $this->core = $core; + + return $this; + } + public function getCore(): Imagick { return $this->core; From 58585c81f330df1bbeea6e832ba8a6087ace552b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 17:42:12 +0200 Subject: [PATCH 0315/1667] Replace method AbstractDecoder::fail() --- src/Drivers/Abstract/Decoders/AbstractDecoder.php | 7 +------ src/Drivers/Gd/Decoders/ArrayColorDecoder.php | 3 ++- src/Drivers/Gd/Decoders/Base64ImageDecoder.php | 3 ++- src/Drivers/Gd/Decoders/BinaryImageDecoder.php | 9 ++++----- src/Drivers/Gd/Decoders/DataUriImageDecoder.php | 6 +++--- src/Drivers/Gd/Decoders/FilePathImageDecoder.php | 7 ++++--- src/Drivers/Gd/Decoders/HexColorDecoder.php | 6 +++--- src/Drivers/Gd/Decoders/ImageObjectDecoder.php | 3 ++- src/Drivers/Gd/Decoders/RgbStringColorDecoder.php | 7 ++++--- src/Drivers/Gd/Decoders/TransparentColorDecoder.php | 5 ++--- src/Drivers/Imagick/Decoders/ArrayColorDecoder.php | 3 ++- src/Drivers/Imagick/Decoders/Base64ImageDecoder.php | 3 ++- src/Drivers/Imagick/Decoders/BinaryImageDecoder.php | 7 ++++--- src/Drivers/Imagick/Decoders/DataUriImageDecoder.php | 6 +++--- src/Drivers/Imagick/Decoders/FilePathImageDecoder.php | 7 ++++--- src/Drivers/Imagick/Decoders/HexColorDecoder.php | 6 +++--- src/Drivers/Imagick/Decoders/ImageObjectDecoder.php | 3 ++- src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php | 8 ++++---- src/Drivers/Imagick/Decoders/TransparentColorDecoder.php | 5 ++--- 19 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index a1ee53eac..61e350d21 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -22,7 +22,7 @@ final public function handle($input): ImageInterface|ColorInterface $decoded = $this->decode($input); } catch (DecoderException $e) { if (!$this->hasSuccessor()) { - $this->fail($e->getMessage()); + throw new DecoderException($e->getMessage()); } return $this->successor->handle($input); @@ -36,11 +36,6 @@ protected function hasSuccessor(): bool return $this->successor !== null; } - protected function fail(string $message = 'Unable to decode given input.'): void - { - throw new DecoderException($message); - } - protected function inputType($input): AbstractType { return MimeSniffer::createFromString($input)->getType(); diff --git a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php index 9f8d7a709..3d694e49c 100644 --- a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Gd/Decoders/ArrayColorDecoder.php @@ -4,6 +4,7 @@ use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -16,7 +17,7 @@ class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! $this->isValidColorArray($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (count($input) === 3) { diff --git a/src/Drivers/Gd/Decoders/Base64ImageDecoder.php b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php index fb22e3bad..ece551473 100644 --- a/src/Drivers/Gd/Decoders/Base64ImageDecoder.php +++ b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -14,7 +15,7 @@ class Base64ImageDecoder extends BinaryImageDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! $this->isValidBase64($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode(base64_decode($input)); diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index f4a36cf20..467493b0e 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -2,7 +2,6 @@ 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; @@ -10,21 +9,21 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageGif; use Intervention\Gif\Decoder as GifDecoder; use Intervention\Gif\Splitter as GifSplitter; +use Intervention\Image\Exceptions\DecoderException; class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (! $this->inputType($input)->isBinary()) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (is_a($this->inputType($input), ImageGif::class)) { @@ -34,7 +33,7 @@ public function decode($input): ImageInterface|ColorInterface $gd = @imagecreatefromstring($input); if ($gd === false) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (! imageistruecolor($gd)) { diff --git a/src/Drivers/Gd/Decoders/DataUriImageDecoder.php b/src/Drivers/Gd/Decoders/DataUriImageDecoder.php index b1c8f245f..32155781a 100644 --- a/src/Drivers/Gd/Decoders/DataUriImageDecoder.php +++ b/src/Drivers/Gd/Decoders/DataUriImageDecoder.php @@ -2,11 +2,11 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanDecodeDataUri; -use Intervention\MimeSniffer\MimeSniffer; class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface { @@ -15,13 +15,13 @@ class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } $uri = $this->decodeDataUri($input); if (! $uri->isValid()) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if ($uri->isBase64Encoded()) { diff --git a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php index 6448a645a..a826734b3 100644 --- a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php +++ b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; use Exception; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,15 +13,15 @@ class FilePathImageDecoder extends BinaryImageDecoder implements DecoderInterfac public function decode($input): ImageInterface|ColorInterface { if (! is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } try { if (! @is_file($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } } catch (Exception $e) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode(file_get_contents($input)); diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php index 2329fe50c..17134634d 100644 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,14 +12,14 @@ class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; $result = preg_match($pattern, $input, $matches); if ($result !== 1) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode([ diff --git a/src/Drivers/Gd/Decoders/ImageObjectDecoder.php b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php index 9dffd5b41..72851dd82 100644 --- a/src/Drivers/Gd/Decoders/ImageObjectDecoder.php +++ b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,7 +13,7 @@ class ImageObjectDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! is_a($input, ImageInterface::class)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return $input; diff --git a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php index 53ded7c5c..300dd4a1b 100644 --- a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -11,11 +12,11 @@ class RgbStringColorDecoder extends ArrayColorDecoder implements DecoderInterfac public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (substr($input, 0, 3) !== 'rgb') { - $this->fail(); + throw new DecoderException('Unable to decode input'); } // rgb string like rgb(102, 200, 0) @@ -30,6 +31,6 @@ public function decode($input): ImageInterface|ColorInterface return parent::decode([$matches['r'], $matches['g'], $matches['b'], $matches['a']]); } - $this->fail(); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php index dd77d055d..64f4adfcd 100644 --- a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php @@ -2,18 +2,17 @@ namespace Intervention\Image\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColors; class TransparentColorDecoder extends ArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { if (! is_string($input) || strtolower($input) !== 'transparent') { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode([0, 0, 0, 0]); diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php index aee0df579..12f8aaa22 100644 --- a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php @@ -5,6 +5,7 @@ use ImagickPixel; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -17,7 +18,7 @@ class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! $this->isValidColorArray($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (count($input) === 3) { diff --git a/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php index 46d344303..fe30c4fb4 100644 --- a/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -14,7 +15,7 @@ class Base64ImageDecoder extends BinaryImageDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! $this->isValidBase64($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode(base64_decode($input)); diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index f09a50ffe..7a8b4e02e 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -7,6 +7,7 @@ use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -16,11 +17,11 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } - if (! $this->inputType($input)->isBinary()) { - $this->fail(); + if (!$this->inputType($input)->isBinary()) { + throw new DecoderException('Unable to decode input'); } $imagick = new Imagick(); diff --git a/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php b/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php index 9f80b117f..1dcff2246 100644 --- a/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/DataUriImageDecoder.php @@ -2,11 +2,11 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanDecodeDataUri; -use Intervention\MimeSniffer\MimeSniffer; class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface { @@ -15,13 +15,13 @@ class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } $uri = $this->decodeDataUri($input); if (! $uri->isValid()) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if ($uri->isBase64Encoded()) { diff --git a/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php index a4fe1d306..f1d7346e3 100644 --- a/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; use Exception; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,15 +13,15 @@ class FilePathImageDecoder extends BinaryImageDecoder implements DecoderInterfac public function decode($input): ImageInterface|ColorInterface { if (! is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } try { if (! @is_file($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } } catch (Exception $e) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode(file_get_contents($input)); diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php index 2eafd126b..84526d162 100644 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,14 +12,14 @@ class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; $result = preg_match($pattern, $input, $matches); if ($result !== 1) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode([ diff --git a/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php index 72318e2ab..9f72634fa 100644 --- a/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php +++ b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -12,7 +13,7 @@ class ImageObjectDecoder extends AbstractDecoder implements DecoderInterface public function decode($input): ImageInterface|ColorInterface { if (! is_a($input, ImageInterface::class)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return $input; diff --git a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php index 4e9c9fa24..7b036033f 100644 --- a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php @@ -6,27 +6,27 @@ use ImagickPixelException; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColors; class RgbStringColorDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } if (substr($input, 0, 3) !== 'rgb') { - $this->fail(); + throw new DecoderException('Unable to decode input'); } try { $pixel = new ImagickPixel($input); } catch (ImagickPixelException $e) { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return new Color($pixel); diff --git a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php index 5d3e3153f..b93ef7063 100644 --- a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php @@ -2,18 +2,17 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanValidateColors; class TransparentColorDecoder extends ArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { if (! is_string($input) || strtolower($input) !== 'transparent') { - $this->fail(); + throw new DecoderException('Unable to decode input'); } return parent::decode([0, 0, 0, 0]); From f3b4092fac3ab1a885f3a0ed94d95573dc661c6c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 18:50:14 +0200 Subject: [PATCH 0316/1667] Wrap code line --- src/Traits/CanDecodeDataUri.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Traits/CanDecodeDataUri.php b/src/Traits/CanDecodeDataUri.php index 350f4096d..b19f06cd6 100644 --- a/src/Traits/CanDecodeDataUri.php +++ b/src/Traits/CanDecodeDataUri.php @@ -11,7 +11,9 @@ trait CanDecodeDataUri */ protected function decodeDataUri($value): object { - $pattern = "/^data:(?P\w+\/[-+.\w]+)?(?P(;[-\w]+=[-\w]+)*)(?P;base64)?,(?P.*)/"; + $pattern = "/^data:(?P\w+\/[-+.\w]+)?" . + "(?P(;[-\w]+=[-\w]+)*)(?P;base64)?,(?P.*)/"; + $result = preg_match($pattern, $value, $matches); return new class ($matches, $result) From bdcf6d490c90fb4773c3012b875b93288f33d4ca Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 18:52:52 +0200 Subject: [PATCH 0317/1667] Reformat code --- src/Drivers/Gd/Encoders/GifEncoder.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 9438e8a81..31227a15a 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -25,8 +25,13 @@ public function encode(ImageInterface $image): EncodedImage protected function encodeAnimated($image): EncodedImage { - $builder = GifBuilder::canvas($image->getWidth(), $image->getHeight(), $image->getLoops()); - foreach ($image as $key => $frame) { + $builder = GifBuilder::canvas( + $image->getWidth(), + $image->getHeight(), + $image->getLoops() + ); + + foreach ($image as $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); } From 38e01fc33982e2a43c1e440ab27db70c8a98070d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 18:53:34 +0200 Subject: [PATCH 0318/1667] Add EncoderInterface implementation to AbstractDecoder --- src/Drivers/Abstract/Encoders/AbstractEncoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Abstract/Encoders/AbstractEncoder.php b/src/Drivers/Abstract/Encoders/AbstractEncoder.php index 726093eb3..36a0c073b 100644 --- a/src/Drivers/Abstract/Encoders/AbstractEncoder.php +++ b/src/Drivers/Abstract/Encoders/AbstractEncoder.php @@ -4,7 +4,7 @@ use Intervention\Image\Interfaces\EncoderInterface; -abstract class AbstractEncoder +abstract class AbstractEncoder implements EncoderInterface { protected $quality; From 2de45528dd42e6844bae4b1715b4ee1ba076beb3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 18:59:53 +0200 Subject: [PATCH 0319/1667] Add method to SizeInterface --- src/Interfaces/SizeInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index 92ba66747..ba8fef44c 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -16,4 +16,5 @@ public function isLandscape(): bool; public function isPortrait(): bool; public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; + public function contain(int $width, int $height): SizeInterface; } From 3da3bad2ab4b31050667cc1c87727d7ae67bdd99 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 19:03:49 +0200 Subject: [PATCH 0320/1667] Add method to SizeInterface --- src/Geometry/Size.php | 2 +- src/Interfaces/SizeInterface.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index d05d04179..9bffaf07d 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -201,7 +201,7 @@ public function alignPivotTo(SizeInterface $size, string $position): self * @param Size $size * @return Point */ - public function getRelativePositionTo(Size $size): Point + public function getRelativePositionTo(SizeInterface $size): PointInterface { $x = $this->getPivot()->getX() - $size->getPivot()->getX(); $y = $this->getPivot()->getY() - $size->getPivot()->getY(); diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index ba8fef44c..da4a6821c 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -17,4 +17,5 @@ public function isPortrait(): bool; public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; public function contain(int $width, int $height): SizeInterface; + public function getRelativePositionTo(SizeInterface $size): PointInterface; } From a796553b6d4ef84c18f8d9a451e97d99602a7ca7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 19:04:00 +0200 Subject: [PATCH 0321/1667] Remove unused code --- src/Drivers/Gd/Modifiers/FillModifier.php | 2 +- src/Drivers/Gd/Modifiers/FitModifier.php | 3 --- src/Drivers/Gd/Modifiers/PadModifier.php | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 70e5fe2ce..0f7473428 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -17,7 +17,7 @@ public function __construct(protected ColorInterface $color, protected ?Point $p public function apply(ImageInterface $image): ImageInterface { - foreach ($image as $key => $frame) { + foreach ($image as $frame) { if ($this->hasPosition()) { $this->floodFillWithColor($frame); } else { diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index de0e9725d..cf55b8c0a 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -3,13 +3,10 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractFitModifier; -use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Traits\CanHandleInput; -use Intervention\Image\Traits\CanResizeGeometrically; class FitModifier extends AbstractFitModifier implements ModifierInterface { diff --git a/src/Drivers/Gd/Modifiers/PadModifier.php b/src/Drivers/Gd/Modifiers/PadModifier.php index 630bb2841..a8e78445d 100644 --- a/src/Drivers/Gd/Modifiers/PadModifier.php +++ b/src/Drivers/Gd/Modifiers/PadModifier.php @@ -9,7 +9,6 @@ use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; -use Intervention\Image\Traits\CanResizeGeometrically; class PadModifier extends AbstractPadModifier implements ModifierInterface { From 5397914d69c032201a391b1bfb000f52276aa87a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 19:08:22 +0200 Subject: [PATCH 0322/1667] Add missing methods to SizeInterface --- src/Geometry/Size.php | 18 +++++++++--------- src/Interfaces/SizeInterface.php | 8 ++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 9bffaf07d..048862856 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -50,7 +50,7 @@ public function getPivot(): PointInterface return $this->pivot; } - public function setPivot(PointInterface $pivot): self + public function setPivot(PointInterface $pivot): SizeInterface { $this->pivot = $pivot; @@ -104,7 +104,7 @@ public function isPortrait(): bool * @param int $offset_y * @return Size */ - public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): self + public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface { switch (strtolower($position)) { case 'top': @@ -182,7 +182,7 @@ public function alignPivot(string $position, int $offset_x = 0, int $offset_y = return $this; } - public function alignPivotTo(SizeInterface $size, string $position): self + public function alignPivotTo(SizeInterface $size, string $position): SizeInterface { $reference = new Size($size->getWidth(), $size->getHeight()); $reference->alignPivot($position); @@ -214,32 +214,32 @@ protected function getResizer(?int $width = null, ?int $height = null): Resizer return new Resizer($width, $height); } - public function resize(?int $width = null, ?int $height = null): self + public function resize(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->resize($this); } - public function resizeDown(?int $width = null, ?int $height = null): self + public function resizeDown(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->resizeDown($this); } - public function scale(?int $width = null, ?int $height = null): self + public function scale(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->scale($this); } - public function scaleDown(?int $width = null, ?int $height = null): self + public function scaleDown(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->scaleDown($this); } - public function cover(int $width, int $height): self + public function cover(int $width, int $height): SizeInterface { return $this->getResizer($width, $height)->cover($this); } - public function contain(int $width, int $height): self + public function contain(int $width, int $height): SizeInterface { return $this->getResizer($width, $height)->contain($this); } diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index da4a6821c..7aaef57d3 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -9,13 +9,17 @@ public function getHeight(): int; public function getPivot(): PointInterface; public function setWidth(int $width): SizeInterface; public function setHeight(int $height): SizeInterface; - public function resize(?int $width = null, ?int $height = null): SizeInterface; public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; public function isPortrait(): bool; public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; - public function contain(int $width, int $height): SizeInterface; public function getRelativePositionTo(SizeInterface $size): PointInterface; + public function resize(?int $width = null, ?int $height = null): SizeInterface; + public function resizeDown(?int $width = null, ?int $height = null): SizeInterface; + public function scale(?int $width = null, ?int $height = null): SizeInterface; + public function scaleDown(?int $width = null, ?int $height = null): SizeInterface; + public function cover(int $width, int $height): SizeInterface; + public function contain(int $width, int $height): SizeInterface; } From 7433c123d53251dbd75b1c876fd5b405e3a2ec96 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 May 2022 19:16:33 +0200 Subject: [PATCH 0323/1667] Remove unused code --- src/Drivers/Imagick/Modifiers/FillModifier.php | 4 ++-- src/Drivers/Imagick/Modifiers/FitModifier.php | 3 --- src/Drivers/Imagick/Modifiers/PadModifier.php | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index ddcf19df6..a6cc51a4d 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -4,16 +4,16 @@ use Imagick; use ImagickDraw; +use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Geometry\Point; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { public function __construct( - protected ColorInterface $color, + protected Color $color, protected ?Point $position = null ) { // diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php index 5b34f7029..f5465c5f8 100644 --- a/src/Drivers/Imagick/Modifiers/FitModifier.php +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -3,11 +3,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractFitModifier; -use Intervention\Image\Geometry\Size; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Interfaces\SizeInterface; class FitModifier extends AbstractFitModifier implements ModifierInterface { diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index 414efc975..94bee08ea 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -6,7 +6,6 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; From fda458ff959ce5f05ac57e244a7c66ecd702b1ba Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 24 May 2022 19:31:53 +0200 Subject: [PATCH 0324/1667] Add modifier TextWriter --- src/Drivers/Abstract/AbstractFont.php | 108 +++++++++++++++++++ src/Drivers/Abstract/AbstractImage.php | 9 ++ src/Drivers/Gd/Font.php | 77 +++++++++++++ src/Drivers/Gd/Modifiers/TextWriter.php | 80 ++++++++++++++ src/Drivers/Imagick/Font.php | 73 +++++++++++++ src/Drivers/Imagick/Modifiers/TextWriter.php | 56 ++++++++++ src/Exceptions/FontException.php | 8 ++ src/Interfaces/FontInterface.php | 23 ++++ 8 files changed, 434 insertions(+) create mode 100644 src/Drivers/Abstract/AbstractFont.php create mode 100644 src/Drivers/Gd/Font.php create mode 100644 src/Drivers/Gd/Modifiers/TextWriter.php create mode 100644 src/Drivers/Imagick/Font.php create mode 100644 src/Drivers/Imagick/Modifiers/TextWriter.php create mode 100644 src/Exceptions/FontException.php create mode 100644 src/Interfaces/FontInterface.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php new file mode 100644 index 000000000..6fee3a603 --- /dev/null +++ b/src/Drivers/Abstract/AbstractFont.php @@ -0,0 +1,108 @@ +text; + } + + public function size(float $size): self + { + $this->size = $size; + + return $this; + } + + public function getSize(): float + { + return $this->size; + } + + public function angle(float $angle): self + { + $this->angle = $angle; + + return $this; + } + + public function getAngle(): float + { + return $this->angle; + } + + public function filename(string $filename): self + { + $this->filename = $filename; + + return $this; + } + + public function getFilename(): string + { + return $this->filename; + } + + public function hasFilename(): bool + { + return is_file($this->filename); + } + + public function color($color): self + { + $this->color = $color; + + return $this; + } + + public function getColor(): ?ColorInterface + { + return $this->handleInput($this->color); + } + + public function align(string $align): self + { + $this->align = $align; + + return $this; + } + + public function getValign(): string + { + return $this->valign; + } + + public function valign(string $valign): self + { + $this->valign = $valign; + + return $this; + } + + public function getAlign(): string + { + return $this->align; + } +} diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7bf797613..50e6fb6ac 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -13,6 +13,7 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Interfaces\FontInterface; abstract class AbstractImage implements ImageInterface { @@ -235,6 +236,14 @@ public function pickColors(int $x, int $y): Collection return $colors; } + public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface + { + $font = $this->resolveDriverClass('Font', $text, $init); + $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php new file mode 100644 index 000000000..90a15b0c9 --- /dev/null +++ b/src/Drivers/Gd/Font.php @@ -0,0 +1,77 @@ +hasFilename()) { + // calculate box size from gd font + $box = new Size(0, 0); + $chars = mb_strlen($this->getText()); + if ($chars > 0) { + $box->setWidth($chars * $this->getGdFontWidth()); + $box->setHeight($this->getGdFontHeight()); + } + return $box; + } + + // calculate box size from font file + $box = imageftbbox( + $this->getSize(), + $this->getAngle(), + $this->getFilename(), + $this->getText() + ); + + return new Size(abs($box[0] - $box[2]), abs($box[1] - $box[7])); + } + + public function getGdFont(): int + { + if (is_numeric($this->filename)) { + return $this->filename; + } + + return 1; + } + + protected function getGdFontWidth(): int + { + return $this->getGdFont() + 4; + } + + protected function getGdFontHeight(): int + { + switch ($this->getGdFont()) { + case 1: + return 8; + + case 2: + return 14; + + case 3: + return 14; + + case 4: + return 16; + + case 5: + return 16; + } + } +} diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php new file mode 100644 index 000000000..6c6277c3d --- /dev/null +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -0,0 +1,80 @@ +getAlignedPosition(); + + foreach ($image as $frame) { + if ($this->font->hasFilename()) { + imagettftext( + $frame->getCore(), + $this->font->getSize(), + $this->font->getAngle(), + $position->getX(), + $position->getY(), + $this->font->getColor()->toInt(), + $this->font->getFilename(), + $this->font->getText() + ); + } else { + imagestring( + $frame->getCore(), + $this->font->getGdFont(), + $position->getX(), + $position->getY(), + $this->font->getText(), + $this->font->getColor()->toInt() + ); + } + } + + return $image; + } + + protected function getAlignedPosition(): Point + { + $position = $this->position; + $box = $this->font->getBoxSize(); + + // adjust x pos + switch ($this->font->getAlign()) { + case 'center': + $position->setX($position->getX() - round($box->getWidth() / 2)); + break; + + case 'right': + $position->setX($position->getX() - $box->getWidth()); + break; + } + + // adjust y pos + switch ($this->font->getValign()) { + case 'top': + $position->setY($position->getY() + $box->getHeight()); + break; + + case 'middle': + case 'center': + $position->setY($position->getY() + round($box->getHeight() / 2)); + break; + } + + return $position; + } +} diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php new file mode 100644 index 000000000..df32d284a --- /dev/null +++ b/src/Drivers/Imagick/Font.php @@ -0,0 +1,73 @@ +hasFilename()) { + throw new FontException('No font file.'); + } + + $draw = new ImagickDraw(); + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + $draw->setFont($this->getFilename()); + $draw->setFontSize($this->getSize()); + $draw->setFillColor($this->getColor()->getPixel()); + $draw->setTextAlignment($this->getImagickAlign()); + + return $draw; + } + + public function getAngle(): float + { + return parent::getAngle() * (-1); + } + + public function getImagickAlign(): int + { + switch (strtolower($this->getAlign())) { + case 'center': + return Imagick::ALIGN_CENTER; + break; + + case 'right': + return Imagick::ALIGN_RIGHT; + break; + } + + return Imagick::ALIGN_LEFT; + } + + /** + * Calculate box size of current font + * + * @return Size + */ + public function getBoxSize(): Size + { + $foo = null; + // no text - no box size + if (mb_strlen($this->getText()) === 0) { + return new Size(0, 0); + } + + $dimensions = (new Imagick())->queryFontMetrics( + $this->toImagickDraw(), + $this->getText() + ); + + return new Size( + intval(abs($dimensions['textWidth'])), + intval(abs($dimensions['textHeight'])) + ); + } +} diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php new file mode 100644 index 000000000..52c6b7be7 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -0,0 +1,56 @@ +font->toImagickDraw(); + $position = $this->getAlignedPosition(); + + foreach ($image as $frame) { + $frame->getCore()->annotateImage( + $draw, + $position->getX(), + $position->getY(), + $this->font->getAngle(), + $this->font->getText() + ); + } + + return $image; + } + + protected function getAlignedPosition(): Point + { + $position = $this->position; + $box = $this->font->getBoxSize(); + + // adjust y pos + switch ($this->font->getValign()) { + case 'top': + $position->setY($position->getY() + $box->getHeight()); + break; + + case 'middle': + case 'center': + $position->setY($position->getY() + round($box->getHeight() / 2)); + break; + } + + return $position; + } +} diff --git a/src/Exceptions/FontException.php b/src/Exceptions/FontException.php new file mode 100644 index 000000000..be32f1779 --- /dev/null +++ b/src/Exceptions/FontException.php @@ -0,0 +1,8 @@ + Date: Tue, 7 Jun 2022 19:11:41 +0100 Subject: [PATCH 0325/1667] Remove a php 8.1 deprecation warning Change implicit float->int conversion with use of round(x,0) --- src/Intervention/Image/Gd/Commands/BrightnessCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php b/src/Intervention/Image/Gd/Commands/BrightnessCommand.php index 4e48464b1..f45b77f6e 100644 --- a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php +++ b/src/Intervention/Image/Gd/Commands/BrightnessCommand.php @@ -16,6 +16,6 @@ public function execute($image) { $level = $this->argument(0)->between(-100, 100)->required()->value(); - return imagefilter($image->getCore(), IMG_FILTER_BRIGHTNESS, ($level * 2.55)); + return imagefilter($image->getCore(), round(IMG_FILTER_BRIGHTNESS,0), ($level * 2.55)); } } From 27eb784a62bed119b4e18d83bedfb9460e62eddd Mon Sep 17 00:00:00 2001 From: JCOGS Design <13821249+jcogs-design@users.noreply.github.com> Date: Tue, 7 Jun 2022 19:17:08 +0100 Subject: [PATCH 0326/1667] Fix typo --- src/Intervention/Image/Gd/Commands/BrightnessCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php b/src/Intervention/Image/Gd/Commands/BrightnessCommand.php index f45b77f6e..a164ad3e1 100644 --- a/src/Intervention/Image/Gd/Commands/BrightnessCommand.php +++ b/src/Intervention/Image/Gd/Commands/BrightnessCommand.php @@ -16,6 +16,6 @@ public function execute($image) { $level = $this->argument(0)->between(-100, 100)->required()->value(); - return imagefilter($image->getCore(), round(IMG_FILTER_BRIGHTNESS,0), ($level * 2.55)); + return imagefilter($image->getCore(), IMG_FILTER_BRIGHTNESS, round($level * 2.55,0)); } } From a8fa90cde96e8e637edaf1e4b674d7d8d3db9eed Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 15 Jun 2022 10:00:33 +0200 Subject: [PATCH 0327/1667] Add BinaryImageDecoderTest for Imagick driver --- .../Decoders/BinaryImageDecoderTest.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php diff --git a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php new file mode 100644 index 000000000..455bdcc7b --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php @@ -0,0 +1,40 @@ +decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); + $this->assertCount(1, $image); + } + + public function testDecodeGif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); + $this->assertCount(1, $image); + } + + public function testDecodeAnimatedGif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(75, $image->getWidth()); + $this->assertEquals(50, $image->getHeight()); + $this->assertCount(4, $image); + } +} From ed12d374ff9a5b09199c23c67c3beed86df802c2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 16 Jun 2022 09:18:08 +0200 Subject: [PATCH 0328/1667] Change signatures of ImageInterface --- src/Drivers/Abstract/AbstractImage.php | 7 ++++--- src/Interfaces/ImageInterface.php | 7 +++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7bf797613..530e52911 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -6,6 +6,7 @@ use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -24,12 +25,12 @@ public function __construct(protected Collection $frames, protected $loops = 0) // } - public function getIterator(): Collection + public function getIterator(): CollectionInterface { return $this->frames; } - public function getFrames(): Collection + public function getFrames(): CollectionInterface { return $this->frames; } @@ -225,7 +226,7 @@ public function sharpen(int $amount = 10): ImageInterface ); } - public function pickColors(int $x, int $y): Collection + public function pickColors(int $x, int $y): CollectionInterface { $colors = new Collection(); foreach ($this->getFrames() as $key => $frame) { diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 182a76dc8..5f16c4d7d 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -2,13 +2,12 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Collection; use Intervention\Image\EncodedImage; interface ImageInterface { - public function getIterator(): Collection; - public function getFrames(): Collection; + public function getIterator(): CollectionInterface; + public function getFrames(): CollectionInterface; public function getFrame(int $key = 0): ?FrameInterface; public function addFrame(FrameInterface $frame): ImageInterface; public function setLoops(int $count): ImageInterface; @@ -21,7 +20,7 @@ public function toJpeg(int $quality = 75): EncodedImage; public function toWebp(int $quality = 75): EncodedImage; public function toGif(): EncodedImage; public function toPng(): EncodedImage; - public function pickColors(int $x, int $y): Collection; + public function pickColors(int $x, int $y): CollectionInterface; public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function greyscale(): ImageInterface; public function blur(int $amount = 5): ImageInterface; From a7eb80f4f1cc7d7576711409d1c8bb99f43906f6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 18 Jun 2022 16:02:11 +0200 Subject: [PATCH 0329/1667] Add Collection::get() method --- src/Collection.php | 5 +++++ src/Interfaces/CollectionInterface.php | 1 + tests/CollectionTest.php | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index 90fbefaf1..e1e37a6ce 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -109,6 +109,11 @@ public function get(int $key = 0, $default = null) return $this->items[$key]; } + public function has(int $key): bool + { + return array_key_exists($key, $this->items); + } + public function query(string $query, $default = null) { $items = $this->getItemsFlat(); diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php index 5c63e5a7d..d51cee76d 100644 --- a/src/Interfaces/CollectionInterface.php +++ b/src/Interfaces/CollectionInterface.php @@ -6,6 +6,7 @@ interface CollectionInterface { public function push($item): CollectionInterface; public function get(int $key); + public function has(int $key); public function first(); public function last(); public function count(): int; diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index b4a6738b7..9703486ef 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -86,6 +86,14 @@ public function testGet() $this->assertEquals('test', $collection->get(3, 'test')); } + public function testHas(): void + { + $collection = new Collection(['foo', 'bar']); + $this->assertTrue($collection->has(0)); + $this->assertTrue($collection->has(1)); + $this->assertFalse($collection->has(2)); + } + public function testToArray() { $collection = new Collection(['foo', 'bar', 'baz']); From dc3253feefc0a2fdf245639962fa9b837fa88fd8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 18 Jun 2022 16:07:36 +0200 Subject: [PATCH 0330/1667] Add default attribute to CollectionInterface --- src/Interfaces/CollectionInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php index d51cee76d..fc87d6268 100644 --- a/src/Interfaces/CollectionInterface.php +++ b/src/Interfaces/CollectionInterface.php @@ -5,7 +5,7 @@ interface CollectionInterface { public function push($item): CollectionInterface; - public function get(int $key); + public function get(int $key, $default = null); public function has(int $key); public function first(); public function last(); From 90ed724cb582598cad3fdcb2a37a2750e9ac846c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 20 Jun 2022 16:33:38 +0200 Subject: [PATCH 0331/1667] Refactor storage of Imagick core, remove method Imagick core image was disassembled in the decoding process. This made the whole object not very memory efficient. This fixes the issue by keeping the original Imagick object in the Intervention Image object. Also the Image::getFrames() method was removed. Users should use iteration to access frames. --- src/Collection.php | 5 +- src/Drivers/Abstract/AbstractImage.php | 47 +------- src/Drivers/Gd/Encoders/GifEncoder.php | 2 +- src/Drivers/Gd/Encoders/JpegEncoder.php | 2 +- src/Drivers/Gd/Encoders/PngEncoder.php | 2 +- src/Drivers/Gd/Encoders/WebpEncoder.php | 2 +- src/Drivers/Gd/Image.php | 47 ++++++++ .../Imagick/Decoders/BinaryImageDecoder.php | 10 +- src/Drivers/Imagick/Encoders/GifEncoder.php | 2 +- src/Drivers/Imagick/Encoders/JpegEncoder.php | 2 +- src/Drivers/Imagick/Encoders/PngEncoder.php | 2 +- src/Drivers/Imagick/Encoders/WebpEncoder.php | 2 +- src/Drivers/Imagick/Frame.php | 3 +- src/Drivers/Imagick/Image.php | 109 +++++++++++++++++- src/Drivers/Imagick/ImageFactory.php | 8 +- src/Interfaces/CollectionInterface.php | 4 +- src/Interfaces/ImageInterface.php | 6 +- tests/Drivers/Abstract/AbstractImageTest.php | 58 +--------- tests/Drivers/Gd/ImageTest.php | 16 +-- .../Imagick/Encoders/GifEncoderTest.php | 32 ++--- .../Imagick/Encoders/JpegEncoderTest.php | 5 +- .../Imagick/Encoders/PngEncoderTest.php | 5 +- .../Imagick/Encoders/WebpEncoderTest.php | 5 +- tests/Drivers/Imagick/ImageTest.php | 41 ++++++- 24 files changed, 242 insertions(+), 175 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index e1e37a6ce..5434f83bc 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -6,6 +6,7 @@ use Intervention\Image\Interfaces\CollectionInterface; use ArrayIterator; use Countable; +use Traversable; use IteratorAggregate; use RecursiveIteratorIterator; use RecursiveArrayIterator; @@ -31,9 +32,9 @@ public static function create(array $items = []): self /** * Returns Iterator * - * @return \Traversable + * @return Traversable */ - public function getIterator(): \Traversable + public function getIterator(): Traversable { return new ArrayIterator($this->items); } diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 530e52911..29fd8f0ad 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -8,7 +8,6 @@ use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\EncoderInterface; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -20,45 +19,6 @@ abstract class AbstractImage implements ImageInterface use CanResolveDriverClass; use CanHandleInput; - public function __construct(protected Collection $frames, protected $loops = 0) - { - // - } - - public function getIterator(): CollectionInterface - { - return $this->frames; - } - - public function getFrames(): CollectionInterface - { - 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); - - return $this; - } - - public function setLoops(int $count): ImageInterface - { - $this->loops = $count; - - return $this; - } - - public function getLoops(): int - { - return $this->loops; - } - public function getSize(): SizeInterface { return new Size($this->getWidth(), $this->getHeight()); @@ -69,11 +29,6 @@ public function size(): SizeInterface return $this->getSize(); } - public function isAnimated(): bool - { - return $this->getFrames()->count() > 1; - } - public function modify(ModifierInterface $modifier): ImageInterface { return $modifier->apply($this); @@ -229,7 +184,7 @@ public function sharpen(int $amount = 10): ImageInterface public function pickColors(int $x, int $y): CollectionInterface { $colors = new Collection(); - foreach ($this->getFrames() as $key => $frame) { + foreach ($this as $key => $frame) { $colors->push($this->pickColor($x, $y, $key)); } diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index 31227a15a..159cc2cda 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -17,7 +17,7 @@ public function encode(ImageInterface $image): EncodedImage } $data = $this->getBuffered(function () use ($image) { - imagegif($image->getFrames()->first()->getCore()); + imagegif($image->getFrame()->getCore()); }); return new EncodedImage($data, 'image/gif'); diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php index 09e695e79..2a8d41da7 100644 --- a/src/Drivers/Gd/Encoders/JpegEncoder.php +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -17,7 +17,7 @@ public function __construct(int $quality) public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { - imagejpeg($image->getFrames()->first()->getCore(), null, $this->quality); + imagejpeg($image->getFrame()->getCore(), null, $this->quality); }); return new EncodedImage($data, 'image/jpeg'); diff --git a/src/Drivers/Gd/Encoders/PngEncoder.php b/src/Drivers/Gd/Encoders/PngEncoder.php index bc578b47e..4369ef143 100644 --- a/src/Drivers/Gd/Encoders/PngEncoder.php +++ b/src/Drivers/Gd/Encoders/PngEncoder.php @@ -12,7 +12,7 @@ class PngEncoder extends AbstractEncoder implements EncoderInterface public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { - imagepng($image->getFrames()->first()->getCore(), null, -1); + imagepng($image->getFrame()->getCore(), null, -1); }); return new EncodedImage($data, 'image/png'); diff --git a/src/Drivers/Gd/Encoders/WebpEncoder.php b/src/Drivers/Gd/Encoders/WebpEncoder.php index 748db2a2f..581358f07 100644 --- a/src/Drivers/Gd/Encoders/WebpEncoder.php +++ b/src/Drivers/Gd/Encoders/WebpEncoder.php @@ -17,7 +17,7 @@ public function __construct(int $quality) public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { - imagewebp($image->getFrames()->first()->getCore(), null, $this->quality); + imagewebp($image->getFrame()->getCore(), null, $this->quality); }); return new EncodedImage($data, 'image/webp'); diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 38c429812..fdacd9d73 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -2,13 +2,60 @@ namespace Intervention\Image\Drivers\Gd; +use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use IteratorAggregate; +use Traversable; class Image extends AbstractImage implements ImageInterface, IteratorAggregate { + public function __construct(protected Collection $frames, protected int $loops = 0) + { + // + } + + public function getIterator(): Traversable + { + return $this->frames; + } + + public function count(): int + { + return $this->frames->count(); + } + + public function isAnimated(): bool + { + return $this->count() > 1; + } + + public function getLoops(): int + { + return $this->loops; + } + + public function setLoops(int $count): self + { + $this->loops = $count; + + return $this; + } + + public function getFrame(int $key = 0): ?FrameInterface + { + return $this->frames->get($key); + } + + public function addFrame(FrameInterface $frame): ImageInterface + { + $this->frames->push($frame); + + return $this; + } + public function getWidth(): int { return imagesx($this->getFrame()->getCore()); diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 7a8b4e02e..5cb315967 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -3,9 +3,7 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; use Imagick; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; @@ -28,15 +26,9 @@ public function decode($input): ImageInterface|ColorInterface $imagick->readImageBlob($input); $imagick = $imagick->coalesceImages(); - $image = new Image(new Collection()); + $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); - foreach ($imagick as $frame_content) { - $image->addFrame( - new Frame($frame_content->getImage()) - ); - } - return $image; } } diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index 0cfca898d..7524380b8 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -16,7 +16,7 @@ public function encode(ImageInterface $image): EncodedImage $compression = Imagick::COMPRESSION_LZW; $gif = new Imagick() ; - foreach ($image->getFrames() as $frame) { + foreach ($image as $frame) { $gif->addImage($frame->getCore()); } diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index 0ea13e671..c61917044 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -20,7 +20,7 @@ public function encode(ImageInterface $image): EncodedImage $format = 'jpeg'; $compression = Imagick::COMPRESSION_JPEG; - $imagick = $image->getFrames()->first()->getCore(); + $imagick = $image->getFrame()->getCore(); $imagick->setImageBackgroundColor('white'); $imagick->setBackgroundColor('white'); $imagick->setFormat($format); diff --git a/src/Drivers/Imagick/Encoders/PngEncoder.php b/src/Drivers/Imagick/Encoders/PngEncoder.php index 8bdc03e6c..3599c02e0 100644 --- a/src/Drivers/Imagick/Encoders/PngEncoder.php +++ b/src/Drivers/Imagick/Encoders/PngEncoder.php @@ -15,7 +15,7 @@ public function encode(ImageInterface $image): EncodedImage $format = 'png'; $compression = Imagick::COMPRESSION_ZIP; - $imagick = $image->getFrames()->first()->getCore(); + $imagick = $image->getFrame()->getCore(); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); diff --git a/src/Drivers/Imagick/Encoders/WebpEncoder.php b/src/Drivers/Imagick/Encoders/WebpEncoder.php index 0f7cb9f92..3f445233e 100644 --- a/src/Drivers/Imagick/Encoders/WebpEncoder.php +++ b/src/Drivers/Imagick/Encoders/WebpEncoder.php @@ -21,7 +21,7 @@ public function encode(ImageInterface $image): EncodedImage $format = 'webp'; $compression = Imagick::COMPRESSION_ZIP; - $imagick = $image->getFrames()->first()->getCore(); + $imagick = $image->getFrame()->getCore(); $imagick->setImageBackgroundColor(new ImagickPixel('transparent')); $imagick = $imagick->mergeImageLayers(Imagick::LAYERMETHOD_MERGE); diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index ef4bd57b7..cb49b24d6 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; -use Intervention\Image\Collection; use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -91,6 +90,6 @@ public function setOffsetTop(int $offset): FrameInterface public function toImage(): ImageInterface { - return new Image(new Collection([$this])); + return new Image($this->getCore()); } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index f6507eec7..e41886c99 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -2,21 +2,122 @@ namespace Intervention\Image\Drivers\Imagick; +use Imagick; +use ImagickException; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; -use IteratorAggregate; +use Iterator; -class Image extends AbstractImage implements ImageInterface, IteratorAggregate +class Image extends AbstractImage implements ImageInterface, Iterator { + protected $iteratorIndex = 0; + + public function __construct(protected Imagick $core) + { + // + } + + public function getCore(): Imagick + { + return $this->core; + } + + public function getFrame(int $key = 0): ?FrameInterface + { + try { + $this->core->setIteratorIndex($key); + } catch (ImagickException $e) { + return null; + } + + return new Frame($this->core->current()); + } + + public function addFrame(FrameInterface $frame): ImageInterface + { + $imagick = $frame->getCore(); + + $imagick->setImageDelay($frame->getDelay()); + $imagick->setImageDispose($frame->getDispose()); + + $size = $frame->getSize(); + $imagick->setImagePage( + $size->getWidth(), + $size->getHeight(), + $frame->getOffsetLeft(), + $frame->getOffsetTop() + ); + + $this->core->addImage($imagick); + + return $this; + } + + public function setLoops(int $count): ImageInterface + { + $this->core->setImageIterations($count); + + return $this; + } + + public function getLoops(): int + { + return $this->core->getImageIterations(); + } + + public function isAnimated(): bool + { + return $this->count() > 1; + } + + public function count(): int + { + return $this->core->getNumberImages(); + } + + public function current() + { + $this->core->setIteratorIndex($this->iteratorIndex); + + return new Frame($this->core->current()); + } + + public function key() + { + return $this->iteratorIndex; + } + + public function next(): void + { + $this->iteratorIndex = $this->iteratorIndex + 1; + } + + public function rewind(): void + { + $this->iteratorIndex = 0; + } + + public function valid(): bool + { + try { + $result = $this->core->setIteratorIndex($this->iteratorIndex); + } catch (ImagickException $e) { + return false; + } + + return $result; + } + public function getWidth(): int { - return $this->frames->first()->getCore()->getImageWidth(); + return $this->getFrame()->getCore()->getImageWidth(); } public function getHeight(): int { - return $this->frames->first()->getCore()->getImageHeight(); + return $this->getFrame()->getCore()->getImageHeight(); } public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index cff14438d..57ef4cc3e 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -4,8 +4,6 @@ use Imagick; use ImagickPixel; -use Intervention\Image\Collection; -use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -13,11 +11,7 @@ class ImageFactory implements FactoryInterface { public function newImage(int $width, int $height): ImageInterface { - return new Image( - new Collection([ - new Frame($this->newCore($width, $height)) - ]) - ); + return new Image($this->newCore($width, $height)); } public function newCore(int $width, int $height): Imagick diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php index fc87d6268..9cfcbae36 100644 --- a/src/Interfaces/CollectionInterface.php +++ b/src/Interfaces/CollectionInterface.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Interfaces; -interface CollectionInterface +use Traversable; + +interface CollectionInterface extends Traversable { public function push($item): CollectionInterface; public function get(int $key, $default = null); diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 5f16c4d7d..f69a98254 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -2,12 +2,12 @@ namespace Intervention\Image\Interfaces; +use Countable; use Intervention\Image\EncodedImage; +use Traversable; -interface ImageInterface +interface ImageInterface extends Traversable, Countable { - public function getIterator(): CollectionInterface; - public function getFrames(): CollectionInterface; public function getFrame(int $key = 0): ?FrameInterface; public function addFrame(FrameInterface $frame): ImageInterface; public function setLoops(int $count): ImageInterface; diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index 52581f216..dde2be233 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -30,58 +30,17 @@ protected function abstractImageMock(): AbstractImage $collection = new Collection([$frame1, $frame2, $frame3]); - $mock = Mockery::mock(AbstractImage::class, ImageInterface::class, [$collection]) + $mock = Mockery::mock(AbstractImage::class, ImageInterface::class) ->shouldAllowMockingProtectedMethods() ->makePartial(); $mock->shouldReceive('getWidth')->andReturn(300); $mock->shouldReceive('getHeight')->andReturn(200); + $mock->shouldReceive('getIterator')->andReturn($collection); return $mock; } - public function testGetIterator(): void - { - $this->assertInstanceOf(Collection::class, $this->abstractImageMock()->getIterator()); - } - - public function testGetFrames(): void - { - $this->assertInstanceOf(Collection::class, $this->abstractImageMock()->getFrames()); - } - - public function testGetFrame(): void - { - $img = $this->abstractImageMock(); - - $this->assertInstanceOf(FrameInterface::class, $img->getFrame()); - $this->assertEquals(1, $img->getFrame()->ident()); - - $this->assertInstanceOf(FrameInterface::class, $img->getFrame(1)); - $this->assertEquals(2, $img->getFrame(1)->ident()); - - $this->assertInstanceOf(FrameInterface::class, $img->getFrame(2)); - $this->assertEquals(3, $img->getFrame(2)->ident()); - } - - public function testAddFrame(): void - { - $img = $this->abstractImageMock(); - $this->assertEquals(3, $img->getFrames()->count()); - $result = $img->addFrame(Mockery::mock(FrameInterface::class)); - $this->assertInstanceOf(AbstractImage::class, $result); - $this->assertEquals(4, $img->getFrames()->count()); - } - - public function testSetGetLoops(): void - { - $img = $this->abstractImageMock(); - $this->assertEquals(0, $img->getLoops()); - $result = $img->setLoops(10); - $this->assertEquals(10, $img->getLoops()); - $this->assertInstanceOf(AbstractImage::class, $result); - } - public function testGetSize(): void { $img = $this->abstractImageMock(); @@ -98,19 +57,6 @@ public function testSizeAlias(): void $this->assertEquals(200, $img->size()->getHeight()); } - public function testIsAnimated(): void - { - $img = Mockery::mock(AbstractImage::class, [new Collection()])->makePartial(); - $this->assertFalse($img->isAnimated()); - - $collection = new Collection([ - Mockery::mock(FrameInterface::class), - Mockery::mock(FrameInterface::class), - ]); - $img = Mockery::mock(AbstractImage::class, [$collection])->makePartial(); - $this->assertTrue($img->isAnimated()); - } - public function testModify(): void { $img = $this->abstractImageMock(); diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 82992c244..27ce28d1f 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -37,6 +37,12 @@ public function testConstructor(): void $this->assertInstanceOf(Image::class, $this->image); } + public function testCount(): void + { + $this->assertEquals(3, $this->image->count()); + $this->assertEquals(3, count($this->image)); + } + public function testIterator(): void { foreach ($this->image as $frame) { @@ -44,12 +50,6 @@ public function testIterator(): void } } - public function testGetFrames(): void - { - $this->assertInstanceOf(Collection::class, $this->image->getFrames()); - $this->assertCount(3, $this->image->getFrames()); - } - public function testGetFrame(): void { $this->assertInstanceOf(Frame::class, $this->image->getFrame()); @@ -58,10 +58,10 @@ public function testGetFrame(): void public function testAddFrame(): void { - $this->assertCount(3, $this->image->getFrames()); + $this->assertCount(3, $this->image); $result = $this->image->addFrame(new Frame(imagecreatetruecolor(3, 2))); $this->assertInstanceOf(Image::class, $result); - $this->assertCount(4, $this->image->getFrames()); + $this->assertCount(4, $this->image); } public function testSetGetLoops(): void diff --git a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php index f0624cf6d..e12b631ca 100644 --- a/tests/Drivers/Imagick/Encoders/GifEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/GifEncoderTest.php @@ -20,20 +20,24 @@ class GifEncoderTest extends TestCase { protected function getTestImage(): Image { - $imagick1 = new Imagick(); - $imagick1->newImage(30, 20, new ImagickPixel('red'), 'png'); - $frame1 = new Frame($imagick1); - $frame1->setDelay(50); - $imagick2 = new Imagick(); - $imagick2->newImage(30, 20, new ImagickPixel('green'), 'png'); - $frame2 = new Frame($imagick2); - $frame2->setDelay(50); - $imagick3 = new Imagick(); - $imagick3->newImage(30, 20, new ImagickPixel('blue'), 'png'); - $frame3 = new Frame($imagick3); - $frame3->setDelay(50); - - return new Image(new Collection([$frame1, $frame2, $frame3])); + $imagick = new Imagick(); + + $frame = new Imagick(); + $frame->newImage(30, 20, new ImagickPixel('red'), 'png'); + $frame->setImageDelay(50); + $imagick->addImage($frame); + + $frame = new Imagick(); + $frame->newImage(30, 20, new ImagickPixel('green'), 'png'); + $frame->setImageDelay(50); + $imagick->addImage($frame); + + $frame = new Imagick(); + $frame->newImage(30, 20, new ImagickPixel('blue'), 'png'); + $frame->setImageDelay(50); + $imagick->addImage($frame); + + return new Image($imagick); } public function testEncode(): void diff --git a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php index 76ea36966..c5e7b702f 100644 --- a/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/JpegEncoderTest.php @@ -4,9 +4,7 @@ use Imagick; use ImagickPixel; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Tests\TestCase; use Intervention\MimeSniffer\MimeSniffer; @@ -22,9 +20,8 @@ protected function getTestImage(): Image { $imagick = new Imagick(); $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $frame = new Frame($imagick); - return new Image(new Collection([$frame])); + return new Image($imagick); } public function testEncode(): void diff --git a/tests/Drivers/Imagick/Encoders/PngEncoderTest.php b/tests/Drivers/Imagick/Encoders/PngEncoderTest.php index 30b41fa8c..92cf1fc65 100644 --- a/tests/Drivers/Imagick/Encoders/PngEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/PngEncoderTest.php @@ -6,9 +6,7 @@ use Imagick; use ImagickPixel; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Encoders\PngEncoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImagePng; @@ -23,9 +21,8 @@ protected function getTestImage(): Image { $imagick = new Imagick(); $imagick->newImage(3, 2, new ImagickPixel('red'), 'jpg'); - $frame = new Frame($imagick); - return new Image(new Collection([$frame])); + return new Image($imagick); } public function testEncode(): void diff --git a/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php b/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php index dcced39bb..69e9df2fb 100644 --- a/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/WebpEncoderTest.php @@ -6,9 +6,7 @@ use Imagick; use ImagickPixel; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Encoders\WebpEncoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\MimeSniffer\MimeSniffer; use Intervention\MimeSniffer\Types\ImageWebp; @@ -23,9 +21,8 @@ protected function getTestImage(): Image { $imagick = new Imagick(); $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $frame = new Frame($imagick); - return new Image(new Collection([$frame])); + return new Image($imagick); } public function testEncode(): void diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 6337c1a79..53e38f2c9 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -4,7 +4,6 @@ use Imagick; use ImagickPixel; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Size; @@ -20,16 +19,46 @@ class ImageTest extends TestCase protected function setUp(): void { + // create base image $imagick = new Imagick(); - $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $this->image = new Image(new Collection([new Frame($imagick)])); + // add frame + $frame = new Imagick(); + $frame->newImage(3, 2, new ImagickPixel('red'), 'png'); + $imagick->addImage($frame); + + // add frame + $frame = new Imagick(); + $frame->newImage(3, 2, new ImagickPixel('green'), 'png'); + $imagick->addImage($frame); + + // create intervention image + $this->image = new Image($imagick); } public function testConstructor(): void { $this->assertInstanceOf(Image::class, $this->image); } + + public function testGetFrame(): void + { + $this->assertInstanceOf(Frame::class, $this->image->getFrame()); + $this->assertInstanceOf(Frame::class, $this->image->getFrame(1)); + $this->assertNull($this->image->getFrame(2)); + } + + public function testAddFrame(): void + { + $frame = new Imagick(); + $frame->newImage(3, 2, new ImagickPixel('blue'), 'png'); + $frame = new Frame($frame); + + $this->assertCount(2, $this->image); + $result = $this->image->addFrame($frame); + $this->assertInstanceOf(Image::class, $result); + $this->assertCount(3, $this->image); + } public function testIterator(): void { @@ -38,6 +67,12 @@ public function testIterator(): void } } + public function testCount(): void + { + $this->assertEquals(2, $this->image->count()); + $this->assertEquals(2, count($this->image)); + } + public function testWidth(): void { $this->assertEquals(3, $this->image->getWidth()); From bf8e3a1fc739064b86fdcc0787c96df4e4fc7650 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 21 Jun 2022 09:59:29 +0200 Subject: [PATCH 0332/1667] Optimize GifEncoder of imagick driver Imagick object is accessed directly instead of frame by frame. --- src/Drivers/Imagick/Encoders/GifEncoder.php | 19 ++++++---------- src/Drivers/Imagick/Image.php | 24 ++++++++++----------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index 7524380b8..f71922747 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -15,18 +15,13 @@ public function encode(ImageInterface $image): EncodedImage $format = 'gif'; $compression = Imagick::COMPRESSION_LZW; - $gif = new Imagick() ; - foreach ($image as $frame) { - $gif->addImage($frame->getCore()); - } + $imagick = $image->getImagick(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + $imagick = $imagick->deconstructImages(); - $gif->setImageIterations($image->getLoops()); - $gif->setFormat($format); - $gif->setImageFormat($format); - $gif->setCompression($compression); - $gif->setImageCompression($compression); - $gif = $gif->deconstructImages(); - - return new EncodedImage($gif->getImagesBlob(), 'image/gif'); + return new EncodedImage($imagick->getImagesBlob(), 'image/gif'); } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index e41886c99..906322afe 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -14,25 +14,25 @@ class Image extends AbstractImage implements ImageInterface, Iterator { protected $iteratorIndex = 0; - public function __construct(protected Imagick $core) + public function __construct(protected Imagick $imagick) { // } - public function getCore(): Imagick + public function getImagick(): Imagick { - return $this->core; + return $this->imagick; } public function getFrame(int $key = 0): ?FrameInterface { try { - $this->core->setIteratorIndex($key); + $this->imagick->setIteratorIndex($key); } catch (ImagickException $e) { return null; } - return new Frame($this->core->current()); + return new Frame($this->imagick->current()); } public function addFrame(FrameInterface $frame): ImageInterface @@ -50,21 +50,21 @@ public function addFrame(FrameInterface $frame): ImageInterface $frame->getOffsetTop() ); - $this->core->addImage($imagick); + $this->imagick->addImage($imagick); return $this; } public function setLoops(int $count): ImageInterface { - $this->core->setImageIterations($count); + $this->imagick->setImageIterations($count); return $this; } public function getLoops(): int { - return $this->core->getImageIterations(); + return $this->imagick->getImageIterations(); } public function isAnimated(): bool @@ -74,14 +74,14 @@ public function isAnimated(): bool public function count(): int { - return $this->core->getNumberImages(); + return $this->imagick->getNumberImages(); } public function current() { - $this->core->setIteratorIndex($this->iteratorIndex); + $this->imagick->setIteratorIndex($this->iteratorIndex); - return new Frame($this->core->current()); + return new Frame($this->imagick->current()); } public function key() @@ -102,7 +102,7 @@ public function rewind(): void public function valid(): bool { try { - $result = $this->core->setIteratorIndex($this->iteratorIndex); + $result = $this->imagick->setIteratorIndex($this->iteratorIndex); } catch (ImagickException $e) { return false; } From 871a1958b3084be9f4d58db5be318b8cd6aa5f7a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 21 Jun 2022 20:09:37 +0200 Subject: [PATCH 0333/1667] Change signature of AbstractFont & FontInterface --- src/Drivers/Abstract/AbstractFont.php | 14 +++++++------- src/Interfaces/FontInterface.php | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 6fee3a603..8991cd904 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -29,7 +29,7 @@ public function getText(): string return $this->text; } - public function size(float $size): self + public function size(float $size): FontInterface { $this->size = $size; @@ -41,7 +41,7 @@ public function getSize(): float return $this->size; } - public function angle(float $angle): self + public function angle(float $angle): FontInterface { $this->angle = $angle; @@ -53,14 +53,14 @@ public function getAngle(): float return $this->angle; } - public function filename(string $filename): self + public function filename(string $filename): FontInterface { $this->filename = $filename; return $this; } - public function getFilename(): string + public function getFilename(): ?string { return $this->filename; } @@ -70,7 +70,7 @@ public function hasFilename(): bool return is_file($this->filename); } - public function color($color): self + public function color($color): FontInterface { $this->color = $color; @@ -82,7 +82,7 @@ public function getColor(): ?ColorInterface return $this->handleInput($this->color); } - public function align(string $align): self + public function align(string $align): FontInterface { $this->align = $align; @@ -94,7 +94,7 @@ public function getValign(): string return $this->valign; } - public function valign(string $valign): self + public function valign(string $valign): FontInterface { $this->valign = $valign; diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index c9f68d2da..26d42fc7b 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -15,7 +15,7 @@ public function getSize(): float; public function angle(float $angle): self; public function getAngle(): float; public function filename(string $filename): self; - public function getFilename(): string; + public function getFilename(): ?string; public function hasFilename(): bool; public function align(string $align): self; public function getAlign(): string; From afec510ad0fcf5a34e757c5129d55b8279767292 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 21 Jun 2022 20:09:54 +0200 Subject: [PATCH 0334/1667] Add test for AbstractFont --- tests/Drivers/Abstract/AbstractFontTest.php | 46 +++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/Drivers/Abstract/AbstractFontTest.php diff --git a/tests/Drivers/Abstract/AbstractFontTest.php b/tests/Drivers/Abstract/AbstractFontTest.php new file mode 100644 index 000000000..e39d345d6 --- /dev/null +++ b/tests/Drivers/Abstract/AbstractFontTest.php @@ -0,0 +1,46 @@ +shouldAllowMockingProtectedMethods() + ->makePartial(); + + // settings + $mock->size(24); + $mock->angle(30); + $mock->filename(__DIR__ . '/AbstractFontTest.php'); + $mock->color('ccc'); + $mock->align('center'); + $mock->valign('top'); + + $mock->shouldReceive('handleInput')->andReturn( + Mockery::mock(ColorInterface::class) + ); + + return $mock; + } + + public function testConstructor(): void + { + $mock = $this->getAbstractFontMock(); + $this->assertEquals('test123', $mock->getText()); + $this->assertEquals(24.0, $mock->getSize()); + $this->assertEquals(30, $mock->getAngle()); + $this->assertEquals(__DIR__ . '/AbstractFontTest.php', $mock->getFilename()); + $this->assertInstanceOf(ColorInterface::class, $mock->getColor()); + $this->assertEquals('center', $mock->getAlign()); + $this->assertEquals('top', $mock->getValign()); + $this->assertTrue($mock->hasFilename()); + } +} From 7abf23edbb93d023681ca535f595fca81f0c1f53 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 23 Jun 2022 12:04:04 +0200 Subject: [PATCH 0335/1667] Implement FontWriter Modifier --- src/Drivers/Gd/Font.php | 22 +- src/Drivers/Gd/Modifiers/TextWriter.php | 35 +- src/Drivers/Imagick/Font.php | 18 +- src/Drivers/Imagick/Modifiers/TextWriter.php | 10 +- src/Geometry/Point.php | 13 +- src/Geometry/Polygon.php | 455 +++++++++++++++++++ src/Geometry/Size.php | 24 + src/Interfaces/FontInterface.php | 4 +- tests/Geometry/PolygonTest.php | 397 ++++++++++++++++ tests/Geometry/SizeTest.php | 18 +- 10 files changed, 942 insertions(+), 54 deletions(-) create mode 100644 src/Geometry/Polygon.php create mode 100644 tests/Geometry/PolygonTest.php diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 90a15b0c9..3ac9c1c7d 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -3,6 +3,8 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Drivers\Abstract\AbstractFont; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Size; class Font extends AbstractFont @@ -15,9 +17,9 @@ public function getSize(): float /** * Calculate size of bounding box of current text * - * @return Size + * @return Polygon */ - public function getBoxSize(): Size + public function getBoxSize(): Polygon { if (!$this->hasFilename()) { // calculate box size from gd font @@ -27,18 +29,26 @@ public function getBoxSize(): Size $box->setWidth($chars * $this->getGdFontWidth()); $box->setHeight($this->getGdFontHeight()); } - return $box; + return $box->toPolygon(); } - // calculate box size from font file + // calculate box size from font file with angle 0 $box = imageftbbox( $this->getSize(), - $this->getAngle(), + 0, $this->getFilename(), $this->getText() ); - return new Size(abs($box[0] - $box[2]), abs($box[1] - $box[7])); + // build polygon from points + $polygon = new Polygon(); + $polygon->addPoint(new Point($box[6], $box[7])); + $polygon->addPoint(new Point($box[4], $box[5])); + $polygon->addPoint(new Point($box[2], $box[3])); + $polygon->addPoint(new Point($box[0], $box[1])); + + + return $polygon; } public function getGdFont(): int diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 6c6277c3d..3a6b18a68 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -19,13 +19,12 @@ public function __construct( public function apply(ImageInterface $image): ImageInterface { $position = $this->getAlignedPosition(); - foreach ($image as $frame) { if ($this->font->hasFilename()) { imagettftext( $frame->getCore(), $this->font->getSize(), - $this->font->getAngle(), + $this->font->getAngle() * (-1), $position->getX(), $position->getY(), $this->font->getColor()->toInt(), @@ -47,34 +46,18 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - protected function getAlignedPosition(): Point + public function getAlignedPosition(): Point { - $position = $this->position; - $box = $this->font->getBoxSize(); - - // adjust x pos - switch ($this->font->getAlign()) { - case 'center': - $position->setX($position->getX() - round($box->getWidth() / 2)); - break; - - case 'right': - $position->setX($position->getX() - $box->getWidth()); - break; - } + $poly = $this->font->getBoxSize(); + $poly->setPivotPoint($this->position); - // adjust y pos - switch ($this->font->getValign()) { - case 'top': - $position->setY($position->getY() + $box->getHeight()); - break; + $poly->align($this->font->getAlign()); + $poly->valign($this->font->getValign()); - case 'middle': - case 'center': - $position->setY($position->getY() + round($box->getHeight() / 2)); - break; + if ($this->font->getAngle() != 0) { + $poly->rotate($this->font->getAngle()); } - return $position; + return $poly->last(); } } diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index df32d284a..93f21943e 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -6,6 +6,7 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; use Intervention\Image\Exceptions\FontException; +use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Size; class Font extends AbstractFont @@ -13,7 +14,7 @@ class Font extends AbstractFont public function toImagickDraw(): ImagickDraw { if (!$this->hasFilename()) { - throw new FontException('No font file.'); + throw new FontException('No font file specified.'); } $draw = new ImagickDraw(); @@ -50,14 +51,13 @@ public function getImagickAlign(): int /** * Calculate box size of current font * - * @return Size + * @return Polygon */ - public function getBoxSize(): Size + public function getBoxSize(): Polygon { - $foo = null; // no text - no box size if (mb_strlen($this->getText()) === 0) { - return new Size(0, 0); + return (new Size(0, 0))->toPolygon(); } $dimensions = (new Imagick())->queryFontMetrics( @@ -65,9 +65,9 @@ public function getBoxSize(): Size $this->getText() ); - return new Size( - intval(abs($dimensions['textWidth'])), - intval(abs($dimensions['textHeight'])) - ); + return (new Size( + intval(round(abs($dimensions['boundingBox']['x1'] - $dimensions['boundingBox']['x2']))), + intval(round(abs($dimensions['boundingBox']['y1'] - $dimensions['boundingBox']['y2']))), + ))->toPolygon(); } } diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index 52c6b7be7..4fe1dc2a0 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -18,15 +18,13 @@ public function __construct( public function apply(ImageInterface $image): ImageInterface { - $draw = $this->font->toImagickDraw(); $position = $this->getAlignedPosition(); - foreach ($image as $frame) { $frame->getCore()->annotateImage( - $draw, + $this->font->toImagickDraw(), $position->getX(), $position->getY(), - $this->font->getAngle(), + $this->font->getAngle() * (-1), $this->font->getText() ); } @@ -42,12 +40,12 @@ protected function getAlignedPosition(): Point // adjust y pos switch ($this->font->getValign()) { case 'top': - $position->setY($position->getY() + $box->getHeight()); + $position->setY($position->getY() + $box->height()); break; case 'middle': case 'center': - $position->setY($position->getY() + round($box->getHeight() / 2)); + $position->setY(intval($position->getY() + round($box->height() / 2))); break; } diff --git a/src/Geometry/Point.php b/src/Geometry/Point.php index 44f3c9769..a83ba0070 100644 --- a/src/Geometry/Point.php +++ b/src/Geometry/Point.php @@ -58,7 +58,7 @@ public function getY(): int /** * Move X coordinate * - * @param integer $x + * @param integer $value */ public function moveX(int $value): self { @@ -70,7 +70,7 @@ public function moveX(int $value): self /** * Move Y coordinate * - * @param integer $y + * @param integer $value */ public function moveY(int $value): self { @@ -79,6 +79,11 @@ public function moveY(int $value): self return $this; } + public function move(int $x, int $y): self + { + return $this->moveX($x)->moveY($y); + } + /** * Sets both X and Y coordinate * @@ -107,8 +112,8 @@ public function rotate(float $angle, Point $pivot): self $cos = round(cos(deg2rad($angle)), 6); return $this->setPosition( - $cos * ($this->x - $pivot->x) - $sin * ($this->y - $pivot->y) + $pivot->x, - $sin * ($this->x - $pivot->x) + $cos * ($this->y - $pivot->y) + $pivot->y + intval($cos * ($this->x - $pivot->x) - $sin * ($this->y - $pivot->y) + $pivot->x), + intval($sin * ($this->x - $pivot->x) + $cos * ($this->y - $pivot->y) + $pivot->y) ); } } diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php new file mode 100644 index 000000000..f7d1eef33 --- /dev/null +++ b/src/Geometry/Polygon.php @@ -0,0 +1,455 @@ +pivot = $pivot ? $pivot : new Point(); + } + + /** + * Return current pivot point + * + * @return Point + */ + public function getPivotPoint(): Point + { + return $this->pivot; + } + + /** + * Change pivot point to given point + * + * @param Point $pivot + * @return Polygon + */ + public function setPivotPoint(Point $pivot): self + { + $this->pivot = $pivot; + + return $this; + } + + /** + * Return first point of polygon + * + * @return ?Point + */ + public function first(): ?Point + { + if ($point = reset($this->points)) { + return $point; + } + + return null; + } + + /** + * Return last point of polygon + * + * @return ?Point + */ + public function last(): ?Point + { + if ($point = end($this->points)) { + return $point; + } + + return null; + } + + /** + * Return polygon's point count + * + * @return int + */ + public function count(): int + { + return count($this->points); + } + + /** + * Determine if point exists at given offset + * + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset): bool + { + return array_key_exists($offset, $this->points); + } + + /** + * Return point at given offset + * + * @param mixed $offset + * @return Point + */ + public function offsetGet($offset) + { + return $this->points[$offset]; + } + + /** + * Set point at given offset + * + * @param mixed $offset + * @param Point $value + * @return void + */ + public function offsetSet($offset, $value): void + { + $this->points[$offset] = $value; + } + + /** + * Unset offset at given offset + * + * @param mixed $offset + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->points[$offset]); + } + + /** + * Add given point to polygon + * + * @param Point $point + * @return Polygon + */ + public function addPoint(Point $point): self + { + $this->points[] = $point; + + return $this; + } + + /** + * Calculate total horizontal span of polygon + * + * @return int + */ + public function width(): int + { + return abs($this->getMostLeftPoint()->getX() - $this->getMostRightPoint()->getX()); + } + + /** + * Calculate total vertical span of polygon + * + * @return int + */ + public function height(): int + { + return abs($this->getMostBottomPoint()->getY() - $this->getMostTopPoint()->getY()); + } + + /** + * Return most left point of all points in polygon + * + * @return Point + */ + public function getMostLeftPoint(): Point + { + $points = []; + foreach ($this->points as $point) { + $points[] = $point; + } + + usort($points, function ($a, $b) { + if ($a->getX() === $b->getX()) { + return 0; + } + return ($a->getX() < $b->getX()) ? -1 : 1; + }); + + return $points[0]; + } + + /** + * Return most right point in polygon + * + * @return Point + */ + public function getMostRightPoint(): Point + { + $points = []; + foreach ($this->points as $point) { + $points[] = $point; + } + + usort($points, function ($a, $b) { + if ($a->getX() === $b->getX()) { + return 0; + } + return ($a->getX() > $b->getX()) ? -1 : 1; + }); + + return $points[0]; + } + + /** + * Return most top point in polygon + * + * @return Point + */ + public function getMostTopPoint(): Point + { + $points = []; + foreach ($this->points as $point) { + $points[] = $point; + } + + usort($points, function ($a, $b) { + if ($a->getY() === $b->getY()) { + return 0; + } + return ($a->getY() > $b->getY()) ? -1 : 1; + }); + + return $points[0]; + } + + /** + * Return most bottom point in polygon + * + * @return Point + */ + public function getMostBottomPoint(): Point + { + $points = []; + foreach ($this->points as $point) { + $points[] = $point; + } + + usort($points, function ($a, $b) { + if ($a->getY() === $b->getY()) { + return 0; + } + return ($a->getY() < $b->getY()) ? -1 : 1; + }); + + return $points[0]; + } + + /** + * Create and return point in absolute center of the polygon + * + * @return Point + */ + public function getCenterPoint(): Point + { + return new Point( + $this->getMostRightPoint()->getX() - (intval(round($this->width() / 2))), + $this->getMostTopPoint()->getY() - (intval(round($this->height() / 2))) + ); + } + + /** + * Align pivot point to given horizontal position + * + * @param string $position + * @return Polygon + */ + public function alignPivot(string $position): self + { + switch (strtolower($position)) { + case 'center': + $this->pivot->setX( + intval(($this->getMostRightPoint()->getX() + $this->getMostLeftPoint()->getX()) / 2) + ); + break; + + case 'right': + $this->pivot->setX( + $this->getMostRightPoint()->getX() + ); + break; + + case 'left': + $this->pivot->setX( + $this->getMostLeftPoint()->getX() + ); + break; + } + + return $this; + } + + /** + * Align pivot point to given vertical position + * + * @param string $position + * @return Polygon + */ + public function valignPivot(string $position): self + { + switch (strtolower($position)) { + case 'center': + case 'middle': + $this->pivot->setY( + intval(($this->getMostTopPoint()->getY() + $this->getMostBottomPoint()->getY()) / 2) + ); + break; + + case 'top': + $this->pivot->setY( + $this->getMostTopPoint()->getY() + ); + break; + + case 'bottom': + $this->pivot->setY( + $this->getMostBottomPoint()->getY() + ); + break; + } + + return $this; + } + + /** + * Align all points of polygon horizontally to given position around pivot point + * + * @param string $position + * @return Polygon + */ + public function align(string $position): self + { + switch (strtolower($position)) { + case 'center': + case 'middle': + $diff = ($this->getCenterPoint()->getX() - $this->pivot->getX()); + break; + + case 'right': + $diff = ($this->getMostRightPoint()->getX() - $this->pivot->getX()); + break; + + default: + case 'left': + $diff = ($this->getMostLeftPoint()->getX() - $this->pivot->getX()); + break; + } + + foreach ($this->points as $point) { + $point->setX($point->getX() - $diff); + } + + return $this; + } + + /** + * Align all points of polygon vertically to given position around pivot point + * + * @param string $position + * @return Polygon + */ + public function valign(string $position): self + { + switch (strtolower($position)) { + case 'center': + case 'middle': + $diff = ($this->getCenterPoint()->getY() - $this->pivot->getY()); + break; + + case 'top': + $diff = ($this->getMostTopPoint()->getY() - $this->pivot->getY()) - $this->height(); + break; + + default: + case 'bottom': + $diff = ($this->getMostBottomPoint()->getY() - $this->pivot->getY()) + $this->height(); + break; + } + + foreach ($this->points as $point) { + $point->setY($point->getY() - $diff); + } + + return $this; + } + + /** + * Rotate points of polygon around pivot point with given angle + * + * @param float $angle + * @return Polygon + */ + public function rotate(float $angle): self + { + $sin = sin(deg2rad($angle)); + $cos = cos(deg2rad($angle)); + + foreach ($this->points as $point) { + // translate point to pivot + $point->setX($point->getX() - $this->pivot->getX()); + $point->setY($point->getY() - $this->pivot->getY()); + + // rotate point + $x = $point->getX() * $cos - $point->getY() * $sin; + $y = $point->getX() * $sin + $point->getY() * $cos; + + // translate point back + $point->setX($x + $this->pivot->getX()); + $point->setY($y + $this->pivot->getY()); + } + + return $this; + } + + /** + * Move all points by given amount on the x-axis + * + * @param int $amount + * @return Polygon + */ + public function movePointsX(int $amount): self + { + foreach ($this->points as $point) { + $point->moveX($amount); + } + + return $this; + } + + /** + * Move all points by given amount on the y-axis + * + * @param int $amount + * @return Polygon + */ + public function movePointsY(int $amount): self + { + foreach ($this->points as $point) { + $point->moveY($amount); + } + + return $this; + } + + /** + * Return array of all x/y values of all points of polygon + * + * @return array + */ + public function toArray(): array + { + $coordinates = []; + foreach ($this->points as $point) { + $coordinates[] = $point->getX(); + $coordinates[] = $point->getY(); + } + + return $coordinates; + } +} diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 048862856..532940b5b 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -209,6 +209,30 @@ public function getRelativePositionTo(SizeInterface $size): PointInterface return new Point($x, $y); } + public function toPolygon(): Polygon + { + $polygon = new Polygon([ + $this->pivot // top/left + ], $this->pivot); + + // top/right + $polygon->addPoint( + new Point($this->pivot->getX() + $this->getWidth(), $this->pivot->getY()) + ); + + // bottom/right + $polygon->addPoint( + new Point($this->pivot->getX() + $this->getWidth(), $this->pivot->getY() - $this->getHeight()) + ); + + // bottom/left + $polygon->addPoint( + new Point($this->pivot->getX(), $this->pivot->getY() - $this->getHeight()) + ); + + return $polygon; + } + protected function getResizer(?int $width = null, ?int $height = null): Resizer { return new Resizer($width, $height); diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 26d42fc7b..92e470085 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Polygon; use Intervention\Image\Interfaces\ColorInterface; interface FontInterface @@ -19,5 +19,5 @@ public function getFilename(): ?string; public function hasFilename(): bool; public function align(string $align): self; public function getAlign(): string; - public function getBoxSize(): Size; + public function getBoxSize(): Polygon; } diff --git a/tests/Geometry/PolygonTest.php b/tests/Geometry/PolygonTest.php new file mode 100644 index 000000000..3ed904de0 --- /dev/null +++ b/tests/Geometry/PolygonTest.php @@ -0,0 +1,397 @@ +assertInstanceOf(Polygon::class, $poly); + $this->assertEquals(0, $poly->count()); + } + + public function testCount(): void + { + $poly = new Polygon([new Point(), new Point()]); + $this->assertEquals(2, $poly->count()); + } + + public function testArrayAccess(): void + { + $poly = new Polygon([new Point(), new Point()]); + $this->assertInstanceOf(Point::class, $poly[0]); + $this->assertInstanceOf(Point::class, $poly[1]); + } + + public function testAddPoint(): void + { + $poly = new Polygon([new Point(), new Point()]); + $this->assertEquals(2, $poly->count()); + $result = $poly->addPoint(new Point()); + $this->assertEquals(3, $poly->count()); + $this->assertInstanceOf(Polygon::class, $result); + } + + public function testGetCenterPoint(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(20, 0), + new Point(20, -20), + new Point(0, -20), + ]); + + $result = $poly->getCenterPoint(); + $this->assertEquals(10, $result->getX()); + $this->assertEquals(-10, $result->getY()); + + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(0, 0)); + + $result = $poly->getCenterPoint(); + $this->assertEquals(150, $result->getX()); + $this->assertEquals(-100, $result->getY()); + } + + public function testWidth(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-23, -49), + new Point(3, 566), + ]); + + $this->assertEquals($poly->width(), 35); + } + + public function testHeight(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-23, -49), + new Point(3, 566), + ]); + + $this->assertEquals(615, $poly->height()); + + $poly = new Polygon([ + new Point(250, 207), + new Point(473, 207), + new Point(473, 250), + new Point(250, 250), + ], new Point(250, 250)); + + $this->assertEquals(43, $poly->height()); + } + + public function testFirst(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-23, -49), + new Point(3, 566), + ]); + + $this->assertEquals(12, $poly->first()->getX()); + $this->assertEquals(45, $poly->first()->getY()); + } + + public function testLast(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-23, -49), + new Point(3, 566), + ]); + + $this->assertEquals(3, $poly->last()->getX()); + $this->assertEquals(566, $poly->last()->getY()); + } + + public function testGetPivotPoint(): void + { + $poly = new Polygon(); + $this->assertInstanceOf(Point::class, $poly->getPivotPoint()); + } + + public function testAlignPivot(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-24, -49), + new Point(3, 566), + ]); + + $this->assertEquals(0, $poly->getPivotPoint()->getX()); + $this->assertEquals(0, $poly->getPivotPoint()->getY()); + + $result = $poly->alignPivot('center'); + $this->assertInstanceOf(Polygon::class, $result); + + $this->assertEquals(-6, $result->getPivotPoint()->getX()); + $this->assertEquals(0, $result->getPivotPoint()->getY()); + } + + public function testValignPivot(): void + { + $poly = new Polygon([ + new Point(12, 45), + new Point(-24, -50), + new Point(3, 566), + ]); + + $this->assertEquals(0, $poly->getPivotPoint()->getX()); + $this->assertEquals(0, $poly->getPivotPoint()->getY()); + + $result = $poly->valignPivot('middle'); + $this->assertInstanceOf(Polygon::class, $result); + + $this->assertEquals(0, $result->getPivotPoint()->getX()); + $this->assertEquals(258, $result->getPivotPoint()->getY()); + } + + public function testGetMostLeftPoint(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(-32, -200), + ], new Point(0, 0)); + + $result = $poly->getMostLeftPoint(); + $this->assertEquals(-32, $result->getX()); + $this->assertEquals(-200, $result->getY()); + } + + public function testGetMostRightPoint(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(350, 0), + new Point(300, -200), + new Point(-32, -200), + ], new Point(0, 0)); + + $result = $poly->getMostRightPoint(); + $this->assertEquals(350, $result->getX()); + $this->assertEquals(0, $result->getY()); + } + + public function testGetMostTopPoint(): void + { + $poly = new Polygon([ + new Point(0, 100), + new Point(350, 0), + new Point(300, -200), + new Point(-32, 200), + ], new Point(0, 0)); + + $result = $poly->getMostTopPoint(); + $this->assertEquals(-32, $result->getX()); + $this->assertEquals(200, $result->getY()); + } + + public function testGetMostBottomPoint(): void + { + $poly = new Polygon([ + new Point(0, 100), + new Point(350, 0), + new Point(300, -200), + new Point(-32, 200), + ], new Point(0, 0)); + + $result = $poly->getMostBottomPoint(); + $this->assertEquals(300, $result->getX()); + $this->assertEquals(-200, $result->getY()); + } + + public function testAlignCenter(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(0, 0)); + + $result = $poly->align('center'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-150, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(150, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(150, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-150, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(-1000, -1000)); + + $result = $poly->align('center'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-1150, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(-850, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(-850, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-1150, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + } + + public function testAlignLeft(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(100, 100)); + + $result = $poly->align('left'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(100, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(400, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(400, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(100, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(-1000, -1000)); + + $result = $poly->align('left'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-1000, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(-700, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(-700, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-1000, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + } + + public function testAlignRight(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(100, 100)); + + $result = $poly->align('right'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-200, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(100, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(100, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-200, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + + $poly = new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, -200), + new Point(0, -200), + ], new Point(-1000, -1000)); + + $result = $poly->align('right'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-1300, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(-1000, $result[1]->getX()); + $this->assertEquals(0, $result[1]->getY()); + $this->assertEquals(-1000, $result[2]->getX()); + $this->assertEquals(-200, $result[2]->getY()); + $this->assertEquals(-1300, $result[3]->getX()); + $this->assertEquals(-200, $result[3]->getY()); + } + + public function testValignMiddle(): void + { + $poly = new Polygon([ + new Point(-21, -22), + new Point(91, -135), + new Point(113, -113), + new Point(0, 0), + ], new Point(250, 250)); + + $result = $poly->valign('middle'); + + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(-21, $result[0]->getX()); + $this->assertEquals(296, $result[0]->getY()); + $this->assertEquals(91, $result[1]->getX()); + $this->assertEquals(183, $result[1]->getY()); + $this->assertEquals(113, $result[2]->getX()); + $this->assertEquals(205, $result[2]->getY()); + $this->assertEquals(0, $result[3]->getX()); + $this->assertEquals(318, $result[3]->getY()); + } + + public function testRotate(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(50, 0), + new Point(50, -50), + new Point(0, -50), + ]); + + $result = $poly->rotate(45); + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(0, $result[0]->getX()); + $this->assertEquals(0, $result[0]->getY()); + $this->assertEquals(35, $result[1]->getX()); + $this->assertEquals(35, $result[1]->getY()); + $this->assertEquals(70, $result[2]->getX()); + $this->assertEquals(0, $result[2]->getY()); + $this->assertEquals(35, $result[3]->getX()); + $this->assertEquals(-35, $result[3]->getY()); + } + + public function testToArray(): void + { + $poly = new Polygon([ + new Point(0, 0), + new Point(50, 0), + new Point(50, -50), + new Point(0, -50), + ]); + + $this->assertEquals([0, 0, 50, 0, 50, -50, 0, -50], $poly->toArray()); + } +} diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php index 68dbe2cd0..c11031f8f 100644 --- a/tests/Geometry/SizeTest.php +++ b/tests/Geometry/SizeTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Geometry; -use Intervention\Image\Geometry\{Point, Size,}; +use Intervention\Image\Geometry\{Point, Polygon, Size,}; use Intervention\Image\Tests\TestCase; /** @@ -254,6 +254,22 @@ public function testgetRelativePositionTo(): void $this->assertEquals(50, $pos->getY()); } + public function testToPolygon(): void + { + $size = new Size(300, 200); + $poly = $size->toPolygon(); + $this->assertInstanceOf(Polygon::class, $poly); + $this->assertCount(4, $poly); + $this->assertEquals(0, $poly[0]->getX()); + $this->assertEquals(0, $poly[0]->getY()); + $this->assertEquals(300, $poly[1]->getX()); + $this->assertEquals(0, $poly[1]->getY()); + $this->assertEquals(300, $poly[2]->getX()); + $this->assertEquals(-200, $poly[2]->getY()); + $this->assertEquals(0, $poly[3]->getX()); + $this->assertEquals(-200, $poly[3]->getY()); + } + public function testResize(): void { $size = new Size(300, 200); From 452b91929cfd809c3ba717025ad1d40e947379ea Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 24 Jun 2022 18:53:47 +0200 Subject: [PATCH 0336/1667] Fix code issues reported by phpstan --- src/Drivers/Gd/Font.php | 7 ++++--- src/Drivers/Gd/ImageFactory.php | 3 +-- src/Drivers/Gd/Modifiers/PlaceModifier.php | 4 ++-- src/Drivers/Imagick/Color.php | 6 +++--- src/Drivers/Imagick/Encoders/GifEncoder.php | 6 ++++++ src/Drivers/Imagick/Font.php | 10 +++++++--- src/Drivers/Imagick/Frame.php | 2 +- src/Drivers/Imagick/ImageFactory.php | 2 +- src/Drivers/Imagick/Modifiers/PadModifier.php | 9 +++++++-- src/Drivers/Imagick/Modifiers/PlaceModifier.php | 3 ++- src/Drivers/Imagick/Modifiers/RotateModifier.php | 9 ++++++++- src/Exceptions/EncoderException.php | 8 ++++++++ src/Interfaces/ColorInterface.php | 2 +- src/Interfaces/FactoryInterface.php | 1 + 14 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 src/Exceptions/EncoderException.php diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 3ac9c1c7d..b407b4955 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -68,9 +68,6 @@ protected function getGdFontWidth(): int protected function getGdFontHeight(): int { switch ($this->getGdFont()) { - case 1: - return 8; - case 2: return 14; @@ -82,6 +79,10 @@ protected function getGdFontHeight(): int case 5: return 16; + + default: + case 1: + return 8; } } } diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 467e0332d..7dbc2f4ea 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Drivers\Gd; -use GdImage; use Intervention\Image\Collection; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -18,7 +17,7 @@ public function newImage(int $width, int $height): ImageInterface ); } - public function newCore(int $width, int $height): GdImage + public function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); $color = imagecolorallocatealpha($core, 0, 0, 0, 127); diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 9350170af..b90233c99 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -3,9 +3,9 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Traits\CanResolveDriverClass; class PlaceModifier implements ModifierInterface @@ -52,7 +52,7 @@ protected function decodeWatermark(): Image return $this->resolveDriverClass('InputHandler')->handle($this->element); } - protected function getPosition(Image $image, Image $watermark): Point + protected function getPosition(ImageInterface $image, ImageInterface $watermark): PointInterface { $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->alignPivot($this->position); diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php index bda5e232c..7503c7b1a 100644 --- a/src/Drivers/Imagick/Color.php +++ b/src/Drivers/Imagick/Color.php @@ -21,17 +21,17 @@ public function getPixel(): ImagickPixel public function red(): int { - return round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255); + return intval(round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255)); } public function green(): int { - return round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255); + return intval(round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255)); } public function blue(): int { - return round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255); + return intval(round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255)); } public function alpha(): float diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index f71922747..d180dc11c 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -4,7 +4,9 @@ use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; +use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\EncodedImage; +use Intervention\Image\Exceptions\EncoderException; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -15,6 +17,10 @@ public function encode(ImageInterface $image): EncodedImage $format = 'gif'; $compression = Imagick::COMPRESSION_LZW; + if (!is_a($image, Image::class)) { + throw new EncoderException('Image does not match the current driver.'); + } + $imagick = $image->getImagick(); $imagick->setFormat($format); $imagick->setImageFormat($format); diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 93f21943e..e719fe7ab 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -5,6 +5,7 @@ use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Size; @@ -17,12 +18,17 @@ public function toImagickDraw(): ImagickDraw throw new FontException('No font file specified.'); } + $color = $this->getColor(); + if (!is_a($color, Color::class)) { + throw new DecoderException('Unable to decode font color.'); + } + $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); - $draw->setFillColor($this->getColor()->getPixel()); + $draw->setFillColor($color->getPixel()); $draw->setTextAlignment($this->getImagickAlign()); return $draw; @@ -38,11 +44,9 @@ public function getImagickAlign(): int switch (strtolower($this->getAlign())) { case 'center': return Imagick::ALIGN_CENTER; - break; case 'right': return Imagick::ALIGN_RIGHT; - break; } return Imagick::ALIGN_LEFT; diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index cb49b24d6..245aff842 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -39,7 +39,7 @@ public function getDelay(): float public function setDelay(float $delay): FrameInterface { - $this->core->setImageDelay(round($delay * 100)); + $this->core->setImageDelay(intval(round($delay * 100))); return $this; } diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 57ef4cc3e..cc54048b7 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -14,7 +14,7 @@ public function newImage(int $width, int $height): ImageInterface return new Image($this->newCore($width, $height)); } - public function newCore(int $width, int $height): Imagick + public function newCore(int $width, int $height) { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index 94bee08ea..94d44a652 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -5,7 +5,8 @@ use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; -use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -23,6 +24,10 @@ public function apply(ImageInterface $image): ImageInterface $crop = $this->getCropSize($image); $background = $this->handleInput($this->background); + if (!is_a($background, Color::class)) { + throw new DecoderException('Unable to decode backgroud color.'); + } + foreach ($image as $frame) { // resize current core $frame->getCore()->scaleImage( @@ -49,7 +54,7 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, ColorInterface $background): Imagick + protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, Color $background): Imagick { // build base canvas in target size $canvas = $this->imageFactory()->newCore( diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index 06f1ef090..894ba1538 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -7,6 +7,7 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Traits\CanResolveDriverClass; class PlaceModifier implements ModifierInterface @@ -44,7 +45,7 @@ protected function decodeWatermark(): Image return $this->resolveDriverClass('InputHandler')->handle($this->element); } - protected function getPosition(Image $image, Image $watermark): Point + protected function getPosition(ImageInterface $image, Image $watermark): PointInterface { $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->alignPivot($this->position); diff --git a/src/Drivers/Imagick/Modifiers/RotateModifier.php b/src/Drivers/Imagick/Modifiers/RotateModifier.php index 3fde612e1..304585e75 100644 --- a/src/Drivers/Imagick/Modifiers/RotateModifier.php +++ b/src/Drivers/Imagick/Modifiers/RotateModifier.php @@ -3,6 +3,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; +use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -10,9 +12,14 @@ class RotateModifier extends AbstractRotateModifier implements ModifierInterface { public function apply(ImageInterface $image): ImageInterface { + $background = $this->backgroundColor(); + if (!is_a($background, Color::class)) { + throw new DecoderException('Unable to decode given background color.'); + } + foreach ($image as $frame) { $frame->getCore()->rotateImage( - $this->backgroundColor()->getPixel(), + $background->getPixel(), $this->rotationAngle() ); } diff --git a/src/Exceptions/EncoderException.php b/src/Exceptions/EncoderException.php new file mode 100644 index 000000000..f7d0721a9 --- /dev/null +++ b/src/Exceptions/EncoderException.php @@ -0,0 +1,8 @@ + Date: Sat, 25 Jun 2022 12:18:46 +0200 Subject: [PATCH 0337/1667] Prepare TextWriter for multi line functionality --- src/Drivers/Abstract/AbstractFont.php | 7 +--- src/Drivers/Abstract/AbstractImage.php | 4 +-- src/Drivers/Abstract/AbstractTextWriter.php | 18 ++++++++++ src/Drivers/Gd/Font.php | 7 ++-- src/Drivers/Gd/Modifiers/TextWriter.php | 38 ++++++++++---------- src/Drivers/Imagick/Font.php | 26 ++++++++------ src/Drivers/Imagick/Modifiers/TextWriter.php | 30 ++++++++-------- src/Interfaces/FontInterface.php | 5 +-- src/Typography/Line.php | 16 +++++++++ src/Typography/TextBlock.php | 20 +++++++++++ tests/Drivers/Abstract/AbstractFontTest.php | 3 +- tests/Typography/LineTest.php | 21 +++++++++++ tests/Typography/TextBlockTest.php | 30 ++++++++++++++++ 13 files changed, 167 insertions(+), 58 deletions(-) create mode 100644 src/Drivers/Abstract/AbstractTextWriter.php create mode 100644 src/Typography/Line.php create mode 100644 src/Typography/TextBlock.php create mode 100644 tests/Typography/LineTest.php create mode 100644 tests/Typography/TextBlockTest.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 8991cd904..dd03a8312 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -17,18 +17,13 @@ abstract class AbstractFont implements FontInterface protected $align = 'left'; protected $valign = 'bottom'; - public function __construct(protected string $text, ?callable $init = null) + public function __construct(callable $init = null) { if (is_callable($init)) { $init($this); } } - public function getText(): string - { - return $this->text; - } - public function size(float $size): FontInterface { $this->size = $size; diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2342b29c4..2136c3bd5 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -194,8 +194,8 @@ public function pickColors(int $x, int $y): CollectionInterface public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface { - $font = $this->resolveDriverClass('Font', $text, $init); - $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font); + $font = $this->resolveDriverClass('Font', $init); + $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font, $text); return $this->modify($modifier); } diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php new file mode 100644 index 000000000..9b1e7a3a5 --- /dev/null +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -0,0 +1,18 @@ +hasFilename()) { // calculate box size from gd font $box = new Size(0, 0); - $chars = mb_strlen($this->getText()); + $chars = mb_strlen($text); if ($chars > 0) { $box->setWidth($chars * $this->getGdFontWidth()); $box->setHeight($this->getGdFontHeight()); @@ -37,7 +37,7 @@ public function getBoxSize(): Polygon $this->getSize(), 0, $this->getFilename(), - $this->getText() + $text ); // build polygon from points @@ -47,7 +47,6 @@ public function getBoxSize(): Polygon $polygon->addPoint(new Point($box[2], $box[3])); $polygon->addPoint(new Point($box[0], $box[1])); - return $polygon; } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 3a6b18a68..486dedb20 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -2,20 +2,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; +use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\ModifierInterface; -class TextWriter implements ModifierInterface +class TextWriter extends AbstractTextWriter { - public function __construct( - protected Point $position, - protected Font $font - ) { - // - } - public function apply(ImageInterface $image): ImageInterface { $position = $this->getAlignedPosition(); @@ -23,21 +17,21 @@ public function apply(ImageInterface $image): ImageInterface if ($this->font->hasFilename()) { imagettftext( $frame->getCore(), - $this->font->getSize(), - $this->font->getAngle() * (-1), + $this->getFont()->getSize(), + $this->getFont()->getAngle() * (-1), $position->getX(), $position->getY(), - $this->font->getColor()->toInt(), - $this->font->getFilename(), - $this->font->getText() + $this->getFont()->getColor()->toInt(), + $this->getFont()->getFilename(), + $this->text ); } else { imagestring( $frame->getCore(), - $this->font->getGdFont(), + $this->getFont()->getGdFont(), $position->getX(), $position->getY(), - $this->font->getText(), + $this->text, $this->font->getColor()->toInt() ); } @@ -46,9 +40,9 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - public function getAlignedPosition(): Point + private function getAlignedPosition(): Point { - $poly = $this->font->getBoxSize(); + $poly = $this->font->getBoxSize($this->text); $poly->setPivotPoint($this->position); $poly->align($this->font->getAlign()); @@ -60,4 +54,12 @@ public function getAlignedPosition(): Point return $poly->last(); } + + private function getFont(): Font + { + if (!is_a($this->font, Font::class)) { + throw new FontException('Font is not compatible to current driver.'); + } + return $this->font; + } } diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index e719fe7ab..2d46a71bf 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -5,10 +5,10 @@ use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\ColorInterface; class Font extends AbstractFont { @@ -18,22 +18,28 @@ public function toImagickDraw(): ImagickDraw throw new FontException('No font file specified.'); } - $color = $this->getColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode font color.'); - } - $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); - $draw->setFillColor($color->getPixel()); + $draw->setFillColor($this->getColor()->getPixel()); $draw->setTextAlignment($this->getImagickAlign()); return $draw; } + public function getColor(): ?ColorInterface + { + $color = parent::getColor(); + + if (!is_a($color, Color::class)) { + throw new FontException('Font is not compatible to current driver.'); + } + + return $color; + } + public function getAngle(): float { return parent::getAngle() * (-1); @@ -57,16 +63,16 @@ public function getImagickAlign(): int * * @return Polygon */ - public function getBoxSize(): Polygon + public function getBoxSize(string $text): Polygon { // no text - no box size - if (mb_strlen($this->getText()) === 0) { + if (mb_strlen($text) === 0) { return (new Size(0, 0))->toPolygon(); } $dimensions = (new Imagick())->queryFontMetrics( $this->toImagickDraw(), - $this->getText() + $text ); return (new Size( diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index 4fe1dc2a0..a85fd8b49 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -2,30 +2,24 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; +use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Imagick\Font; +use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\ModifierInterface; -class TextWriter implements ModifierInterface +class TextWriter extends AbstractTextWriter { - public function __construct( - protected Point $position, - protected Font $font - ) { - // - } - public function apply(ImageInterface $image): ImageInterface { $position = $this->getAlignedPosition(); foreach ($image as $frame) { $frame->getCore()->annotateImage( - $this->font->toImagickDraw(), + $this->getFont()->toImagickDraw(), $position->getX(), $position->getY(), - $this->font->getAngle() * (-1), - $this->font->getText() + $this->getFont()->getAngle() * (-1), + $this->text ); } @@ -35,10 +29,10 @@ public function apply(ImageInterface $image): ImageInterface protected function getAlignedPosition(): Point { $position = $this->position; - $box = $this->font->getBoxSize(); + $box = $this->getFont()->getBoxSize($this->text); // adjust y pos - switch ($this->font->getValign()) { + switch ($this->getFont()->getValign()) { case 'top': $position->setY($position->getY() + $box->height()); break; @@ -51,4 +45,12 @@ protected function getAlignedPosition(): Point return $position; } + + private function getFont(): Font + { + if (!is_a($this->font, Font::class)) { + throw new FontException('Font is not compatible to current driver.'); + } + return $this->font; + } } diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 92e470085..66a78b15a 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -7,7 +7,6 @@ interface FontInterface { - public function getText(): string; public function color($color): self; public function getColor(): ?ColorInterface; public function size(float $size): self; @@ -19,5 +18,7 @@ public function getFilename(): ?string; public function hasFilename(): bool; public function align(string $align): self; public function getAlign(): string; - public function getBoxSize(): Polygon; + public function valign(string $align): self; + public function getValign(): string; + public function getBoxSize(string $text): Polygon; } diff --git a/src/Typography/Line.php b/src/Typography/Line.php new file mode 100644 index 000000000..800378695 --- /dev/null +++ b/src/Typography/Line.php @@ -0,0 +1,16 @@ +text; + } +} diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php new file mode 100644 index 000000000..c8cdadf37 --- /dev/null +++ b/src/Typography/TextBlock.php @@ -0,0 +1,20 @@ +push(new Line($line)); + } + } + + public function lines(): array + { + return $this->items; + } +} diff --git a/tests/Drivers/Abstract/AbstractFontTest.php b/tests/Drivers/Abstract/AbstractFontTest.php index e39d345d6..0a5be1820 100644 --- a/tests/Drivers/Abstract/AbstractFontTest.php +++ b/tests/Drivers/Abstract/AbstractFontTest.php @@ -12,7 +12,7 @@ class AbstractFontTest extends TestCase private function getAbstractFontMock() { // create mock - $mock = Mockery::mock(AbstractFont::class, ['test123']) + $mock = Mockery::mock(AbstractFont::class) ->shouldAllowMockingProtectedMethods() ->makePartial(); @@ -34,7 +34,6 @@ private function getAbstractFontMock() public function testConstructor(): void { $mock = $this->getAbstractFontMock(); - $this->assertEquals('test123', $mock->getText()); $this->assertEquals(24.0, $mock->getSize()); $this->assertEquals(30, $mock->getAngle()); $this->assertEquals(__DIR__ . '/AbstractFontTest.php', $mock->getFilename()); diff --git a/tests/Typography/LineTest.php b/tests/Typography/LineTest.php new file mode 100644 index 000000000..fb7de7278 --- /dev/null +++ b/tests/Typography/LineTest.php @@ -0,0 +1,21 @@ +assertInstanceOf(Line::class, $line); + } + + public function testToString(): void + { + $line = new Line('foo'); + $this->assertEquals('foo', (string) $line); + } +} diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php new file mode 100644 index 000000000..bb4a901c6 --- /dev/null +++ b/tests/Typography/TextBlockTest.php @@ -0,0 +1,30 @@ +getTestBlock(); + $this->assertInstanceOf(TextBlock::class, $block); + $this->assertEquals(3, $block->count()); + } + + public function testLines(): void + { + $block = $this->getTestBlock(); + $this->assertCount(3, $block->lines()); + } +} From 0e2aa7f595fe78020e098be5a0252804c43510b5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 25 Jun 2022 16:43:34 +0200 Subject: [PATCH 0338/1667] Implement multiline functionality for TextWriter --- src/Drivers/Abstract/AbstractFont.php | 13 ++++ src/Drivers/Abstract/AbstractTextWriter.php | 6 ++ src/Drivers/Gd/Font.php | 15 +++++ src/Drivers/Gd/Modifiers/TextWriter.php | 72 ++++++++++++++------- src/Interfaces/FontInterface.php | 5 ++ src/Typography/Line.php | 7 ++ src/Typography/TextBlock.php | 13 ++++ 7 files changed, 109 insertions(+), 22 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index dd03a8312..dc2467374 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -16,6 +16,7 @@ abstract class AbstractFont implements FontInterface protected $filename; protected $align = 'left'; protected $valign = 'bottom'; + protected $lineHeight = 1.25; public function __construct(callable $init = null) { @@ -100,4 +101,16 @@ public function getAlign(): string { return $this->align; } + + public function lineHeight(float $height): FontInterface + { + $this->lineHeight = $height; + + return $this; + } + + public function getLineHeight(): float + { + return $this->lineHeight; + } } diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php index 9b1e7a3a5..7c0e196f2 100644 --- a/src/Drivers/Abstract/AbstractTextWriter.php +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -5,6 +5,7 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Typography\TextBlock; abstract class AbstractTextWriter implements ModifierInterface { @@ -15,4 +16,9 @@ public function __construct( ) { // } + + public function getTextBlock(): TextBlock + { + return new TextBlock($this->text); + } } diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 88048d61a..e524dcfef 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -84,4 +84,19 @@ protected function getGdFontHeight(): int return 8; } } + + public function capHeight(): int + { + return $this->getBoxSize('T')->height(); + } + + public function leadingInPixels(): int + { + return intval(round($this->fontSizeInPixels() * $this->getLineHeight())); + } + + public function fontSizeInPixels(): int + { + return $this->getBoxSize('Hy')->height(); + } } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 486dedb20..6d2f4d4d6 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -5,26 +5,39 @@ use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $position = $this->getAlignedPosition(); + $box = $this->getBoundingBox(); + $position = clone $box->last(); + $leading = $this->getFont()->leadingInPixels(); + foreach ($image as $frame) { if ($this->font->hasFilename()) { - imagettftext( - $frame->getCore(), - $this->getFont()->getSize(), - $this->getFont()->getAngle() * (-1), - $position->getX(), - $position->getY(), - $this->getFont()->getColor()->toInt(), - $this->getFont()->getFilename(), - $this->text - ); + $position->moveY($this->getFont()->capHeight()); + $posx = $position->getX(); + $posy = $position->getY(); + foreach ($this->getTextBlock() as $line) { + imagettftext( + $frame->getCore(), + $this->getFont()->getSize(), + $this->getFont()->getAngle() * (-1), + $posx, + $posy, + $this->getFont()->getColor()->toInt(), + $this->getFont()->getFilename(), + $line + ); + $posy += $leading; + } + + // debug + imagepolygon($frame->getCore(), $box->toArray(), 0); } else { imagestring( $frame->getCore(), @@ -40,21 +53,36 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - private function getAlignedPosition(): Point + private function getBoundingBox(): Polygon { - $poly = $this->font->getBoxSize($this->text); - $poly->setPivotPoint($this->position); - - $poly->align($this->font->getAlign()); - $poly->valign($this->font->getValign()); + $size = new Size( + $this->getTextBlock()->longestLine()->width($this->font), + $this->getFont()->leadingInPixels() * $this->getTextBlock()->count() + ); - if ($this->font->getAngle() != 0) { - $poly->rotate($this->font->getAngle()); - } + $poly = $size->toPolygon(); + $poly->setPivotPoint($this->position); + $poly->align($this->getFont()->getAlign()); + $poly->valign($this->getFont()->getValign()); - return $poly->last(); + return $poly; } + // private function getAlignedPosition(): Point + // { + // $poly = $this->font->getBoxSize($this->text); + // $poly->setPivotPoint($this->position); + // + // $poly->align($this->font->getAlign()); + // $poly->valign($this->font->getValign()); + // + // if ($this->font->getAngle() != 0) { + // $poly->rotate($this->font->getAngle()); + // } + // + // return $poly->last(); + // } + private function getFont(): Font { if (!is_a($this->font, Font::class)) { diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 66a78b15a..6ff95e447 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -20,5 +20,10 @@ public function align(string $align): self; public function getAlign(): string; public function valign(string $align): self; public function getValign(): string; + public function lineHeight(float $value): self; + public function getLineHeight(): float; + public function leadingInPixels(): int; + public function fontSizeInPixels(): int; + public function capHeight(): int; public function getBoxSize(string $text): Polygon; } diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 800378695..67e385715 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Typography; +use Intervention\Image\Interfaces\FontInterface; + class Line { public function __construct(protected string $text) @@ -13,4 +15,9 @@ public function __toString(): string { return $this->text; } + + public function width(FontInterface $font): int + { + return $font->getBoxSize($this->text)->width(); + } } diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index c8cdadf37..c77c29c8c 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -17,4 +17,17 @@ public function lines(): array { return $this->items; } + + public function longestLine(): Line + { + $lines = $this->lines(); + usort($lines, function ($a, $b) { + if (mb_strlen($a) === mb_strlen($b)) { + return 0; + } + return (mb_strlen($a) > mb_strlen($b)) ? -1 : 1; + }); + + return $lines[0]; + } } From e1e1291fc51206d9be96d4f04c7905d165551d87 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 26 Jun 2022 09:20:26 +0200 Subject: [PATCH 0339/1667] Add position to typographic text line --- src/Typography/Line.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 67e385715..f4c05a014 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -3,21 +3,29 @@ namespace Intervention\Image\Typography; use Intervention\Image\Interfaces\FontInterface; +use Intervention\Image\Geometry\Point; class Line { + protected $position; + public function __construct(protected string $text) { - // + $this->position = new Point(); } - public function __toString(): string + public function getPosition(): Point { - return $this->text; + return $this->position; } public function width(FontInterface $font): int { return $font->getBoxSize($this->text)->width(); } + + public function __toString(): string + { + return $this->text; + } } From 5dc4e669699c8b5a43e4bb41ca2227308864510f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 26 Jun 2022 20:09:21 +0200 Subject: [PATCH 0340/1667] Extended Textwriter to handle multi line text --- src/Drivers/Abstract/AbstractFont.php | 4 ++ src/Drivers/Gd/Font.php | 2 +- src/Drivers/Gd/Modifiers/TextWriter.php | 69 ++++++---------------- src/Geometry/Size.php | 28 +++++++++ src/Typography/Line.php | 9 ++- src/Typography/TextBlock.php | 76 +++++++++++++++++++++++++ tests/Drivers/Gd/FontTest.php | 30 ++++++++++ tests/Geometry/PointTest.php | 5 ++ tests/Typography/LineTest.php | 12 ++++ tests/Typography/TextBlockTest.php | 53 ++++++++++++++++- 10 files changed, 234 insertions(+), 54 deletions(-) create mode 100644 tests/Drivers/Gd/FontTest.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index dc2467374..2b5059527 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -2,9 +2,13 @@ namespace Intervention\Image\Drivers\Abstract; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Traits\CanHandleInput; +use Intervention\Image\Typography\TextBlock; abstract class AbstractFont implements FontInterface { diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index e524dcfef..79ee8f81b 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -15,7 +15,7 @@ public function getSize(): float } /** - * Calculate size of bounding box of current text + * Calculate size of bounding box of given text * * @return Polygon */ diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 6d2f4d4d6..b08b90b4c 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -5,84 +5,51 @@ use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $box = $this->getBoundingBox(); - $position = clone $box->last(); - $leading = $this->getFont()->leadingInPixels(); + $lines = $this->getTextBlock(); + $boundingBox = $lines->getBoundingBox($this->getFont(), $this->position); + $lines->alignByFont($this->getFont(), $boundingBox->last()); foreach ($image as $frame) { if ($this->font->hasFilename()) { - $position->moveY($this->getFont()->capHeight()); - $posx = $position->getX(); - $posy = $position->getY(); - foreach ($this->getTextBlock() as $line) { + foreach ($lines as $line) { imagettftext( $frame->getCore(), $this->getFont()->getSize(), $this->getFont()->getAngle() * (-1), - $posx, - $posy, + $line->getPosition()->getX(), + $line->getPosition()->getY(), $this->getFont()->getColor()->toInt(), $this->getFont()->getFilename(), $line ); - $posy += $leading; } // debug - imagepolygon($frame->getCore(), $box->toArray(), 0); + imagepolygon($frame->getCore(), $boundingBox->toArray(), 0); } else { - imagestring( - $frame->getCore(), - $this->getFont()->getGdFont(), - $position->getX(), - $position->getY(), - $this->text, - $this->font->getColor()->toInt() - ); + foreach ($lines as $line) { + imagestring( + $frame->getCore(), + $this->getFont()->getGdFont(), + $line->getPosition()->getX(), + $line->getPosition()->getY(), + $line, + $this->font->getColor()->toInt() + ); + imagepolygon($frame->getCore(), $boundingBox->toArray(), 0); + } } } return $image; } - private function getBoundingBox(): Polygon - { - $size = new Size( - $this->getTextBlock()->longestLine()->width($this->font), - $this->getFont()->leadingInPixels() * $this->getTextBlock()->count() - ); - - $poly = $size->toPolygon(); - $poly->setPivotPoint($this->position); - $poly->align($this->getFont()->getAlign()); - $poly->valign($this->getFont()->getValign()); - - return $poly; - } - - // private function getAlignedPosition(): Point - // { - // $poly = $this->font->getBoxSize($this->text); - // $poly->setPivotPoint($this->position); - // - // $poly->align($this->font->getAlign()); - // $poly->valign($this->font->getValign()); - // - // if ($this->font->getAngle() != 0) { - // $poly->rotate($this->font->getAngle()); - // } - // - // return $poly->last(); - // } - private function getFont(): Font { if (!is_a($this->font, Font::class)) { diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 532940b5b..07c15d060 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -40,6 +40,34 @@ public function setHeight(int $height): SizeInterface return $this; } + public function addWidth(int $value): SizeInterface + { + $this->width = $this->width + $value; + + return $this; + } + + public function subWidth(int $value): SizeInterface + { + $this->width = $this->width - $value; + + return $this; + } + + public function addHeight(int $value): SizeInterface + { + $this->height = $this->height + $value; + + return $this; + } + + public function subHeight(int $value): SizeInterface + { + $this->height = $this->height - $value; + + return $this; + } + /** * Get current pivot point * diff --git a/src/Typography/Line.php b/src/Typography/Line.php index f4c05a014..10e98665a 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -19,7 +19,14 @@ public function getPosition(): Point return $this->position; } - public function width(FontInterface $font): int + public function setPosition(Point $point): self + { + $this->position = $point; + + return $this; + } + + public function widthInFont(FontInterface $font): int { return $font->getBoxSize($this->text)->width(); } diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index c77c29c8c..b6174b1a8 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -3,6 +3,10 @@ namespace Intervention\Image\Typography; use Intervention\Image\Collection; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\FontInterface; class TextBlock extends Collection { @@ -13,11 +17,83 @@ public function __construct(string $text) } } + /** + * Set position of each line in text block + * according to given font settings. + * + * @param FontInterface $font + * @param Point $pivot + * @return TextBlock + */ + public function alignByFont(FontInterface $font, Point $pivot = null): self + { + $pivot = $pivot ? $pivot : new Point(); + + $leading = $font->leadingInPixels(); + $x = $pivot->getX(); + $y = $font->hasFilename() ? $pivot->getY() + $font->capHeight() : $pivot->getY(); + + $x_adjustment = 0; + $total_width = $this->longestLine()->widthInFont($font); + foreach ($this as $line) { + $x_adjustment = $font->getAlign() == 'left' ? 0 : $total_width - $line->widthInFont($font); + $x_adjustment = $font->getAlign() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; + $x_adjustment = $font->getAlign() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; + $position = new Point($x + $x_adjustment, $y); + $position->rotate($font->getAngle(), $pivot); + $line->setPosition($position); + $y += $leading; + } + + return $this; + } + + public function getBoundingBox(FontInterface $font, Point $pivot = null): Polygon + { + $pivot = $pivot ? $pivot : new Point(); + + // bounding box + $box = (new Size( + $this->longestLine()->widthInFont($font), + $font->leadingInPixels() * ($this->count() - 1) + $font->capHeight() + ))->toPolygon(); + + // set pivot + $box->setPivotPoint($pivot); + + // align + $box->align($font->getAlign()); + $box->valign($font->getValign()); + + $box->rotate($font->getAngle()); + + return $box; + } + + /** + * Return array of lines in text block + * + * @return array + */ public function lines(): array { return $this->items; } + public function getLine($key): ?Line + { + if (!array_key_exists($key, $this->lines())) { + return null; + } + + return $this->lines()[$key]; + } + + /** + * Return line with most characters of text block + * + * @return Line + */ public function longestLine(): Line { $lines = $this->lines(); diff --git a/tests/Drivers/Gd/FontTest.php b/tests/Drivers/Gd/FontTest.php new file mode 100644 index 000000000..62e02e48a --- /dev/null +++ b/tests/Drivers/Gd/FontTest.php @@ -0,0 +1,30 @@ +size(12); + $this->assertEquals(9, $font->getSize()); + } + + public function testGetGdFont(): void + { + $font = new Font(); + $this->assertEquals(1, $font->getGdFont()); + $font->filename(12); + $this->assertEquals(12, $font->getGdFont()); + } + + public function testCapHeight(): void + { + $font = new Font(); + $this->assertEquals(8, $font->capHeight()); + } +} diff --git a/tests/Geometry/PointTest.php b/tests/Geometry/PointTest.php index 8f95d81d6..98db43933 100644 --- a/tests/Geometry/PointTest.php +++ b/tests/Geometry/PointTest.php @@ -81,5 +81,10 @@ public function testRotate() $point->rotate(90, new Point(0, 0)); $this->assertEquals(-200, $point->getX()); $this->assertEquals(300, $point->getY()); + + $point = new Point(0, 74); + $point->rotate(45, new Point(0, 0)); + $this->assertEquals(-52, $point->getX()); + $this->assertEquals(52, $point->getY()); } } diff --git a/tests/Typography/LineTest.php b/tests/Typography/LineTest.php index fb7de7278..b4c2de6a2 100644 --- a/tests/Typography/LineTest.php +++ b/tests/Typography/LineTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Typography; +use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\Line; @@ -18,4 +19,15 @@ public function testToString(): void $line = new Line('foo'); $this->assertEquals('foo', (string) $line); } + + public function testSetGetPosition(): void + { + $line = new Line('foo'); + $this->assertEquals(0, $line->getPosition()->getX()); + $this->assertEquals(0, $line->getPosition()->getY()); + + $line->setPosition(new Point(10, 11)); + $this->assertEquals(10, $line->getPosition()->getX()); + $this->assertEquals(11, $line->getPosition()->getY()); + } } diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index bb4a901c6..36fb27791 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -4,6 +4,10 @@ use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\TextBlock; +use Intervention\Image\Drivers\Abstract\AbstractFont; +use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Mockery; class TextBlockTest extends TestCase { @@ -11,10 +15,11 @@ protected function getTestBlock(): TextBlock { return new TextBlock(<<getTestBlock(); @@ -27,4 +32,50 @@ public function testLines(): void $block = $this->getTestBlock(); $this->assertCount(3, $block->lines()); } + + public function testGetLine(): void + { + $block = $this->getTestBlock(); + $this->assertEquals('foo', $block->getLine(0)); + $this->assertEquals('FooBar', $block->getLine(1)); + $this->assertEquals('bar', $block->getLine(2)); + } + + public function testAlignByFont(): void + { + $font = Mockery::mock(AbstractFont::class) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $font->shouldReceive('getBoxSize')->andReturn( + new Polygon([ + new Point(-1, -29), + new Point(141, -29), + new Point(141, 98), + new Point(-1, 98), + ]) + ); + + // $font->shouldReceive('capHeight')->andReturn(22); + + $font->shouldReceive('leadingInPixels')->andReturn(74); + $font->angle(45); + + $block = $this->getTestBlock(); // before + $this->assertEquals(0, $block->getLine(0)->getPosition()->getX()); + $this->assertEquals(0, $block->getLine(0)->getPosition()->getY()); + $this->assertEquals(0, $block->getLine(1)->getPosition()->getX()); + $this->assertEquals(0, $block->getLine(1)->getPosition()->getY()); + $this->assertEquals(0, $block->getLine(2)->getPosition()->getX()); + $this->assertEquals(0, $block->getLine(2)->getPosition()->getY()); + + $result = $block->alignByFont($font); // after + $this->assertInstanceOf(TextBlock::class, $result); + $this->assertEquals(0, $block->getLine(0)->getPosition()->getX()); + $this->assertEquals(0, $block->getLine(0)->getPosition()->getY()); + $this->assertEquals(-52, $block->getLine(1)->getPosition()->getX()); + $this->assertEquals(52, $block->getLine(1)->getPosition()->getY()); + $this->assertEquals(-104, $block->getLine(2)->getPosition()->getX()); + $this->assertEquals(104, $block->getLine(2)->getPosition()->getY()); + } } From 6a4a7bfa8fe6540d060354696cf6f52d52e584dd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 4 Jul 2022 15:46:32 +0200 Subject: [PATCH 0341/1667] Refactor GD TextWriter --- src/Drivers/Abstract/AbstractTextWriter.php | 44 ++++++++++++++++++++- src/Drivers/Gd/Modifiers/TextWriter.php | 12 ++---- src/Typography/TextBlock.php | 31 --------------- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php index 7c0e196f2..502f41726 100644 --- a/src/Drivers/Abstract/AbstractTextWriter.php +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -17,8 +17,48 @@ public function __construct( // } - public function getTextBlock(): TextBlock + protected function getFont(): FontInterface { - return new TextBlock($this->text); + return $this->font; + } + + protected function getPosition(): Point + { + return $this->position; + } + + /** + * Build TextBlock object from text string and align every line + * according to text writers font object and position. + * + * @return TextBlock + */ + public function getAlignedTextBlock(): TextBlock + { + $lines = new TextBlock($this->text); + $position = $this->getPosition(); + $font = $this->getFont(); + + $boundingBox = $lines->getBoundingBox($font, $position); + $pivot = $boundingBox->last(); + + $leading = $font->leadingInPixels(); + $blockWidth = $lines->longestLine()->widthInFont($font); + + $x = $pivot->getX(); + $y = $font->hasFilename() ? $pivot->getY() + $font->capHeight() : $pivot->getY(); + $x_adjustment = 0; + + foreach ($lines as $line) { + $x_adjustment = $font->getAlign() == 'left' ? 0 : $blockWidth - $line->widthInFont($font); + $x_adjustment = $font->getAlign() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; + $x_adjustment = $font->getAlign() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; + $position = new Point($x + $x_adjustment, $y); + $position->rotate($font->getAngle(), $pivot); + $line->setPosition($position); + $y += $leading; + } + + return $lines; } } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index b08b90b4c..70ebae213 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -5,16 +5,14 @@ use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; use Intervention\Image\Exceptions\FontException; +use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $lines = $this->getTextBlock(); - $boundingBox = $lines->getBoundingBox($this->getFont(), $this->position); - $lines->alignByFont($this->getFont(), $boundingBox->last()); - + $lines = $this->getAlignedTextBlock(); foreach ($image as $frame) { if ($this->font->hasFilename()) { foreach ($lines as $line) { @@ -29,9 +27,6 @@ public function apply(ImageInterface $image): ImageInterface $line ); } - - // debug - imagepolygon($frame->getCore(), $boundingBox->toArray(), 0); } else { foreach ($lines as $line) { imagestring( @@ -42,7 +37,6 @@ public function apply(ImageInterface $image): ImageInterface $line, $this->font->getColor()->toInt() ); - imagepolygon($frame->getCore(), $boundingBox->toArray(), 0); } } } @@ -50,7 +44,7 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - private function getFont(): Font + protected function getFont(): FontInterface { if (!is_a($this->font, Font::class)) { throw new FontException('Font is not compatible to current driver.'); diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index b6174b1a8..2430f269f 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -17,37 +17,6 @@ public function __construct(string $text) } } - /** - * Set position of each line in text block - * according to given font settings. - * - * @param FontInterface $font - * @param Point $pivot - * @return TextBlock - */ - public function alignByFont(FontInterface $font, Point $pivot = null): self - { - $pivot = $pivot ? $pivot : new Point(); - - $leading = $font->leadingInPixels(); - $x = $pivot->getX(); - $y = $font->hasFilename() ? $pivot->getY() + $font->capHeight() : $pivot->getY(); - - $x_adjustment = 0; - $total_width = $this->longestLine()->widthInFont($font); - foreach ($this as $line) { - $x_adjustment = $font->getAlign() == 'left' ? 0 : $total_width - $line->widthInFont($font); - $x_adjustment = $font->getAlign() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; - $x_adjustment = $font->getAlign() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; - $position = new Point($x + $x_adjustment, $y); - $position->rotate($font->getAngle(), $pivot); - $line->setPosition($position); - $y += $leading; - } - - return $this; - } - public function getBoundingBox(FontInterface $font, Point $pivot = null): Polygon { $pivot = $pivot ? $pivot : new Point(); From da2a1cca7a0c1cdb05861b23d04aa3dd62e0cb29 Mon Sep 17 00:00:00 2001 From: MathiasReker Date: Mon, 4 Jul 2022 17:42:28 +0200 Subject: [PATCH 0342/1667] Single blank line at eof A PHP file without end tag must always end with a single empty line feed. --- src/Intervention/Image/Commands/PsrResponseCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Commands/PsrResponseCommand.php b/src/Intervention/Image/Commands/PsrResponseCommand.php index d75cd903b..4e5153516 100644 --- a/src/Intervention/Image/Commands/PsrResponseCommand.php +++ b/src/Intervention/Image/Commands/PsrResponseCommand.php @@ -42,4 +42,4 @@ public function execute($image) return true; } -} \ No newline at end of file +} From b17d7b9812b469ada0a64cb7f0f6b67b48bf7d1f Mon Sep 17 00:00:00 2001 From: MathiasReker Date: Mon, 4 Jul 2022 17:43:51 +0200 Subject: [PATCH 0343/1667] Logical operators Use && and || logical operators instead of and and or. --- src/Intervention/Image/Gd/Color.php | 6 +++--- src/Intervention/Image/Imagick/Color.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Intervention/Image/Gd/Color.php b/src/Intervention/Image/Gd/Color.php index 44370ba09..145396c0e 100644 --- a/src/Intervention/Image/Gd/Color.php +++ b/src/Intervention/Image/Gd/Color.php @@ -201,9 +201,9 @@ public function differs(AbstractColor $color, $tolerance = 0) ]; return ( - $delta['r'] > $color_tolerance or - $delta['g'] > $color_tolerance or - $delta['b'] > $color_tolerance or + $delta['r'] > $color_tolerance || + $delta['g'] > $color_tolerance || + $delta['b'] > $color_tolerance || $delta['a'] > $alpha_tolerance ); } diff --git a/src/Intervention/Image/Imagick/Color.php b/src/Intervention/Image/Imagick/Color.php index 1e109b171..98a359284 100644 --- a/src/Intervention/Image/Imagick/Color.php +++ b/src/Intervention/Image/Imagick/Color.php @@ -193,9 +193,9 @@ public function differs(AbstractColor $color, $tolerance = 0) ]; return ( - $delta['r'] > $color_tolerance or - $delta['g'] > $color_tolerance or - $delta['b'] > $color_tolerance or + $delta['r'] > $color_tolerance || + $delta['g'] > $color_tolerance || + $delta['b'] > $color_tolerance || $delta['a'] > $alpha_tolerance ); } From ccd335f27549e4d2dfed922f11bdb14455b51404 Mon Sep 17 00:00:00 2001 From: MathiasReker Date: Mon, 4 Jul 2022 17:44:58 +0200 Subject: [PATCH 0344/1667] No unused imports Unused use statements must be removed. --- src/Intervention/Image/Commands/AbstractCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Intervention/Image/Commands/AbstractCommand.php b/src/Intervention/Image/Commands/AbstractCommand.php index e31078ceb..f1b50f3d7 100644 --- a/src/Intervention/Image/Commands/AbstractCommand.php +++ b/src/Intervention/Image/Commands/AbstractCommand.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Commands; -use Intervention\Image\Commands\Argument; abstract class AbstractCommand { From f2e5b3b75f1e08466a934c38ca66b41ff6762a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Reker=20=E2=9A=A1=EF=B8=8F?= <26626066+MathiasReker@users.noreply.github.com> Date: Mon, 4 Jul 2022 17:45:52 +0200 Subject: [PATCH 0345/1667] Remove exstra LF --- src/Intervention/Image/Commands/AbstractCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Intervention/Image/Commands/AbstractCommand.php b/src/Intervention/Image/Commands/AbstractCommand.php index f1b50f3d7..d9cdd7481 100644 --- a/src/Intervention/Image/Commands/AbstractCommand.php +++ b/src/Intervention/Image/Commands/AbstractCommand.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Commands; - abstract class AbstractCommand { /** From b7371cf9fbe46a940341ad4c33a137cc696515b2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 4 Jul 2022 19:11:25 +0200 Subject: [PATCH 0346/1667] Implement multiline TextWriter for Imagick driver --- src/Drivers/Abstract/AbstractFont.php | 19 ++++-- src/Drivers/Gd/Font.php | 15 ----- src/Drivers/Imagick/Font.php | 32 +++------- src/Drivers/Imagick/Modifiers/TextWriter.php | 62 ++++++++++---------- tests/Typography/TextBlockTest.php | 43 +------------- 5 files changed, 55 insertions(+), 116 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 2b5059527..6f2530a3f 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -2,13 +2,9 @@ namespace Intervention\Image\Drivers\Abstract; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Traits\CanHandleInput; -use Intervention\Image\Typography\TextBlock; abstract class AbstractFont implements FontInterface { @@ -117,4 +113,19 @@ public function getLineHeight(): float { return $this->lineHeight; } + + public function leadingInPixels(): int + { + return intval(round($this->fontSizeInPixels() * $this->getLineHeight())); + } + + public function capHeight(): int + { + return $this->getBoxSize('T')->height(); + } + + public function fontSizeInPixels(): int + { + return $this->getBoxSize('Hy')->height(); + } } diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 79ee8f81b..7fb63f514 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -84,19 +84,4 @@ protected function getGdFontHeight(): int return 8; } } - - public function capHeight(): int - { - return $this->getBoxSize('T')->height(); - } - - public function leadingInPixels(): int - { - return intval(round($this->fontSizeInPixels() * $this->getLineHeight())); - } - - public function fontSizeInPixels(): int - { - return $this->getBoxSize('Hy')->height(); - } } diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 2d46a71bf..3ce76d17b 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -24,7 +24,7 @@ public function toImagickDraw(): ImagickDraw $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); $draw->setFillColor($this->getColor()->getPixel()); - $draw->setTextAlignment($this->getImagickAlign()); + $draw->setTextAlignment(Imagick::ALIGN_LEFT); return $draw; } @@ -40,24 +40,6 @@ public function getColor(): ?ColorInterface return $color; } - public function getAngle(): float - { - return parent::getAngle() * (-1); - } - - public function getImagickAlign(): int - { - switch (strtolower($this->getAlign())) { - case 'center': - return Imagick::ALIGN_CENTER; - - case 'right': - return Imagick::ALIGN_RIGHT; - } - - return Imagick::ALIGN_LEFT; - } - /** * Calculate box size of current font * @@ -70,14 +52,14 @@ public function getBoxSize(string $text): Polygon return (new Size(0, 0))->toPolygon(); } - $dimensions = (new Imagick())->queryFontMetrics( - $this->toImagickDraw(), - $text - ); + $draw = $this->toImagickDraw(); + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + $dimensions = (new Imagick())->queryFontMetrics($draw, $text); return (new Size( - intval(round(abs($dimensions['boundingBox']['x1'] - $dimensions['boundingBox']['x2']))), - intval(round(abs($dimensions['boundingBox']['y1'] - $dimensions['boundingBox']['y2']))), + intval(round($dimensions['textWidth'])), + intval(round($dimensions['textHeight'])), ))->toPolygon(); } } diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index a85fd8b49..3887225ba 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -5,48 +5,50 @@ use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Imagick\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Geometry\Point; +use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $position = $this->getAlignedPosition(); + $lines = $this->getAlignedTextBlock(); foreach ($image as $frame) { - $frame->getCore()->annotateImage( - $this->getFont()->toImagickDraw(), - $position->getX(), - $position->getY(), - $this->getFont()->getAngle() * (-1), - $this->text - ); + foreach ($lines as $line) { + $frame->getCore()->annotateImage( + $this->getFont()->toImagickDraw(), + $line->getPosition()->getX(), + $line->getPosition()->getY(), + $this->getFont()->getAngle(), + $line + ); + } } return $image; } - protected function getAlignedPosition(): Point - { - $position = $this->position; - $box = $this->getFont()->getBoxSize($this->text); - - // adjust y pos - switch ($this->getFont()->getValign()) { - case 'top': - $position->setY($position->getY() + $box->height()); - break; - - case 'middle': - case 'center': - $position->setY(intval($position->getY() + round($box->height() / 2))); - break; - } - - return $position; - } - - private function getFont(): Font + // protected function getAlignedPosition(): Point + // { + // $position = $this->position; + // $box = $this->getFont()->getBoxSize($this->text); + // + // // adjust y pos + // switch ($this->getFont()->getValign()) { + // case 'top': + // $position->setY($position->getY() + $box->height()); + // break; + // + // case 'middle': + // case 'center': + // $position->setY(intval($position->getY() + round($box->height() / 2))); + // break; + // } + // + // return $position; + // } + + protected function getFont(): FontInterface { if (!is_a($this->font, Font::class)) { throw new FontException('Font is not compatible to current driver.'); diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index 36fb27791..a97f95a01 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -2,12 +2,9 @@ namespace Intervention\Image\Tests\Typography; +use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\TextBlock; -use Intervention\Image\Drivers\Abstract\AbstractFont; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Polygon; -use Mockery; class TextBlockTest extends TestCase { @@ -40,42 +37,4 @@ public function testGetLine(): void $this->assertEquals('FooBar', $block->getLine(1)); $this->assertEquals('bar', $block->getLine(2)); } - - public function testAlignByFont(): void - { - $font = Mockery::mock(AbstractFont::class) - ->shouldAllowMockingProtectedMethods() - ->makePartial(); - - $font->shouldReceive('getBoxSize')->andReturn( - new Polygon([ - new Point(-1, -29), - new Point(141, -29), - new Point(141, 98), - new Point(-1, 98), - ]) - ); - - // $font->shouldReceive('capHeight')->andReturn(22); - - $font->shouldReceive('leadingInPixels')->andReturn(74); - $font->angle(45); - - $block = $this->getTestBlock(); // before - $this->assertEquals(0, $block->getLine(0)->getPosition()->getX()); - $this->assertEquals(0, $block->getLine(0)->getPosition()->getY()); - $this->assertEquals(0, $block->getLine(1)->getPosition()->getX()); - $this->assertEquals(0, $block->getLine(1)->getPosition()->getY()); - $this->assertEquals(0, $block->getLine(2)->getPosition()->getX()); - $this->assertEquals(0, $block->getLine(2)->getPosition()->getY()); - - $result = $block->alignByFont($font); // after - $this->assertInstanceOf(TextBlock::class, $result); - $this->assertEquals(0, $block->getLine(0)->getPosition()->getX()); - $this->assertEquals(0, $block->getLine(0)->getPosition()->getY()); - $this->assertEquals(-52, $block->getLine(1)->getPosition()->getX()); - $this->assertEquals(52, $block->getLine(1)->getPosition()->getY()); - $this->assertEquals(-104, $block->getLine(2)->getPosition()->getX()); - $this->assertEquals(104, $block->getLine(2)->getPosition()->getY()); - } } From d1ce4a3a83ef62cd856b5ff69076ac53ee3fef85 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 4 Jul 2022 19:42:22 +0200 Subject: [PATCH 0347/1667] Add test for TextBlock::getBoundingBox() --- tests/Typography/TextBlockTest.php | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index a97f95a01..6f95d2121 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -3,8 +3,11 @@ namespace Intervention\Image\Tests\Typography; use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\TextBlock; +use Mockery; class TextBlockTest extends TestCase { @@ -37,4 +40,40 @@ public function testGetLine(): void $this->assertEquals('FooBar', $block->getLine(1)); $this->assertEquals('bar', $block->getLine(2)); } + + public function testLongestLine(): void + { + $block = $this->getTestBlock(); + $result = $block->longestLine(); + $this->assertEquals('FooBar', (string) $result); + } + + public function testGetBoundingBox(): void + { + $block = $this->getTestBlock(); + $font = Mockery::mock(FontInterface::class) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $font->shouldReceive('getBoxSize')->andReturn( + new Polygon([ + new Point(0, 0), + new Point(300, 0), + new Point(300, 150), + new Point(0, 150), + ]) + ); + + $font->shouldReceive('leadingInPixels')->andReturn(30); + $font->shouldReceive('getAlign')->andReturn('left'); + $font->shouldReceive('getValign')->andReturn('bottom'); + $font->shouldReceive('getAngle')->andReturn(0); + $font->shouldReceive('capHeight')->andReturn(22); + + $box = $block->getBoundingBox($font, new Point(10, 15)); + $this->assertEquals(300, $box->width()); + $this->assertEquals(82, $box->height()); + $this->assertEquals(10, $box->getPivotPoint()->getX()); + $this->assertEquals(15, $box->getPivotPoint()->getY()); + } } From bb6d81b96f24bef53f7d714067fe453155a3167e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 4 Jul 2022 20:01:20 +0200 Subject: [PATCH 0348/1667] Fix Imagick versions in test workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 7fcdf2545..dd737c22e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: php: [ '8.0', '8.1' ] - imagemagick: [ '6.9.12-43', '7.1.0-28' ] + imagemagick: [ '6.9.12-55', '7.1.0-40' ] imagick: [ '3.7.0' ] stability: [ prefer-lowest, prefer-stable ] From 58d17f044f028bb90bffc72622c4a31ca41ebc4e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 14:53:30 +0200 Subject: [PATCH 0349/1667] Change box size calculation for Imagick font --- src/Drivers/Gd/Modifiers/TextWriter.php | 5 +++ src/Drivers/Imagick/Font.php | 2 +- src/Drivers/Imagick/Modifiers/TextWriter.php | 35 +++++++++----------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 70ebae213..8a404b448 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -27,6 +27,11 @@ public function apply(ImageInterface $image): ImageInterface $line ); } + + // debug + // $lines = new TextBlock($this->text); + // $box = $lines->getBoundingBox($this->font, $this->position); + // imagepolygon($frame->getCore(), $box->toArray(), 0); } else { foreach ($lines as $line) { imagestring( diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 3ce76d17b..8325a8806 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -59,7 +59,7 @@ public function getBoxSize(string $text): Polygon return (new Size( intval(round($dimensions['textWidth'])), - intval(round($dimensions['textHeight'])), + intval(round($dimensions['ascender'] + $dimensions['descender'])), ))->toPolygon(); } } diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index 3887225ba..b0e1572c3 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -23,31 +23,26 @@ public function apply(ImageInterface $image): ImageInterface $line ); } + + // debug + // $lines = new TextBlock($this->text); + // $box = $lines->getBoundingBox($this->font, $this->position); + // $points = []; + // foreach (array_chunk($box->toArray(), 2) as $p) { + // $points[] = ['x' => $p[0], 'y' => $p[1]]; + // } + // $draw = new \ImagickDraw(); + // $draw->setStrokeOpacity(1); + // $draw->setStrokeColor('black'); + // $draw->setFillColor('transparent'); + // $draw->polygon($points); + // $frame->getCore()->drawImage($draw); + } return $image; } - // protected function getAlignedPosition(): Point - // { - // $position = $this->position; - // $box = $this->getFont()->getBoxSize($this->text); - // - // // adjust y pos - // switch ($this->getFont()->getValign()) { - // case 'top': - // $position->setY($position->getY() + $box->height()); - // break; - // - // case 'middle': - // case 'center': - // $position->setY(intval($position->getY() + round($box->height() / 2))); - // break; - // } - // - // return $position; - // } - protected function getFont(): FontInterface { if (!is_a($this->font, Font::class)) { From eba3948749abc2fc2c7f034cf7a069f0ca80684b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:34:35 +0200 Subject: [PATCH 0350/1667] Change test workflow to run only stable versions --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index dd737c22e..08d5866e6 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,7 +11,7 @@ jobs: php: [ '8.0', '8.1' ] imagemagick: [ '6.9.12-55', '7.1.0-40' ] imagick: [ '3.7.0' ] - stability: [ prefer-lowest, prefer-stable ] + stability: [ prefer-stable ] name: P${{ matrix.php }} - ${{ matrix.stability }} From 55e9f5e9ee70984ce10abbe049afc1ea84d4ec7d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:35:01 +0200 Subject: [PATCH 0351/1667] Fix Imagick URI in test workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 08d5866e6..89c1edab9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,7 +50,7 @@ jobs: - name: Install ImageMagick if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | - curl -o /tmp/ImageMagick.tar.gz -sL https://download.imagemagick.org/ImageMagick/download/releases/ImageMagick-${{ matrix.imagemagick }}.tar.gz + curl -o /tmp/ImageMagick.tar.gz -sL https://imagemagick.org/archive/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( cd /tmp || exit 1 tar xf ImageMagick.tar.gz From 269c0bdfe6f0c59babdc9f279c250849498c0c80 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:43:18 +0200 Subject: [PATCH 0352/1667] Change action name in testing workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 89c1edab9..a5ca24098 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,7 +13,7 @@ jobs: imagick: [ '3.7.0' ] stability: [ prefer-stable ] - name: P${{ matrix.php }} - ${{ matrix.stability }} + name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ImageMaguck ${{ matrix.imagemagick }} steps: - name: Checkout project From 9f773b8d49559d773466b5a4c661cbac54e9e117 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:44:06 +0200 Subject: [PATCH 0353/1667] Fix typo --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index a5ca24098..ae9bec459 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,7 +13,7 @@ jobs: imagick: [ '3.7.0' ] stability: [ prefer-stable ] - name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ImageMaguck ${{ matrix.imagemagick }} + name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ImageMagick ${{ matrix.imagemagick }} steps: - name: Checkout project From b80b0976150b1b54bb85225a09a3f5d20d392225 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 5 Jul 2022 19:54:11 +0200 Subject: [PATCH 0354/1667] Change workflow status badge --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 116625165..e67425400 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ ## PHP Image Manipulation [![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) -[![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) +[![Build Status](https://github.com/Intervention/image/actions/workflows/run-tests.yml/badge.svg)](https://github.com/Intervention/image/actions) [![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. From 6092ec113ffe52eba904cee0cff05cb2da2fc636 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 7 Jul 2022 16:39:27 +0200 Subject: [PATCH 0355/1667] Implement DrawPixelModifier --- src/Drivers/Abstract/AbstractImage.php | 8 ++++ .../Gd/Modifiers/DrawPixelModifier.php | 30 ++++++++++++++ .../Imagick/Modifiers/DrawPixelModifier.php | 41 +++++++++++++++++++ src/Interfaces/ImageInterface.php | 1 + .../Gd/Modifiers/DrawPixelModifierTest.php | 26 ++++++++++++ .../Modifiers/DrawPixelModifierTest.php | 27 ++++++++++++ 6 files changed, 133 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/DrawPixelModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawPixelModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 2136c3bd5..9b71d7786 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -200,6 +200,14 @@ public function text(string $text, int $x, int $y, ?callable $init = null): Imag return $this->modify($modifier); } + public function drawPixel(int $x, int $y, $color = null): ImageInterface + { + $color = $this->handleInput($color); + $modifier = $this->resolveDriverClass('Modifiers\DrawPixelModifier', new Point($x, $y), $color); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php new file mode 100644 index 000000000..3f44a23b5 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php @@ -0,0 +1,30 @@ +getCore(), + $this->position->getX(), + $this->position->getY(), + $this->color->toInt() + ); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php new file mode 100644 index 000000000..00f34ba4a --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -0,0 +1,41 @@ +setFillColor($this->getColor()->getPixel()); + $pixel->point($this->position->getX(), $this->position->getY()); + + foreach ($image as $frame) { + $frame->getCore()->drawImage($pixel); + } + + return $image; + } + + public function getColor(): Color + { + if (!is_a($this->color, Color::class)) { + throw new DecoderException('Unable to decode given pixel color.'); + } + + return $this->color; + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index f69a98254..2ba694d62 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -36,6 +36,7 @@ public function fit(int $width, int $height, string $position = 'center'): Image public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface; public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + public function drawPixel(int $x, int $y, $color = null): ImageInterface; public function getWidth(): int; public function getHeight(): int; public function destroy(): void; diff --git a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php new file mode 100644 index 000000000..0b5521163 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php @@ -0,0 +1,26 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new DrawPixelModifier(new Point(14, 14), new Color(16777215))); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php new file mode 100644 index 000000000..c0f3cf541 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php @@ -0,0 +1,27 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new DrawPixelModifier(new Point(14, 14), new Color(new ImagickPixel('#ffffff')))); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + } +} From a24240acffc8e37f6b620fd88c7c34915702b00a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 8 Jul 2022 17:42:50 +0200 Subject: [PATCH 0356/1667] Add text method to ImageInterface --- src/Interfaces/ImageInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 2ba694d62..4c1c9a5e3 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -21,6 +21,7 @@ public function toWebp(int $quality = 75): EncodedImage; public function toGif(): EncodedImage; public function toPng(): EncodedImage; public function pickColors(int $x, int $y): CollectionInterface; + public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface; public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function greyscale(): ImageInterface; public function blur(int $amount = 5): ImageInterface; From 347942f14ce6cae0c89449421d15bced132e5e14 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 9 Jul 2022 16:15:46 +0200 Subject: [PATCH 0357/1667] Implement HTML color name decoder --- .../Gd/Decoders/HtmlColorNameDecoder.php | 28 ++++ src/Drivers/Gd/InputHandler.php | 16 +- .../Imagick/Decoders/HtmlColorNameDecoder.php | 28 ++++ src/Drivers/Imagick/InputHandler.php | 14 +- src/Traits/CanReadHtmlColorNames.php | 157 ++++++++++++++++++ .../Gd/Decoders/HtmlColorNameDecoderTest.php | 18 ++ .../Decoders/HtmlColorNameDecoderTest.php | 18 ++ 7 files changed, 266 insertions(+), 13 deletions(-) create mode 100644 src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php create mode 100644 src/Traits/CanReadHtmlColorNames.php create mode 100644 tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php create mode 100644 tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php diff --git a/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php b/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php new file mode 100644 index 000000000..59ed6fd28 --- /dev/null +++ b/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php @@ -0,0 +1,28 @@ +hexColorFromColorName($input); + + if (empty($hexcolor)) { + throw new DecoderException('Unable to decode input'); + } + + return parent::decode($hexcolor); + } +} diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 3b66efe29..f32612e6f 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -11,13 +11,15 @@ protected function chain(): AbstractDecoder { return new Decoders\ImageObjectDecoder( new Decoders\ArrayColorDecoder( - new Decoders\RgbStringColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\HtmlColorNameDecoder( + new Decoders\RgbStringColorDecoder( + new Decoders\HexColorDecoder( + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php b/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php new file mode 100644 index 000000000..fba912ea5 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php @@ -0,0 +1,28 @@ +hexColorFromColorName($input); + + if (empty($hexcolor)) { + throw new DecoderException('Unable to decode input'); + } + + return parent::decode($hexcolor); + } +} diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 839d401c0..90577ff19 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -12,12 +12,14 @@ protected function chain(): AbstractDecoder return new Decoders\ImageObjectDecoder( new Decoders\ArrayColorDecoder( new Decoders\HexColorDecoder( - new Decoders\RgbStringColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() + new Decoders\HtmlColorNameDecoder( + new Decoders\RgbStringColorDecoder( + new Decoders\TransparentColorDecoder( + new Decoders\FilePathImageDecoder( + new Decoders\BinaryImageDecoder( + new Decoders\DataUriImageDecoder( + new Decoders\Base64ImageDecoder() + ) ) ) ) diff --git a/src/Traits/CanReadHtmlColorNames.php b/src/Traits/CanReadHtmlColorNames.php new file mode 100644 index 000000000..be5dc6752 --- /dev/null +++ b/src/Traits/CanReadHtmlColorNames.php @@ -0,0 +1,157 @@ + '#FFA07A', + 'salmon' => '#FA8072', + 'darksalmon' => '#E9967A', + 'lightcoral' => '#F08080', + 'indianred' => '#CD5C5C', + 'crimson' => '#DC143C', + 'firebrick' => '#B22222', + 'red' => '#FF0000', + 'darkred' => '#8B0000', + 'coral' => '#FF7F50', + 'tomato' => '#FF6347', + 'orangered' => '#FF4500', + 'gold' => '#FFD700', + 'orange' => '#FFA500', + 'darkorange' => '#FF8C00', + 'lightyellow' => '#FFFFE0', + 'lemonchiffon' => '#FFFACD', + 'lightgoldenrodyellow' => '#FAFAD2', + 'papayawhip' => '#FFEFD5', + 'moccasin' => '#FFE4B5', + 'peachpuff' => '#FFDAB9', + 'palegoldenrod' => '#EEE8AA', + 'khaki' => '#F0E68C', + 'darkkhaki' => '#BDB76B', + 'yellow' => '#FFFF00', + 'lawngreen' => '#7CFC00', + 'chartreuse' => '#7FFF00', + 'limegreen' => '#32CD32', + 'lime' => '#00FF00', + 'forestgreen' => '#228B22', + 'green' => '#008000', + 'darkgreen' => '#006400', + 'greenyellow' => '#ADFF2F', + 'yellowgreen' => '#9ACD32', + 'springgreen' => '#00FF7F', + 'mediumspringgreen' => '#00FA9A', + 'lightgreen' => '#90EE90', + 'palegreen' => '#98FB98', + 'darkseagreen' => '#8FBC8F', + 'mediumseagre' => 'en #3CB371', + 'seagreen' => '#2E8B57', + 'olive' => '#808000', + 'darkolivegreen' => '#556B2F', + 'olivedrab' => '#6B8E23', + 'lightcyan' => '#E0FFFF', + 'cyan' => '#00FFFF', + 'aqua' => '#00FFFF', + 'aquamarine' => '#7FFFD4', + 'mediumaquamarine' => '#66CDAA', + 'paleturquoise' => '#AFEEEE', + 'turquoise' => '#40E0D0', + 'mediumturquoise' => '#48D1CC', + 'darkturquoise' => '#00CED1', + 'lightseagreen' => '#20B2AA', + 'cadetblue' => '#5F9EA0', + 'darkcyan' => '#008B8B', + 'teal' => '#008080', + 'powderblue' => '#B0E0E6', + 'lightblue' => '#ADD8E6', + 'lightskyblue' => '#87CEFA', + 'skyblue' => '#87CEEB', + 'deepskyblue' => '#00BFFF', + 'lightsteelblue' => '#B0C4DE', + 'dodgerblue' => '#1E90FF', + 'cornflowerblue' => '#6495ED', + 'steelblue' => '#4682B4', + 'royalblue' => '#4169E1', + 'blue' => '#0000FF', + 'mediumblue' => '#0000CD', + 'darkblue' => '#00008B', + 'navy' => '#000080', + 'midnightblue' => '#191970', + 'mediumslateblue' => '#7B68EE', + 'slateblue' => '#6A5ACD', + 'darkslateblue' => '#483D8B', + 'lavender' => '#E6E6FA', + 'thistle' => '#D8BFD8', + 'plum' => '#DDA0DD', + 'violet' => '#EE82EE', + 'orchid' => '#DA70D6', + 'fuchsia' => '#FF00FF', + 'magenta' => '#FF00FF', + 'mediumorchid' => '#BA55D3', + 'mediumpurple' => '#9370DB', + 'blueviolet' => '#8A2BE2', + 'darkviolet' => '#9400D3', + 'darkorchid' => '#9932CC', + 'darkmagenta' => '#8B008B', + 'purple' => '#800080', + 'indigo' => '#4B0082', + 'pink' => '#FFC0CB', + 'lightpink' => '#FFB6C1', + 'hotpink' => '#FF69B4', + 'deeppink' => '#FF1493', + 'palevioletred' => '#DB7093', + 'mediumvioletred' => '#C71585', + 'white' => '#FFFFFF', + 'snow' => '#FFFAFA', + 'honeydew' => '#F0FFF0', + 'mintcream' => '#F5FFFA', + 'azure' => '#F0FFFF', + 'aliceblue' => '#F0F8FF', + 'ghostwhite' => '#F8F8FF', + 'whitesmoke' => '#F5F5F5', + 'seashell' => '#FFF5EE', + 'beige' => '#F5F5DC', + 'oldlace' => '#FDF5E6', + 'floralwhite' => '#FFFAF0', + 'ivory' => '#FFFFF0', + 'antiquewhite' => '#FAEBD7', + 'linen' => '#FAF0E6', + 'lavenderblush' => '#FFF0F5', + 'mistyrose' => '#FFE4E1', + 'gainsboro' => '#DCDCDC', + 'lightgray' => '#D3D3D3', + 'silver' => '#C0C0C0', + 'darkgray' => '#A9A9A9', + 'gray' => '#808080', + 'dimgray' => '#696969', + 'lightslategray' => '#778899', + 'slategray' => '#708090', + 'darkslategray' => '#2F4F4F', + 'black' => '#000000', + 'cornsilk' => '#FFF8DC', + 'blanchedalmond' => '#FFEBCD', + 'bisque' => '#FFE4C4', + 'navajowhite' => '#FFDEAD', + 'wheat' => '#F5DEB3', + 'burlywood' => '#DEB887', + 'tan' => '#D2B48C', + 'rosybrown' => '#BC8F8F', + 'sandybrown' => '#F4A460', + 'goldenrod' => '#DAA520', + 'peru' => '#CD853F', + 'chocolate' => '#D2691E', + 'saddlebrown' => '#8B4513', + 'sienna' => '#A0522D', + 'brown' => '#A52A2A', + 'maroon' => '#800000', + ]; + + public function hexColorFromColorName(string $name): ?string + { + if (!array_key_exists($name, $this->color_names)) { + return null; + } + + return $this->color_names[$name]; + } +} diff --git a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php new file mode 100644 index 000000000..8b7135997 --- /dev/null +++ b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php @@ -0,0 +1,18 @@ +decode('tomato'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('ff6347', $color->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php new file mode 100644 index 000000000..4328f4b12 --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php @@ -0,0 +1,18 @@ +decode('tomato'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('ff6347', $color->toHex()); + } +} From e457f3aeb19fff217f34a1a9cd87ac049a5a83ba Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 9 Jul 2022 16:19:33 +0200 Subject: [PATCH 0358/1667] Normalize decoding of html color names to lower case --- src/Traits/CanReadHtmlColorNames.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Traits/CanReadHtmlColorNames.php b/src/Traits/CanReadHtmlColorNames.php index be5dc6752..c02b43676 100644 --- a/src/Traits/CanReadHtmlColorNames.php +++ b/src/Traits/CanReadHtmlColorNames.php @@ -146,8 +146,16 @@ trait CanReadHtmlColorNames 'maroon' => '#800000', ]; + /** + * Transform given html color name to hex color + * or return null, if color name doesn't exist. + * + * @param string $name + * @return null|string + */ public function hexColorFromColorName(string $name): ?string { + $name = strtolower($name); if (!array_key_exists($name, $this->color_names)) { return null; } From a8db65287fe15b3ddcc8b20245cebc8f86b19e3a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 9 Jul 2022 17:58:26 +0200 Subject: [PATCH 0359/1667] Edit readme.md --- readme.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index e67425400..7704f23e1 100644 --- a/readme.md +++ b/readme.md @@ -5,12 +5,12 @@ [![Build Status](https://github.com/Intervention/image/actions/workflows/run-tests.yml/badge.svg)](https://github.com/Intervention/image/actions) [![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) -Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. +Intervention Image is a **image handling and manipulation library written in PHP** providing an easier and expressive way to create, edit, and compose images. GD library or Imagick can be selected as the base layer for all operations. -- Simple interface for -- Driver agnostic +- Simple interface for common tasks +- Interchangable driver architecture - Support for animated images -- Framework-agnostic, will work with any project +- Framework-agnostic - PSR-12 compliant ## Code Examples @@ -20,7 +20,7 @@ Intervention Image is a **PHP image handling and manipulation** library providin $manager = new ImageManager('gd') // open an image file -$image = $manager->make('images/example.jpg'); +$image = $manager->make('images/example.gif'); // resize image instance $image->resize(320, 240); @@ -52,10 +52,10 @@ composer require intervention/image ## Getting started -Learn the [basics](https://image.intervention.io/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/). +Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/v3/). ## License Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). -Copyright 2021 Oliver Vogel +Copyright 2022 Oliver Vogel From e79c5911b10ee3e70f0d30ef6e5d419a9f9bc1fa Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 13 Jul 2022 16:58:08 +0000 Subject: [PATCH 0360/1667] Move Geometry\Resizer to Geometry\Tools\Resizer --- src/Geometry/Size.php | 2 +- src/Geometry/{ => Tools}/Resizer.php | 2 +- src/Traits/CanResizeGeometrically.php | 2 +- tests/Geometry/ResizerTest.php | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) rename src/Geometry/{ => Tools}/Resizer.php (99%) diff --git a/src/Geometry/Size.php b/src/Geometry/Size.php index 07c15d060..3dbf67e48 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Size.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Geometry; -use Intervention\Image\Geometry\Resizer; +use Intervention\Image\Geometry\Tools\Resizer; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; diff --git a/src/Geometry/Resizer.php b/src/Geometry/Tools/Resizer.php similarity index 99% rename from src/Geometry/Resizer.php rename to src/Geometry/Tools/Resizer.php index 6a5c0edd9..d295ead97 100644 --- a/src/Geometry/Resizer.php +++ b/src/Geometry/Tools/Resizer.php @@ -1,6 +1,6 @@ toSize(new Size(200, 100)); $this->assertInstanceOf(Resizer::class, $resizer); From 7ba98cfe5dab0c58229fb89766c247bb7ca1e57b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 13 Jul 2022 19:08:02 +0200 Subject: [PATCH 0361/1667] Add Geometry\Pixel::class --- src/Geometry/Pixel.php | 28 ++++++++++++++++++++++++++++ tests/Geometry/PixelTest.php | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/Geometry/Pixel.php create mode 100644 tests/Geometry/PixelTest.php diff --git a/src/Geometry/Pixel.php b/src/Geometry/Pixel.php new file mode 100644 index 000000000..e1672fdd6 --- /dev/null +++ b/src/Geometry/Pixel.php @@ -0,0 +1,28 @@ +background = $background; + + return $this; + } + + public function background(): ColorInterface + { + return $this->background; + } +} diff --git a/tests/Geometry/PixelTest.php b/tests/Geometry/PixelTest.php new file mode 100644 index 000000000..6719682a0 --- /dev/null +++ b/tests/Geometry/PixelTest.php @@ -0,0 +1,23 @@ +withBackground($color); + $this->assertInstanceOf(ColorInterface::class, $pixel->background()); + $this->assertInstanceOf(Pixel::class, $result); + } +} From d1313a420b9f8bed1d4d1e1660381fbf2f4e85c3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Jul 2022 15:01:00 +0000 Subject: [PATCH 0362/1667] Replace Size::class with Rectangle::class --- src/Drivers/Abstract/AbstractImage.php | 5 +- .../Modifiers/AbstractFitModifier.php | 8 +- .../Modifiers/AbstractPadModifier.php | 4 +- src/Drivers/Gd/Font.php | 10 +- src/Drivers/Gd/Frame.php | 4 +- src/Drivers/Gd/Modifiers/FillModifier.php | 4 +- src/Drivers/Gd/Modifiers/FitModifier.php | 20 +- src/Drivers/Gd/Modifiers/PadDownModifier.php | 6 +- src/Drivers/Gd/Modifiers/ResizeModifier.php | 20 +- src/Drivers/Imagick/Font.php | 8 +- src/Drivers/Imagick/Frame.php | 7 +- src/Drivers/Imagick/Image.php | 4 +- src/Drivers/Imagick/Modifiers/FitModifier.php | 12 +- .../Imagick/Modifiers/PadDownModifier.php | 6 +- .../Imagick/Modifiers/PixelateModifier.php | 7 +- .../Imagick/Modifiers/ResizeModifier.php | 5 +- src/Geometry/{Size.php => Rectangle.php} | 197 ++++------- src/Geometry/Tools/Resizer.php | 88 ++--- src/Interfaces/SizeInterface.php | 11 +- src/Typography/TextBlock.php | 6 +- tests/Drivers/Abstract/AbstractImageTest.php | 14 +- .../Modifiers/AbstractFitModifierTest.php | 16 +- .../Modifiers/AbstractPadModifierTest.php | 16 +- tests/Drivers/Gd/FrameTest.php | 10 +- tests/Drivers/Gd/ImageTest.php | 4 +- tests/Drivers/Imagick/FrameTest.php | 4 +- tests/Drivers/Imagick/ImageTest.php | 4 +- tests/Geometry/RectangleTest.php | 240 +++++++++++++ tests/Geometry/ResizerTest.php | 300 ++++++++-------- tests/Geometry/SizeTest.php | 332 ------------------ 30 files changed, 611 insertions(+), 761 deletions(-) rename src/Geometry/{Size.php => Rectangle.php} (58%) create mode 100644 tests/Geometry/RectangleTest.php delete mode 100644 tests/Geometry/SizeTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 9b71d7786..4521b84d2 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -5,7 +5,7 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -13,7 +13,6 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; -use Intervention\Image\Interfaces\FontInterface; abstract class AbstractImage implements ImageInterface { @@ -22,7 +21,7 @@ abstract class AbstractImage implements ImageInterface public function getSize(): SizeInterface { - return new Size($this->getWidth(), $this->getHeight()); + return new Rectangle($this->getWidth(), $this->getHeight()); } public function size(): SizeInterface diff --git a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php index fa9e6b1bd..3c38bf8f6 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -20,10 +20,10 @@ protected function getCropSize(ImageInterface $image): SizeInterface { $imagesize = $image->getSize(); - $crop = new Size($this->width, $this->height); + $crop = new Rectangle($this->width, $this->height); $crop = $crop->contain( - $imagesize->getWidth(), - $imagesize->getHeight() + $imagesize->width(), + $imagesize->height() )->alignPivotTo($imagesize, $this->position); return $crop; diff --git a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php index 1cd4f1ec6..521dab302 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -26,6 +26,6 @@ protected function getCropSize(ImageInterface $image): SizeInterface protected function getResizeSize(ImageInterface $image): SizeInterface { - return new Size($this->width, $this->height); + return new Rectangle($this->width, $this->height); } } diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 7fb63f514..0371b9628 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -5,7 +5,7 @@ use Intervention\Image\Drivers\Abstract\AbstractFont; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; class Font extends AbstractFont { @@ -23,13 +23,13 @@ public function getBoxSize(string $text): Polygon { if (!$this->hasFilename()) { // calculate box size from gd font - $box = new Size(0, 0); + $box = new Rectangle(0, 0); $chars = mb_strlen($text); if ($chars > 0) { - $box->setWidth($chars * $this->getGdFontWidth()); - $box->setHeight($this->getGdFontHeight()); + $box->withWidth($chars * $this->getGdFontWidth()); + $box->withHeight($this->getGdFontHeight()); } - return $box->toPolygon(); + return $box; } // calculate box size from font file with angle 0 diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 885934c4b..b0e890d56 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -4,7 +4,7 @@ use GdImage; use Intervention\Image\Collection; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -40,7 +40,7 @@ public function unsetCore(): void public function getSize(): SizeInterface { - return new Size(imagesx($this->core), imagesy($this->core)); + return new Rectangle(imagesx($this->core), imagesy($this->core)); } public function getDelay(): float diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 0f7473428..65de556c9 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -45,8 +45,8 @@ protected function fillAllWithColor(Frame $frame): void $frame->getCore(), 0, 0, - $frame->getSize()->getWidth() - 1, - $frame->getSize()->getHeight() - 1, + $frame->getSize()->width() - 1, + $frame->getSize()->height() - 1, $this->color->toInt() ); } diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index cf55b8c0a..9da6944f7 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -26,8 +26,8 @@ protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeI { // create new image $modified = imagecreatetruecolor( - $resize->getWidth(), - $resize->getHeight() + $resize->width(), + $resize->height() ); // get current image @@ -51,20 +51,20 @@ protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeI $current, 0, 0, - $crop->getPivot()->getX(), - $crop->getPivot()->getY(), - $resize->getWidth(), - $resize->getHeight(), - $crop->getWidth(), - $crop->getHeight() + $crop->pivot()->getX(), + $crop->pivot()->getY(), + $resize->width(), + $resize->height(), + $crop->width(), + $crop->height() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); - for ($y = 0; $y < $resize->getHeight(); ++$y) { - for ($x = 0; $x < $resize->getWidth(); ++$x) { + for ($y = 0; $y < $resize->height(); ++$y) { + for ($x = 0; $x < $resize->width(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, diff --git a/src/Drivers/Gd/Modifiers/PadDownModifier.php b/src/Drivers/Gd/Modifiers/PadDownModifier.php index cb80248da..53247d6c1 100644 --- a/src/Drivers/Gd/Modifiers/PadDownModifier.php +++ b/src/Drivers/Gd/Modifiers/PadDownModifier.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -13,13 +13,13 @@ protected function getCropSize(ImageInterface $image): SizeInterface $resize = $this->getResizeSize($image); return $image->getSize() - ->contain($resize->getWidth(), $resize->getHeight()) + ->contain($resize->width(), $resize->height()) ->alignPivotTo($resize, $this->position); } protected function getResizeSize(ImageInterface $image): SizeInterface { - return (new Size($this->width, $this->height)) + return (new Rectangle($this->width, $this->height)) ->resizeDown($image->getWidth(), $image->getHeight()); } } diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 512491f5d..2b91e1c6c 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -34,8 +34,8 @@ protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): { // create new image $modified = imagecreatetruecolor( - $resizeTo->getWidth(), - $resizeTo->getHeight() + $resizeTo->width(), + $resizeTo->height() ); // get current image @@ -57,22 +57,22 @@ protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): imagecopyresampled( $modified, $current, - $resizeTo->getPivot()->getX(), - $resizeTo->getPivot()->getY(), + $resizeTo->pivot()->getX(), + $resizeTo->pivot()->getY(), 0, 0, - $resizeTo->getWidth(), - $resizeTo->getHeight(), - $frame->getSize()->getWidth(), - $frame->getSize()->getHeight() + $resizeTo->width(), + $resizeTo->height(), + $frame->getSize()->width(), + $frame->getSize()->height() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); - for ($y = 0; $y < $resizeTo->getHeight(); ++$y) { - for ($x = 0; $x < $resizeTo->getWidth(); ++$x) { + for ($y = 0; $y < $resizeTo->height(); ++$y) { + for ($x = 0; $x < $resizeTo->width(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 8325a8806..8e2394af2 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -7,7 +7,7 @@ use Intervention\Image\Drivers\Abstract\AbstractFont; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ColorInterface; class Font extends AbstractFont @@ -49,7 +49,7 @@ public function getBoxSize(string $text): Polygon { // no text - no box size if (mb_strlen($text) === 0) { - return (new Size(0, 0))->toPolygon(); + return (new Rectangle(0, 0)); } $draw = $this->toImagickDraw(); @@ -57,9 +57,9 @@ public function getBoxSize(string $text): Polygon $draw->setTextAntialias(true); $dimensions = (new Imagick())->queryFontMetrics($draw, $text); - return (new Size( + return (new Rectangle( intval(round($dimensions['textWidth'])), intval(round($dimensions['ascender'] + $dimensions['descender'])), - ))->toPolygon(); + )); } } diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index 245aff842..895d228b4 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -29,7 +29,10 @@ public function getCore(): Imagick public function getSize(): SizeInterface { - return new Size($this->core->getImageWidth(), $this->core->getImageHeight()); + return new Rectangle( + $this->core->getImageWidth(), + $this->core->getImageHeight() + ); } public function getDelay(): float diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 906322afe..2bcbdec5e 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -44,8 +44,8 @@ public function addFrame(FrameInterface $frame): ImageInterface $size = $frame->getSize(); $imagick->setImagePage( - $size->getWidth(), - $size->getHeight(), + $size->width(), + $size->height(), $frame->getOffsetLeft(), $frame->getOffsetTop() ); diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php index f5465c5f8..910d86e56 100644 --- a/src/Drivers/Imagick/Modifiers/FitModifier.php +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -15,15 +15,15 @@ public function apply(ImageInterface $image): ImageInterface foreach ($image as $frame) { $frame->getCore()->extentImage( - $crop->getWidth(), - $crop->getHeight(), - $crop->getPivot()->getX(), - $crop->getPivot()->getY() + $crop->width(), + $crop->height(), + $crop->pivot()->getX(), + $crop->pivot()->getY() ); $frame->getCore()->scaleImage( - $resize->getWidth(), - $resize->getHeight() + $resize->width(), + $resize->height() ); } diff --git a/src/Drivers/Imagick/Modifiers/PadDownModifier.php b/src/Drivers/Imagick/Modifiers/PadDownModifier.php index e0649857c..f6de2fc4f 100644 --- a/src/Drivers/Imagick/Modifiers/PadDownModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadDownModifier.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -13,13 +13,13 @@ protected function getCropSize(ImageInterface $image): SizeInterface $resize = $this->getResizeSize($image); return $image->getSize() - ->contain($resize->getWidth(), $resize->getHeight()) + ->contain($resize->width(), $resize->height()) ->alignPivotTo($resize, $this->position); } protected function getResizeSize(ImageInterface $image): SizeInterface { - return (new Size($this->width, $this->height)) + return (new Rectangle($this->width, $this->height)) ->resizeDown($image->getWidth(), $image->getHeight()); } } diff --git a/src/Drivers/Imagick/Modifiers/PixelateModifier.php b/src/Drivers/Imagick/Modifiers/PixelateModifier.php index 770f6f330..7552e4010 100644 --- a/src/Drivers/Imagick/Modifiers/PixelateModifier.php +++ b/src/Drivers/Imagick/Modifiers/PixelateModifier.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Imagick\Frame; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -28,10 +27,10 @@ protected function pixelateFrame(Frame $frame): void $size = $frame->getSize(); $frame->getCore()->scaleImage( - max(1, ($size->getWidth() / $this->size)), - max(1, ($size->getHeight() / $this->size)) + max(1, ($size->width() / $this->size)), + max(1, ($size->height() / $this->size)) ); - $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); + $frame->getCore()->scaleImage($size->width(), $size->height()); } } diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index dbf0697a8..83d163c72 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; -use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -20,8 +19,8 @@ public function apply(ImageInterface $image): ImageInterface foreach ($image as $frame) { $frame->getCore()->scaleImage( - $resizeTo->getWidth(), - $resizeTo->getHeight() + $resizeTo->width(), + $resizeTo->height() ); } diff --git a/src/Geometry/Size.php b/src/Geometry/Rectangle.php similarity index 58% rename from src/Geometry/Size.php rename to src/Geometry/Rectangle.php index 3dbf67e48..088ed268a 100644 --- a/src/Geometry/Size.php +++ b/src/Geometry/Rectangle.php @@ -6,123 +6,48 @@ use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; -class Size implements SizeInterface +class Rectangle extends Polygon implements SizeInterface { public function __construct( - protected int $width, - protected int $height, + int $width, + int $height, protected ?Point $pivot = null ) { $this->pivot = $pivot ? $pivot : new Point(); + $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY())); + $this->addPoint(new Point($this->pivot->getX() + $width, $this->pivot->getY())); + $this->addPoint(new Point($this->pivot->getX() + $width, $this->pivot->getY() - $height)); + $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } - public function getWidth(): int + public function withWidth(int $width): self { - return $this->width; - } - - public function getHeight(): int - { - return $this->height; - } - - public function setWidth(int $width): SizeInterface - { - $this->width = $width; - - return $this; - } - - public function setHeight(int $height): SizeInterface - { - $this->height = $height; - - return $this; - } - - public function addWidth(int $value): SizeInterface - { - $this->width = $this->width + $value; - - return $this; - } - - public function subWidth(int $value): SizeInterface - { - $this->width = $this->width - $value; - - return $this; - } - - public function addHeight(int $value): SizeInterface - { - $this->height = $this->height + $value; + $this[1]->setX($this[0]->getX() + $width); + $this[2]->setX($this[3]->getX() + $width); return $this; } - public function subHeight(int $value): SizeInterface + public function withHeight(int $height): self { - $this->height = $this->height - $value; + $this[2]->setY($this[1]->getY() + $height); + $this[3]->setY($this[0]->getY() + $height); return $this; } - /** - * Get current pivot point - * - * @return Point - */ - public function getPivot(): PointInterface + public function pivot(): Point { return $this->pivot; } - public function setPivot(PointInterface $pivot): SizeInterface + public function withPivot(PointInterface $pivot): self { $this->pivot = $pivot; return $this; } - public function getAspectRatio(): float - { - return $this->width / $this->height; - } - - public function fitsInto(SizeInterface $size): bool - { - if ($this->getWidth() > $size->getWidth()) { - return false; - } - - if ($this->getHeight() > $size->getHeight()) { - return false; - } - - return true; - } - - /** - * Determine if size is landscape format - * - * @return boolean - */ - public function isLandscape(): bool - { - return $this->getWidth() > $this->getHeight(); - } - - /** - * Determine if size is portrait format - * - * @return boolean - */ - public function isPortrait(): bool - { - return $this->getWidth() < $this->getHeight(); - } - /** * Aligns current size's pivot point to given position * and moves point automatically by offset. @@ -130,9 +55,10 @@ public function isPortrait(): bool * @param string $position * @param int $offset_x * @param int $offset_y - * @return Size + * @return Rectangle */ - public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface + // TODO: rename method to movePivot + public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): self { switch (strtolower($position)) { case 'top': @@ -140,13 +66,13 @@ public function alignPivot(string $position, int $offset_x = 0, int $offset_y = case 'top-middle': case 'center-top': case 'middle-top': - $x = intval($this->width / 2); + $x = intval($this->width() / 2); $y = 0 + $offset_y; break; case 'top-right': case 'right-top': - $x = $this->width - $offset_x; + $x = $this->width() - $offset_x; $y = 0 + $offset_y; break; @@ -156,7 +82,7 @@ public function alignPivot(string $position, int $offset_x = 0, int $offset_y = case 'center-left': case 'middle-left': $x = 0 + $offset_x; - $y = intval($this->height / 2); + $y = intval($this->height() / 2); break; case 'right': @@ -164,14 +90,14 @@ public function alignPivot(string $position, int $offset_x = 0, int $offset_y = case 'right-middle': case 'center-right': case 'middle-right': - $x = $this->width - $offset_x; - $y = intval($this->height / 2); + $x = $this->width() - $offset_x; + $y = intval($this->height() / 2); break; case 'bottom-left': case 'left-bottom': $x = 0 + $offset_x; - $y = $this->height - $offset_y; + $y = $this->height() - $offset_y; break; case 'bottom': @@ -179,22 +105,22 @@ public function alignPivot(string $position, int $offset_x = 0, int $offset_y = case 'bottom-middle': case 'center-bottom': case 'middle-bottom': - $x = intval($this->width / 2); - $y = $this->height - $offset_y; + $x = intval($this->width() / 2); + $y = $this->height() - $offset_y; break; case 'bottom-right': case 'right-bottom': - $x = $this->width - $offset_x; - $y = $this->height - $offset_y; + $x = $this->width() - $offset_x; + $y = $this->height() - $offset_y; break; case 'center': case 'middle': case 'center-center': case 'middle-middle': - $x = intval($this->width / 2) + $offset_x; - $y = intval($this->height / 2) + $offset_y; + $x = intval($this->width() / 2) + $offset_x; + $y = intval($this->height() / 2) + $offset_y; break; default: @@ -210,12 +136,13 @@ public function alignPivot(string $position, int $offset_x = 0, int $offset_y = return $this; } - public function alignPivotTo(SizeInterface $size, string $position): SizeInterface + // TODO: rename to alignPivot + public function alignPivotTo(SizeInterface $size, string $position): self { - $reference = new Size($size->getWidth(), $size->getHeight()); + $reference = new self($size->width(), $size->height()); $reference->alignPivot($position); - $this->alignPivot($position)->setPivot( + $this->alignPivot($position)->withPivot( $reference->getRelativePositionTo($this) ); @@ -229,36 +156,50 @@ public function alignPivotTo(SizeInterface $size, string $position): SizeInterfa * @param Size $size * @return Point */ - public function getRelativePositionTo(SizeInterface $size): PointInterface + public function getRelativePositionTo(SizeInterface $rectangle): PointInterface { - $x = $this->getPivot()->getX() - $size->getPivot()->getX(); - $y = $this->getPivot()->getY() - $size->getPivot()->getY(); + return new Point( + $this->pivot()->getX() - $rectangle->pivot()->getX(), + $this->pivot()->getY() - $rectangle->pivot()->getY() + ); + } - return new Point($x, $y); + public function getAspectRatio(): float + { + return $this->width() / $this->height(); } - public function toPolygon(): Polygon + public function fitsInto(SizeInterface $size): bool { - $polygon = new Polygon([ - $this->pivot // top/left - ], $this->pivot); + if ($this->width() > $size->width()) { + return false; + } - // top/right - $polygon->addPoint( - new Point($this->pivot->getX() + $this->getWidth(), $this->pivot->getY()) - ); + if ($this->height() > $size->height()) { + return false; + } - // bottom/right - $polygon->addPoint( - new Point($this->pivot->getX() + $this->getWidth(), $this->pivot->getY() - $this->getHeight()) - ); + return true; + } - // bottom/left - $polygon->addPoint( - new Point($this->pivot->getX(), $this->pivot->getY() - $this->getHeight()) - ); + /** + * Determine if size is landscape format + * + * @return boolean + */ + public function isLandscape(): bool + { + return $this->width() > $this->height(); + } - return $polygon; + /** + * Determine if size is portrait format + * + * @return boolean + */ + public function isPortrait(): bool + { + return $this->width() < $this->height(); } protected function getResizer(?int $width = null, ?int $height = null): Resizer diff --git a/src/Geometry/Tools/Resizer.php b/src/Geometry/Tools/Resizer.php index d295ead97..e90d87b5a 100644 --- a/src/Geometry/Tools/Resizer.php +++ b/src/Geometry/Tools/Resizer.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Geometry\Tools; use Intervention\Image\Exceptions\GeometryException; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\SizeInterface; class Resizer @@ -46,7 +46,7 @@ protected function getTargetSize(): SizeInterface throw new GeometryException('Target size needs width and height.'); } - return new Size($this->width, $this->height); + return new Rectangle($this->width, $this->height); } public function toWidth(int $width): self @@ -65,8 +65,8 @@ public function toHeight(int $height): self public function toSize(SizeInterface $size): self { - $this->width = $size->getWidth(); - $this->height = $size->getHeight(); + $this->width = $size->width(); + $this->height = $size->height(); return $this; } @@ -74,7 +74,7 @@ public function toSize(SizeInterface $size): self protected function getProportionalWidth(SizeInterface $size): int { if (! $this->hasTargetHeight()) { - return $size->getWidth(); + return $size->width(); } return (int) round($this->height * $size->getAspectRatio()); @@ -83,7 +83,7 @@ protected function getProportionalWidth(SizeInterface $size): int protected function getProportionalHeight(SizeInterface $size): int { if (! $this->hasTargetWidth()) { - return $size->getHeight(); + return $size->height(); } return (int) round($this->width / $size->getAspectRatio()); @@ -91,14 +91,14 @@ protected function getProportionalHeight(SizeInterface $size): int public function resize(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); if ($width = $this->getTargetWidth()) { - $resized->setWidth($width); + $resized->withWidth($width); } if ($height = $this->getTargetHeight()) { - $resized->setHeight($height); + $resized->withHeight($height); } return $resized; @@ -106,17 +106,17 @@ public function resize(SizeInterface $size): SizeInterface public function resizeDown(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); if ($width = $this->getTargetWidth()) { - $resized->setWidth( - min($width, $size->getWidth()) + $resized->withWidth( + min($width, $size->width()) ); } if ($height = $this->getTargetHeight()) { - $resized->setHeight( - min($height, $size->getHeight()) + $resized->withHeight( + min($height, $size->height()) ); } @@ -125,23 +125,23 @@ public function resizeDown(SizeInterface $size): SizeInterface public function scale(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $resized->setWidth(min( + $resized->withWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth() )); - $resized->setHeight(min( + $resized->withHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight() )); } elseif ($this->hasTargetWidth()) { - $resized->setWidth($this->getTargetWidth()); - $resized->setHeight($this->getProportionalHeight($size)); + $resized->withWidth($this->getTargetWidth()); + $resized->withHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { - $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->getTargetHeight()); + $resized->withWidth($this->getProportionalWidth($size)); + $resized->withHeight($this->getTargetHeight()); } return $resized; @@ -149,36 +149,36 @@ public function scale(SizeInterface $size): SizeInterface public function scaleDown(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $resized->setWidth(min( + $resized->withWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth(), - $size->getWidth() + $size->width() )); - $resized->setHeight(min( + $resized->withHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight(), - $size->getHeight() + $size->height() )); } elseif ($this->hasTargetWidth()) { - $resized->setWidth(min( + $resized->withWidth(min( $this->getTargetWidth(), - $size->getWidth() + $size->width() )); - $resized->setHeight(min( + $resized->withHeight(min( $this->getProportionalHeight($size), - $size->getHeight() + $size->height() )); } elseif ($this->hasTargetHeight()) { - $resized->setWidth(min( + $resized->withWidth(min( $this->getProportionalWidth($size), - $size->getWidth() + $size->width() )); - $resized->setHeight(min( + $resized->withHeight(min( $this->getTargetHeight(), - $size->getHeight() + $size->height() )); } @@ -193,16 +193,16 @@ public function scaleDown(SizeInterface $size): SizeInterface */ public function cover(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); // auto height - $resized->setWidth($this->getTargetWidth()); - $resized->setHeight($this->getProportionalHeight($size)); + $resized->withWidth($this->getTargetWidth()); + $resized->withHeight($this->getProportionalHeight($size)); if ($resized->fitsInto($this->getTargetSize())) { // auto width - $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->getTargetHeight()); + $resized->withWidth($this->getProportionalWidth($size)); + $resized->withHeight($this->getTargetHeight()); } return $resized; @@ -216,16 +216,16 @@ public function cover(SizeInterface $size): SizeInterface */ public function contain(SizeInterface $size): SizeInterface { - $resized = new Size($size->getWidth(), $size->getHeight()); + $resized = new Rectangle($size->width(), $size->height()); // auto height - $resized->setWidth($this->getTargetWidth()); - $resized->setHeight($this->getProportionalHeight($size)); + $resized->withWidth($this->getTargetWidth()); + $resized->withHeight($this->getProportionalHeight($size)); if (!$resized->fitsInto($this->getTargetSize())) { // auto width - $resized->setWidth($this->getProportionalWidth($size)); - $resized->setHeight($this->getTargetHeight()); + $resized->withWidth($this->getProportionalWidth($size)); + $resized->withHeight($this->getTargetHeight()); } return $resized; diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index 7aaef57d3..94579afaa 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -4,11 +4,12 @@ interface SizeInterface { - public function getWidth(): int; - public function getHeight(): int; - public function getPivot(): PointInterface; - public function setWidth(int $width): SizeInterface; - public function setHeight(int $height): SizeInterface; + public function width(): int; + public function height(): int; + public function pivot(): PointInterface; + public function withWidth(int $width): SizeInterface; + public function withHeight(int $height): SizeInterface; + public function withPivot(PointInterface $pivot): SizeInterface; public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index 2430f269f..4593d67f4 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -5,7 +5,7 @@ use Intervention\Image\Collection; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\FontInterface; class TextBlock extends Collection @@ -22,10 +22,10 @@ public function getBoundingBox(FontInterface $font, Point $pivot = null): Polygo $pivot = $pivot ? $pivot : new Point(); // bounding box - $box = (new Size( + $box = (new Rectangle( $this->longestLine()->widthInFont($font), $font->leadingInPixels() * ($this->count() - 1) + $font->capHeight() - ))->toPolygon(); + )); // set pivot $box->setPivotPoint($pivot); diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index dde2be233..21bdd8570 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -5,7 +5,7 @@ use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\EncodedImage; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\FrameInterface; @@ -44,17 +44,17 @@ protected function abstractImageMock(): AbstractImage public function testGetSize(): void { $img = $this->abstractImageMock(); - $this->assertInstanceOf(Size::class, $img->getSize()); - $this->assertEquals(300, $img->getSize()->getWidth()); - $this->assertEquals(200, $img->getSize()->getHeight()); + $this->assertInstanceOf(Rectangle::class, $img->getSize()); + $this->assertEquals(300, $img->getSize()->width()); + $this->assertEquals(200, $img->getSize()->height()); } public function testSizeAlias(): void { $img = $this->abstractImageMock(); - $this->assertInstanceOf(Size::class, $img->getSize()); - $this->assertEquals(300, $img->size()->getWidth()); - $this->assertEquals(200, $img->size()->getHeight()); + $this->assertInstanceOf(Rectangle::class, $img->getSize()); + $this->assertEquals(300, $img->size()->width()); + $this->assertEquals(200, $img->size()->height()); } public function testModify(): void diff --git a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php index f8678f96e..28ebb3f6d 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php @@ -29,10 +29,10 @@ public function testGetCropSize(int $width, int $height, int $expectedWidth, int $image = (new ImageFactory())->newImage($width, $height); $size = $modifier->getCropSize($image); - static::assertSame($expectedWidth, $size->getWidth()); - static::assertSame($expectedHeight, $size->getHeight()); - static::assertSame($expectedX, $size->getPivot()->getX()); - static::assertSame($expectedY, $size->getPivot()->getY()); + static::assertSame($expectedWidth, $size->width()); + static::assertSame($expectedHeight, $size->height()); + static::assertSame($expectedX, $size->pivot()->getX()); + static::assertSame($expectedY, $size->pivot()->getY()); } public function testGetResizeSize(): void @@ -43,10 +43,10 @@ public function testGetResizeSize(): void $size = $modifier->getCropSize($image); $resize = $modifier->getResizeSize($size); - static::assertSame(200, $resize->getWidth()); - static::assertSame(100, $resize->getHeight()); - static::assertSame(0, $resize->getPivot()->getX()); - static::assertSame(0, $resize->getPivot()->getY()); + static::assertSame(200, $resize->width()); + static::assertSame(100, $resize->height()); + static::assertSame(0, $resize->pivot()->getX()); + static::assertSame(0, $resize->pivot()->getY()); } private function getModifier(int $width, int $height, string $position): AbstractFitModifier diff --git a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php index 4308a3bf5..860aa5466 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php @@ -31,10 +31,10 @@ public function testGetCropSize(int $width, int $height, int $expectedWidth, int $image = (new ImageFactory())->newImage($width, $height); $size = $modifier->getCropSize($image); - static::assertSame($expectedWidth, $size->getWidth()); - static::assertSame($expectedHeight, $size->getHeight()); - static::assertSame($expectedX, $size->getPivot()->getX()); - static::assertSame($expectedY, $size->getPivot()->getY()); + static::assertSame($expectedWidth, $size->width()); + static::assertSame($expectedHeight, $size->height()); + static::assertSame($expectedX, $size->pivot()->getX()); + static::assertSame($expectedY, $size->pivot()->getY()); } public function testGetResizeSize(): void @@ -44,10 +44,10 @@ public function testGetResizeSize(): void $image = (new ImageFactory())->newImage(300, 200); $resize = $modifier->getResizeSize($image); - static::assertSame(200, $resize->getWidth()); - static::assertSame(100, $resize->getHeight()); - static::assertSame(0, $resize->getPivot()->getX()); - static::assertSame(0, $resize->getPivot()->getY()); + static::assertSame(200, $resize->width()); + static::assertSame(100, $resize->height()); + static::assertSame(0, $resize->pivot()->getX()); + static::assertSame(0, $resize->pivot()->getY()); } private function getModifier(int $width, int $height, $background, string $position): AbstractPadModifier diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index 422676d2a..a27a42d3f 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -5,7 +5,7 @@ use GdImage; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; /** @@ -36,16 +36,16 @@ public function testSetCore(): void $core1 = imagecreatetruecolor(3, 2); $core2 = imagecreatetruecolor(3, 3); $frame = new Frame($core1); - $this->assertEquals(2, $frame->getSize()->getHeight()); + $this->assertEquals(2, $frame->getSize()->height()); $result = $frame->setCore($core2); - $this->assertInstanceOf(Frame::Class, $result); - $this->assertEquals(3, $frame->getSize()->getHeight()); + $this->assertInstanceOf(Frame::class, $result); + $this->assertEquals(3, $frame->getSize()->height()); } public function testGetSize(): void { $frame = $this->getTestFrame(); - $this->assertInstanceOf(Size::class, $frame->getSize()); + $this->assertInstanceOf(Rectangle::class, $frame->getSize()); } public function testSetGetDelay() diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 27ce28d1f..927450fd2 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -6,7 +6,7 @@ use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; /** @@ -89,7 +89,7 @@ public function testHeight(): void public function testGetSize(): void { - $this->assertInstanceOf(Size::class, $this->image->getSize()); + $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } public function testPickColor(): void diff --git a/tests/Drivers/Imagick/FrameTest.php b/tests/Drivers/Imagick/FrameTest.php index 85e20f10e..015b8ac90 100644 --- a/tests/Drivers/Imagick/FrameTest.php +++ b/tests/Drivers/Imagick/FrameTest.php @@ -6,7 +6,7 @@ use ImagickPixel; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; /** @@ -35,7 +35,7 @@ public function testConstructor(): void public function testGetSize(): void { $frame = $this->getTestFrame(); - $this->assertInstanceOf(Size::class, $frame->getSize()); + $this->assertInstanceOf(Rectangle::class, $frame->getSize()); } public function testSetGetDelay() diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 53e38f2c9..d76e8d198 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -6,7 +6,7 @@ use ImagickPixel; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Geometry\Size; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; /** @@ -85,6 +85,6 @@ public function testHeight(): void public function testGetSize(): void { - $this->assertInstanceOf(Size::class, $this->image->getSize()); + $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } } diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php new file mode 100644 index 000000000..9c42922eb --- /dev/null +++ b/tests/Geometry/RectangleTest.php @@ -0,0 +1,240 @@ +assertEquals(0, $rectangle[0]->getX()); + $this->assertEquals(0, $rectangle[0]->getY()); + $this->assertEquals(300, $rectangle[1]->getX()); + $this->assertEquals(0, $rectangle[1]->getY()); + $this->assertEquals(300, $rectangle[2]->getX()); + $this->assertEquals(-200, $rectangle[2]->getY()); + $this->assertEquals(0, $rectangle[3]->getX()); + $this->assertEquals(-200, $rectangle[3]->getY()); + $this->assertEquals(300, $rectangle->width()); + $this->assertEquals(200, $rectangle->height()); + } + + public function testWithWidth(): void + { + $rectangle = new Rectangle(300, 200); + $this->assertEquals(300, $rectangle->width()); + $rectangle->withWidth(400); + $this->assertEquals(400, $rectangle->width()); + } + + public function testWithHeight(): void + { + $rectangle = new Rectangle(300, 200); + $this->assertEquals(200, $rectangle->height()); + $rectangle->withHeight(800); + $this->assertEquals(800, $rectangle->height()); + } + + public function testGetAspectRatio() + { + $size = new Rectangle(800, 600); + $this->assertEquals(1.33333333333, $size->getAspectRatio()); + + $size = new Rectangle(100, 100); + $this->assertEquals(1, $size->getAspectRatio()); + + $size = new Rectangle(1920, 1080); + $this->assertEquals(1.777777777778, $size->getAspectRatio()); + } + + public function testFitsInto() + { + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(100, 100)); + $this->assertFalse($fits); + + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(1000, 100)); + $this->assertFalse($fits); + + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(100, 1000)); + $this->assertFalse($fits); + + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(800, 600)); + $this->assertTrue($fits); + + $box = new Rectangle(800, 600); + $fits = $box->fitsInto(new Rectangle(1000, 1000)); + $this->assertTrue($fits); + + $box = new Rectangle(100, 100); + $fits = $box->fitsInto(new Rectangle(800, 600)); + $this->assertTrue($fits); + + $box = new Rectangle(100, 100); + $fits = $box->fitsInto(new Rectangle(80, 60)); + $this->assertFalse($fits); + } + + public function testIsLandscape() + { + $box = new Rectangle(100, 100); + $this->assertFalse($box->isLandscape()); + + $box = new Rectangle(100, 200); + $this->assertFalse($box->isLandscape()); + + $box = new Rectangle(300, 200); + $this->assertTrue($box->isLandscape()); + } + + public function testIsPortrait() + { + $box = new Rectangle(100, 100); + $this->assertFalse($box->isPortrait()); + + $box = new Rectangle(200, 100); + $this->assertFalse($box->isPortrait()); + + $box = new Rectangle(200, 300); + $this->assertTrue($box->isPortrait()); + } + + public function testSetGetPivot(): void + { + $box = new Rectangle(800, 600); + $pivot = $box->pivot(); + $this->assertInstanceOf(Point::class, $pivot); + $this->assertEquals(0, $pivot->getX()); + $result = $box->withPivot(new Point(10, 0)); + $this->assertInstanceOf(Rectangle::class, $result); + $this->assertEquals(10, $box->pivot()->getX()); + } + + public function testAlignPivot(): void + { + $box = new Rectangle(640, 480); + $this->assertEquals(0, $box->pivot()->getX()); + $this->assertEquals(0, $box->pivot()->getY()); + + $box->alignPivot('top-left', 3, 3); + $this->assertEquals(3, $box->pivot()->getX()); + $this->assertEquals(3, $box->pivot()->getY()); + + $box->alignPivot('top', 3, 3); + $this->assertEquals(320, $box->pivot()->getX()); + $this->assertEquals(3, $box->pivot()->getY()); + + $box->alignPivot('top-right', 3, 3); + $this->assertEquals(637, $box->pivot()->getX()); + $this->assertEquals(3, $box->pivot()->getY()); + + $box->alignPivot('left', 3, 3); + $this->assertEquals(3, $box->pivot()->getX()); + $this->assertEquals(240, $box->pivot()->getY()); + + $box->alignPivot('center', 3, 3); + $this->assertEquals(323, $box->pivot()->getX()); + $this->assertEquals(243, $box->pivot()->getY()); + + $box->alignPivot('right', 3, 3); + $this->assertEquals(637, $box->pivot()->getX()); + $this->assertEquals(240, $box->pivot()->getY()); + + $box->alignPivot('bottom-left', 3, 3); + $this->assertEquals(3, $box->pivot()->getX()); + $this->assertEquals(477, $box->pivot()->getY()); + + $box->alignPivot('bottom', 3, 3); + $this->assertEquals(320, $box->pivot()->getX()); + $this->assertEquals(477, $box->pivot()->getY()); + + $result = $box->alignPivot('bottom-right', 3, 3); + $this->assertEquals(637, $box->pivot()->getX()); + $this->assertEquals(477, $box->pivot()->getY()); + + $this->assertInstanceOf(Rectangle::class, $result); + } + + public function testAlignPivotTo(): void + { + $container = new Rectangle(800, 600); + $size = new Rectangle(200, 100); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(300, $size->pivot()->getX()); + $this->assertEquals(250, $size->pivot()->getY()); + + $container = new Rectangle(800, 600); + $size = new Rectangle(100, 100); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(350, $size->pivot()->getX()); + $this->assertEquals(250, $size->pivot()->getY()); + + $container = new Rectangle(800, 600); + $size = new Rectangle(800, 600); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(0, $size->pivot()->getX()); + $this->assertEquals(0, $size->pivot()->getY()); + + $container = new Rectangle(100, 100); + $size = new Rectangle(800, 600); + $size->alignPivotTo($container, 'center'); + $this->assertEquals(-350, $size->pivot()->getX()); + $this->assertEquals(-250, $size->pivot()->getY()); + + $container = new Rectangle(100, 100); + $size = new Rectangle(800, 600); + $size->alignPivotTo($container, 'bottom-right'); + $this->assertEquals(-700, $size->pivot()->getX()); + $this->assertEquals(-500, $size->pivot()->getY()); + } + + public function testgetRelativePositionTo(): void + { + $container = new Rectangle(800, 600); + $input = new Rectangle(200, 100); + $container->alignPivot('top-left'); + $input->alignPivot('top-left'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(0, $pos->getX()); + $this->assertEquals(0, $pos->getY()); + + $container = new Rectangle(800, 600); + $input = new Rectangle(200, 100); + $container->alignPivot('center'); + $input->alignPivot('top-left'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(400, $pos->getX()); + $this->assertEquals(300, $pos->getY()); + + $container = new Rectangle(800, 600); + $input = new Rectangle(200, 100); + $container->alignPivot('bottom-right'); + $input->alignPivot('top-right'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(600, $pos->getX()); + $this->assertEquals(600, $pos->getY()); + + $container = new Rectangle(800, 600); + $input = new Rectangle(200, 100); + $container->alignPivot('center'); + $input->alignPivot('center'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(300, $pos->getX()); + $this->assertEquals(250, $pos->getY()); + + $container = new Rectangle(100, 200); + $input = new Rectangle(100, 100); + $container->alignPivot('center'); + $input->alignPivot('center'); + $pos = $container->getRelativePositionTo($input); + $this->assertEquals(0, $pos->getX()); + $this->assertEquals(50, $pos->getY()); + } +} diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/ResizerTest.php index c003b5abd..47f4fea10 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/ResizerTest.php @@ -3,8 +3,8 @@ namespace Intervention\Image\Tests\Geometry; use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Geometry\Tools\Resizer; -use Intervention\Image\Geometry\Size; use PHPUnit\Framework\TestCase; /** @@ -44,325 +44,325 @@ public function testToHeight(): void public function testToSize(): void { $resizer = new Resizer(); - $resizer = $resizer->toSize(new Size(200, 100)); + $resizer = $resizer->toSize(new Rectangle(200, 100)); $this->assertInstanceOf(Resizer::class, $resizer); } public function testResize() { - $size = new Size(300, 200); + $size = new Rectangle(300, 200); $resizer = new Resizer(); $resizer->toWidth(150); $result = $resizer->resize($size); - $this->assertEquals(150, $result->getWidth()); - $this->assertEquals(200, $result->getHeight()); + $this->assertEquals(150, $result->width()); + $this->assertEquals(200, $result->height()); - $size = new Size(300, 200); + $size = new Rectangle(300, 200); $resizer = new Resizer(); $resizer->toWidth(20); $resizer->toHeight(10); $result = $resizer->resize($size); - $this->assertEquals(20, $result->getWidth()); - $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(20, $result->width()); + $this->assertEquals(10, $result->height()); - $size = new Size(300, 200); + $size = new Rectangle(300, 200); $resizer = new Resizer(width: 150); $result = $resizer->resize($size); - $this->assertEquals(150, $result->getWidth()); - $this->assertEquals(200, $result->getHeight()); + $this->assertEquals(150, $result->width()); + $this->assertEquals(200, $result->height()); - $size = new Size(300, 200); + $size = new Rectangle(300, 200); $resizer = new Resizer(height: 10, width: 20); $result = $resizer->resize($size); - $this->assertEquals(20, $result->getWidth()); - $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(20, $result->width()); + $this->assertEquals(10, $result->height()); } public function testResizeDown() { // 800x600 > 1000x2000 = 800x600 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); // 800x600 > 400x1000 = 400x600 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(600, $result->height()); // 800x600 > 1000x400 = 800x400 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(400); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(400, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(400, $result->height()); // 800x600 > 400x300 = 400x300 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(300); $result = $resizer->resizeDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); // 800x600 > 1000xnull = 800x600 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); // 800x600 > nullx1000 = 800x600 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); } public function testScale() { // 800x600 > 1000x2000 = 1000x750 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scale($size); - $this->assertEquals(1000, $result->getWidth()); - $this->assertEquals(750, $result->getHeight()); + $this->assertEquals(1000, $result->width()); + $this->assertEquals(750, $result->height()); // 800x600 > 2000x1000 = 1333x1000 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(2000); $resizer->toHeight(1000); $result = $resizer->scale($size); - $this->assertEquals(1333, $result->getWidth()); - $this->assertEquals(1000, $result->getHeight()); + $this->assertEquals(1333, $result->width()); + $this->assertEquals(1000, $result->height()); // // 800x600 > nullx3000 = 4000x3000 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->getWidth()); - $this->assertEquals(3000, $result->getHeight()); + $this->assertEquals(4000, $result->width()); + $this->assertEquals(3000, $result->height()); // // 800x600 > 8000xnull = 8000x6000 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(8000); $result = $resizer->scale($size); - $this->assertEquals(8000, $result->getWidth()); - $this->assertEquals(6000, $result->getHeight()); + $this->assertEquals(8000, $result->width()); + $this->assertEquals(6000, $result->height()); // // 800x600 > 100x400 = 100x75 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(100); $resizer->toHeight(400); $result = $resizer->scale($size); - $this->assertEquals(100, $result->getWidth()); - $this->assertEquals(75, $result->getHeight()); + $this->assertEquals(100, $result->width()); + $this->assertEquals(75, $result->height()); // // 800x600 > 400x100 = 133x100 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(100); $result = $resizer->scale($size); - $this->assertEquals(133, $result->getWidth()); - $this->assertEquals(100, $result->getHeight()); + $this->assertEquals(133, $result->width()); + $this->assertEquals(100, $result->height()); // // 800x600 > nullx300 = 400x300 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(300); $result = $resizer->scale($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); // // 800x600 > 80xnull = 80x60 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(80); $result = $resizer->scale($size); - $this->assertEquals(80, $result->getWidth()); - $this->assertEquals(60, $result->getHeight()); + $this->assertEquals(80, $result->width()); + $this->assertEquals(60, $result->height()); // // 640x480 > 225xnull = 225x169 - $size = new Size(640, 480); + $size = new Rectangle(640, 480); $resizer = new Resizer(); $resizer->toWidth(225); $result = $resizer->scale($size); - $this->assertEquals(225, $result->getWidth()); - $this->assertEquals(169, $result->getHeight()); + $this->assertEquals(225, $result->width()); + $this->assertEquals(169, $result->height()); // // 640x480 > 223xnull = 223x167 - $size = new Size(640, 480); + $size = new Rectangle(640, 480); $resizer = new Resizer(); $resizer->toWidth(223); $result = $resizer->scale($size); - $this->assertEquals(223, $result->getWidth()); - $this->assertEquals(167, $result->getHeight()); + $this->assertEquals(223, $result->width()); + $this->assertEquals(167, $result->height()); // // 600x800 > 300x300 = 225x300 - $size = new Size(600, 800); + $size = new Rectangle(600, 800); $resizer = new Resizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scale($size); - $this->assertEquals(225, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(225, $result->width()); + $this->assertEquals(300, $result->height()); // // 800x600 > 400x10 = 13x10 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scale($size); - $this->assertEquals(13, $result->getWidth()); - $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(13, $result->width()); + $this->assertEquals(10, $result->height()); // // 800x600 > 1000x1200 = 1000x750 - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(1200); $result = $resizer->scale($size); - $this->assertEquals(1000, $result->getWidth()); - $this->assertEquals(750, $result->getHeight()); + $this->assertEquals(1000, $result->width()); + $this->assertEquals(750, $result->height()); - $size = new Size(12000, 12); + $size = new Rectangle(12000, 12); $resizer = new Resizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->getWidth()); - $this->assertEquals(4, $result->getHeight()); + $this->assertEquals(4000, $result->width()); + $this->assertEquals(4, $result->height()); - $size = new Size(12, 12000); + $size = new Rectangle(12, 12000); $resizer = new Resizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(3, $result->getWidth()); - $this->assertEquals(3000, $result->getHeight()); + $this->assertEquals(3, $result->width()); + $this->assertEquals(3000, $result->height()); - $size = new Size(12000, 6000); + $size = new Rectangle(12000, 6000); $resizer = new Resizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->getWidth()); - $this->assertEquals(2000, $result->getHeight()); + $this->assertEquals(4000, $result->width()); + $this->assertEquals(2000, $result->height()); } public function testScaleDown() { - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(600); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(400, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); + $this->assertEquals(800, $result->width()); + $this->assertEquals(600, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(100); $result = $resizer->scaleDown($size); - $this->assertEquals(100, $result->getWidth()); - $this->assertEquals(75, $result->getHeight()); + $this->assertEquals(100, $result->width()); + $this->assertEquals(75, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(300); $resizer->toHeight(200); $result = $resizer->scaleDown($size); - $this->assertEquals(267, $result->getWidth()); - $this->assertEquals(200, $result->getHeight()); + $this->assertEquals(267, $result->width()); + $this->assertEquals(200, $result->height()); - $size = new Size(600, 800); + $size = new Rectangle(600, 800); $resizer = new Resizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(225, $result->getWidth()); - $this->assertEquals(300, $result->getHeight()); + $this->assertEquals(225, $result->width()); + $this->assertEquals(300, $result->height()); - $size = new Size(800, 600); + $size = new Rectangle(800, 600); $resizer = new Resizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scaleDown($size); - $this->assertEquals(13, $result->getWidth()); - $this->assertEquals(10, $result->getHeight()); + $this->assertEquals(13, $result->width()); + $this->assertEquals(10, $result->height()); } /** @@ -373,22 +373,22 @@ public function testCover($origin, $target, $result): void $resizer = new Resizer(); $resizer->toSize($target); $resized = $resizer->cover($origin); - $this->assertEquals($result->getWidth(), $resized->getWidth()); - $this->assertEquals($result->getHeight(), $resized->getHeight()); + $this->assertEquals($result->width(), $resized->width()); + $this->assertEquals($result->height(), $resized->height()); } public function coverDataProvider(): array { return [ - [new Size(800, 600), new Size(100, 100), new Size(133, 100)], - [new Size(800, 600), new Size(200, 100), new Size(200, 150)], - [new Size(800, 600), new Size(100, 200), new Size(267, 200)], - [new Size(800, 600), new Size(2000, 10), new Size(2000, 1500)], - [new Size(800, 600), new Size(10, 2000), new Size(2667, 2000)], - [new Size(800, 600), new Size(800, 600), new Size(800, 600)], - [new Size(400, 300), new Size(120, 120), new Size(160, 120)], - [new Size(600, 800), new Size(100, 100), new Size(100, 133)], - [new Size(100, 100), new Size(800, 600), new Size(800, 800)], + [new Rectangle(800, 600), new Rectangle(100, 100), new Rectangle(133, 100)], + [new Rectangle(800, 600), new Rectangle(200, 100), new Rectangle(200, 150)], + [new Rectangle(800, 600), new Rectangle(100, 200), new Rectangle(267, 200)], + [new Rectangle(800, 600), new Rectangle(2000, 10), new Rectangle(2000, 1500)], + [new Rectangle(800, 600), new Rectangle(10, 2000), new Rectangle(2667, 2000)], + [new Rectangle(800, 600), new Rectangle(800, 600), new Rectangle(800, 600)], + [new Rectangle(400, 300), new Rectangle(120, 120), new Rectangle(160, 120)], + [new Rectangle(600, 800), new Rectangle(100, 100), new Rectangle(100, 133)], + [new Rectangle(100, 100), new Rectangle(800, 600), new Rectangle(800, 800)], ]; } @@ -400,22 +400,22 @@ public function testContain($origin, $target, $result): void $resizer = new Resizer(); $resizer->toSize($target); $resized = $resizer->contain($origin); - $this->assertEquals($result->getWidth(), $resized->getWidth()); - $this->assertEquals($result->getHeight(), $resized->getHeight()); + $this->assertEquals($result->width(), $resized->width()); + $this->assertEquals($result->height(), $resized->height()); } public function containDataProvider(): array { return [ - [new Size(800, 600), new Size(100, 100), new Size(100, 75)], - [new Size(800, 600), new Size(200, 100), new Size(133, 100)], - [new Size(800, 600), new Size(100, 200), new Size(100, 75)], - [new Size(800, 600), new Size(2000, 10), new Size(13, 10)], - [new Size(800, 600), new Size(10, 2000), new Size(10, 8)], - [new Size(800, 600), new Size(800, 600), new Size(800, 600)], - [new Size(400, 300), new Size(120, 120), new Size(120, 90)], - [new Size(600, 800), new Size(100, 100), new Size(75, 100)], - [new Size(100, 100), new Size(800, 600), new Size(600, 600)], + [new Rectangle(800, 600), new Rectangle(100, 100), new Rectangle(100, 75)], + [new Rectangle(800, 600), new Rectangle(200, 100), new Rectangle(133, 100)], + [new Rectangle(800, 600), new Rectangle(100, 200), new Rectangle(100, 75)], + [new Rectangle(800, 600), new Rectangle(2000, 10), new Rectangle(13, 10)], + [new Rectangle(800, 600), new Rectangle(10, 2000), new Rectangle(10, 8)], + [new Rectangle(800, 600), new Rectangle(800, 600), new Rectangle(800, 600)], + [new Rectangle(400, 300), new Rectangle(120, 120), new Rectangle(120, 90)], + [new Rectangle(600, 800), new Rectangle(100, 100), new Rectangle(75, 100)], + [new Rectangle(100, 100), new Rectangle(800, 600), new Rectangle(600, 600)], ]; } @@ -427,23 +427,23 @@ public function testCrop($origin, $target, $position, $result): void $resizer = new Resizer(); $resizer->toSize($target); $resized = $resizer->crop($origin, $position); - $this->assertEquals($result->getWidth(), $resized->getWidth()); - $this->assertEquals($result->getHeight(), $resized->getHeight()); - $this->assertEquals($result->getPivot()->getX(), $resized->getPivot()->getX()); - $this->assertEquals($result->getPivot()->getY(), $resized->getPivot()->getY()); + $this->assertEquals($result->width(), $resized->width()); + $this->assertEquals($result->height(), $resized->height()); + $this->assertEquals($result->pivot()->getX(), $resized->pivot()->getX()); + $this->assertEquals($result->pivot()->getY(), $resized->pivot()->getY()); } public function cropDataProvider(): array { return [ - [new Size(800, 600), new Size(100, 100), 'center', new Size(100, 100, new Point(350, 250))], - [new Size(800, 600), new Size(200, 100), 'center', new Size(200, 100, new Point(300, 250))], - [new Size(800, 600), new Size(100, 200), 'center', new Size(100, 200, new Point(350, 200))], - [new Size(800, 600), new Size(2000, 10), 'center', new Size(2000, 10, new Point(-600, 295))], - [new Size(800, 600), new Size(10, 2000), 'center', new Size(10, 2000, new Point(395, -700))], - [new Size(800, 600), new Size(800, 600), 'center', new Size(800, 600, new Point(0, 0))], - [new Size(400, 300), new Size(120, 120), 'center', new Size(120, 120, new Point(140, 90))], - [new Size(600, 800), new Size(100, 100), 'center', new Size(100, 100, new Point(250, 350))], + [new Rectangle(800, 600), new Rectangle(100, 100), 'center', new Rectangle(100, 100, new Point(350, 250))], + [new Rectangle(800, 600), new Rectangle(200, 100), 'center', new Rectangle(200, 100, new Point(300, 250))], + [new Rectangle(800, 600), new Rectangle(100, 200), 'center', new Rectangle(100, 200, new Point(350, 200))], + [new Rectangle(800, 600), new Rectangle(2000, 10), 'center', new Rectangle(2000, 10, new Point(-600, 295))], + [new Rectangle(800, 600), new Rectangle(10, 2000), 'center', new Rectangle(10, 2000, new Point(395, -700))], + [new Rectangle(800, 600), new Rectangle(800, 600), 'center', new Rectangle(800, 600, new Point(0, 0))], + [new Rectangle(400, 300), new Rectangle(120, 120), 'center', new Rectangle(120, 120, new Point(140, 90))], + [new Rectangle(600, 800), new Rectangle(100, 100), 'center', new Rectangle(100, 100, new Point(250, 350))], ]; } } diff --git a/tests/Geometry/SizeTest.php b/tests/Geometry/SizeTest.php deleted file mode 100644 index c11031f8f..000000000 --- a/tests/Geometry/SizeTest.php +++ /dev/null @@ -1,332 +0,0 @@ -assertInstanceOf(Size::class, $size); - $this->assertInstanceOf(Point::class, $size->getPivot()); - $this->assertEquals(300, $size->getWidth()); - $this->assertEquals(200, $size->getHeight()); - } - - public function testCompareSizes(): void - { - $size1 = new Size(300, 200); - $size2 = new Size(300, 200); - $size2a = new Size(300, 200, new Point(1, 1)); - $size2b = new Size(300, 200, new Point(1, 1)); - $size3 = new Size(300, 201); - $size4 = new Size(301, 200); - - $this->assertTrue($size1 == $size2); - $this->assertTrue($size2a == $size2b); - $this->assertFalse($size2 == $size2a); - $this->assertFalse($size2 == $size3); - $this->assertFalse($size2 == $size4); - $this->assertFalse($size3 == $size4); - } - - public function testSetGetWidth() - { - $size = new Size(800, 600); - $this->assertEquals(800, $size->getWidth()); - $result = $size->setWidth(30); - $this->assertEquals(30, $size->getWidth()); - $this->assertInstanceOf(Size::class, $result); - } - - public function testSetGetHeight() - { - $size = new Size(800, 600); - $this->assertEquals(600, $size->getHeight()); - $result = $size->setHeight(30); - $this->assertEquals(30, $size->getHeight()); - $this->assertInstanceOf(Size::class, $result); - } - - public function testSetGetPivot(): void - { - $size = new Size(800, 600); - $pivot = $size->getPivot(); - $this->assertInstanceOf(Point::class, $pivot); - $this->assertEquals(0, $pivot->getX()); - $result = $size->setPivot(new Point(10, 0)); - $this->assertInstanceOf(Size::class, $result); - $this->assertEquals(10, $size->getPivot()->getX()); - } - - public function testGetAspectRatio() - { - $size = new Size(800, 600); - $this->assertEquals(1.33333333333, $size->getAspectRatio()); - - $size = new Size(100, 100); - $this->assertEquals(1, $size->getAspectRatio()); - - $size = new Size(1920, 1080); - $this->assertEquals(1.777777777778, $size->getAspectRatio()); - } - - public function testFitsInto() - { - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(100, 100)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(1000, 100)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(100, 1000)); - $this->assertFalse($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(800, 600)); - $this->assertTrue($fits); - - $box = new Size(800, 600); - $fits = $box->fitsInto(new Size(1000, 1000)); - $this->assertTrue($fits); - - $box = new Size(100, 100); - $fits = $box->fitsInto(new Size(800, 600)); - $this->assertTrue($fits); - - $box = new Size(100, 100); - $fits = $box->fitsInto(new Size(80, 60)); - $this->assertFalse($fits); - } - - public function testIsLandscape() - { - $box = new Size(100, 100); - $this->assertFalse($box->isLandscape()); - - $box = new Size(100, 200); - $this->assertFalse($box->isLandscape()); - - $box = new Size(300, 200); - $this->assertTrue($box->isLandscape()); - } - - public function testIsPortrait() - { - $box = new Size(100, 100); - $this->assertFalse($box->isPortrait()); - - $box = new Size(200, 100); - $this->assertFalse($box->isPortrait()); - - $box = new Size(200, 300); - $this->assertTrue($box->isPortrait()); - } - - public function testAlignPivot(): void - { - $box = new Size(640, 480); - $this->assertEquals(0, $box->getPivot()->getX()); - $this->assertEquals(0, $box->getPivot()->getY()); - - $box->alignPivot('top-left', 3, 3); - $this->assertEquals(3, $box->getPivot()->getX()); - $this->assertEquals(3, $box->getPivot()->getY()); - - $box->alignPivot('top', 3, 3); - $this->assertEquals(320, $box->getPivot()->getX()); - $this->assertEquals(3, $box->getPivot()->getY()); - - $box->alignPivot('top-right', 3, 3); - $this->assertEquals(637, $box->getPivot()->getX()); - $this->assertEquals(3, $box->getPivot()->getY()); - - $box->alignPivot('left', 3, 3); - $this->assertEquals(3, $box->getPivot()->getX()); - $this->assertEquals(240, $box->getPivot()->getY()); - - $box->alignPivot('center', 3, 3); - $this->assertEquals(323, $box->getPivot()->getX()); - $this->assertEquals(243, $box->getPivot()->getY()); - - $box->alignPivot('right', 3, 3); - $this->assertEquals(637, $box->getPivot()->getX()); - $this->assertEquals(240, $box->getPivot()->getY()); - - $box->alignPivot('bottom-left', 3, 3); - $this->assertEquals(3, $box->getPivot()->getX()); - $this->assertEquals(477, $box->getPivot()->getY()); - - $box->alignPivot('bottom', 3, 3); - $this->assertEquals(320, $box->getPivot()->getX()); - $this->assertEquals(477, $box->getPivot()->getY()); - - $result = $box->alignPivot('bottom-right', 3, 3); - $this->assertEquals(637, $box->getPivot()->getX()); - $this->assertEquals(477, $box->getPivot()->getY()); - - $this->assertInstanceOf(Size::class, $result); - } - - public function testAlignPivotTo(): void - { - $container = new Size(800, 600); - $size = new Size(200, 100); - $size->alignPivotTo($container, 'center'); - $this->assertEquals(300, $size->getPivot()->getX()); - $this->assertEquals(250, $size->getPivot()->getY()); - - $container = new Size(800, 600); - $size = new Size(100, 100); - $size->alignPivotTo($container, 'center'); - $this->assertEquals(350, $size->getPivot()->getX()); - $this->assertEquals(250, $size->getPivot()->getY()); - - $container = new Size(800, 600); - $size = new Size(800, 600); - $size->alignPivotTo($container, 'center'); - $this->assertEquals(0, $size->getPivot()->getX()); - $this->assertEquals(0, $size->getPivot()->getY()); - - $container = new Size(100, 100); - $size = new Size(800, 600); - $size->alignPivotTo($container, 'center'); - $this->assertEquals(-350, $size->getPivot()->getX()); - $this->assertEquals(-250, $size->getPivot()->getY()); - - $container = new Size(100, 100); - $size = new Size(800, 600); - $size->alignPivotTo($container, 'bottom-right'); - $this->assertEquals(-700, $size->getPivot()->getX()); - $this->assertEquals(-500, $size->getPivot()->getY()); - } - - public function testgetRelativePositionTo(): void - { - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->alignPivot('top-left'); - $input->alignPivot('top-left'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(0, $pos->getX()); - $this->assertEquals(0, $pos->getY()); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->alignPivot('center'); - $input->alignPivot('top-left'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(400, $pos->getX()); - $this->assertEquals(300, $pos->getY()); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->alignPivot('bottom-right'); - $input->alignPivot('top-right'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(600, $pos->getX()); - $this->assertEquals(600, $pos->getY()); - - $container = new Size(800, 600); - $input = new Size(200, 100); - $container->alignPivot('center'); - $input->alignPivot('center'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(300, $pos->getX()); - $this->assertEquals(250, $pos->getY()); - - $container = new Size(100, 200); - $input = new Size(100, 100); - $container->alignPivot('center'); - $input->alignPivot('center'); - $pos = $container->getRelativePositionTo($input); - $this->assertEquals(0, $pos->getX()); - $this->assertEquals(50, $pos->getY()); - } - - public function testToPolygon(): void - { - $size = new Size(300, 200); - $poly = $size->toPolygon(); - $this->assertInstanceOf(Polygon::class, $poly); - $this->assertCount(4, $poly); - $this->assertEquals(0, $poly[0]->getX()); - $this->assertEquals(0, $poly[0]->getY()); - $this->assertEquals(300, $poly[1]->getX()); - $this->assertEquals(0, $poly[1]->getY()); - $this->assertEquals(300, $poly[2]->getX()); - $this->assertEquals(-200, $poly[2]->getY()); - $this->assertEquals(0, $poly[3]->getX()); - $this->assertEquals(-200, $poly[3]->getY()); - } - - public function testResize(): void - { - $size = new Size(300, 200); - $result = $size->resize(120, 150); - $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->resize(height: 100); - $this->assertInstanceOf(Size::class, $result); - } - - public function testResizeDown(): void - { - $size = new Size(300, 200); - $result = $size->resizeDown(120, 150); - $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->resizeDown(height: 100); - $this->assertInstanceOf(Size::class, $result); - } - - public function testScale(): void - { - $size = new Size(300, 200); - $result = $size->scale(120, 150); - $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->scale(height: 100); - $this->assertInstanceOf(Size::class, $result); - } - - public function testScaleDown(): void - { - $size = new Size(300, 200); - $result = $size->scaleDown(120, 150); - $this->assertInstanceOf(Size::class, $result); - - $size = new Size(300, 200); - $result = $size->scaleDown(height: 100); - $this->assertInstanceOf(Size::class, $result); - } - - public function testCover(): void - { - $size = new Size(300, 200); - $result = $size->cover(120, 150); - $this->assertInstanceOf(Size::class, $result); - } - - public function testContain(): void - { - $size = new Size(100, 100); - $result = $size->contain(800, 600); - $this->assertInstanceOf(Size::class, $result); - $this->assertEquals(600, $result->getWidth()); - $this->assertEquals(600, $result->getHeight()); - } -} From 2c4a6921d864d109945250c4d0f068b3182955ae Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Jul 2022 19:28:32 +0200 Subject: [PATCH 0363/1667] Rename Rectangle::alignPivot() to movePivot() --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 4 +- .../Imagick/Modifiers/PlaceModifier.php | 4 +- src/Geometry/Rectangle.php | 10 ++--- src/Geometry/Tools/Resizer.php | 2 +- src/Interfaces/SizeInterface.php | 2 +- tests/Geometry/RectangleTest.php | 38 +++++++++---------- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index b90233c99..1c35c435b 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -54,8 +54,8 @@ protected function decodeWatermark(): Image protected function getPosition(ImageInterface $image, ImageInterface $watermark): PointInterface { - $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); - $watermark_size = $watermark->getSize()->alignPivot($this->position); + $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->movePivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index 894ba1538..d6181c422 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -47,8 +47,8 @@ protected function decodeWatermark(): Image protected function getPosition(ImageInterface $image, Image $watermark): PointInterface { - $image_size = $image->getSize()->alignPivot($this->position, $this->offset_x, $this->offset_y); - $watermark_size = $watermark->getSize()->alignPivot($this->position); + $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); + $watermark_size = $watermark->getSize()->movePivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 088ed268a..57f9dc0b7 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -49,7 +49,7 @@ public function withPivot(PointInterface $pivot): self } /** - * Aligns current size's pivot point to given position + * Move current pivot of current rectangle to given position * and moves point automatically by offset. * * @param string $position @@ -57,8 +57,7 @@ public function withPivot(PointInterface $pivot): self * @param int $offset_y * @return Rectangle */ - // TODO: rename method to movePivot - public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): self + public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0): self { switch (strtolower($position)) { case 'top': @@ -136,13 +135,12 @@ public function alignPivot(string $position, int $offset_x = 0, int $offset_y = return $this; } - // TODO: rename to alignPivot public function alignPivotTo(SizeInterface $size, string $position): self { $reference = new self($size->width(), $size->height()); - $reference->alignPivot($position); + $reference->movePivot($position); - $this->alignPivot($position)->withPivot( + $this->movePivot($position)->withPivot( $reference->getRelativePositionTo($this) ); diff --git a/src/Geometry/Tools/Resizer.php b/src/Geometry/Tools/Resizer.php index e90d87b5a..62f0f0dbd 100644 --- a/src/Geometry/Tools/Resizer.php +++ b/src/Geometry/Tools/Resizer.php @@ -241,7 +241,7 @@ public function contain(SizeInterface $size): SizeInterface public function crop(SizeInterface $size, string $position = 'top-left'): SizeInterface { return $this->resize($size)->alignPivotTo( - $size->alignPivot($position), + $size->movePivot($position), $position ); } diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index 94579afaa..c8a29b8ea 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -14,7 +14,7 @@ public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; public function isPortrait(): bool; - public function alignPivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; + public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0): SizeInterface; public function alignPivotTo(SizeInterface $size, string $position): SizeInterface; public function getRelativePositionTo(SizeInterface $size): PointInterface; public function resize(?int $width = null, ?int $height = null): SizeInterface; diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index 9c42922eb..ddc9e1020 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -123,39 +123,39 @@ public function testAlignPivot(): void $this->assertEquals(0, $box->pivot()->getX()); $this->assertEquals(0, $box->pivot()->getY()); - $box->alignPivot('top-left', 3, 3); + $box->movePivot('top-left', 3, 3); $this->assertEquals(3, $box->pivot()->getX()); $this->assertEquals(3, $box->pivot()->getY()); - $box->alignPivot('top', 3, 3); + $box->movePivot('top', 3, 3); $this->assertEquals(320, $box->pivot()->getX()); $this->assertEquals(3, $box->pivot()->getY()); - $box->alignPivot('top-right', 3, 3); + $box->movePivot('top-right', 3, 3); $this->assertEquals(637, $box->pivot()->getX()); $this->assertEquals(3, $box->pivot()->getY()); - $box->alignPivot('left', 3, 3); + $box->movePivot('left', 3, 3); $this->assertEquals(3, $box->pivot()->getX()); $this->assertEquals(240, $box->pivot()->getY()); - $box->alignPivot('center', 3, 3); + $box->movePivot('center', 3, 3); $this->assertEquals(323, $box->pivot()->getX()); $this->assertEquals(243, $box->pivot()->getY()); - $box->alignPivot('right', 3, 3); + $box->movePivot('right', 3, 3); $this->assertEquals(637, $box->pivot()->getX()); $this->assertEquals(240, $box->pivot()->getY()); - $box->alignPivot('bottom-left', 3, 3); + $box->movePivot('bottom-left', 3, 3); $this->assertEquals(3, $box->pivot()->getX()); $this->assertEquals(477, $box->pivot()->getY()); - $box->alignPivot('bottom', 3, 3); + $box->movePivot('bottom', 3, 3); $this->assertEquals(320, $box->pivot()->getX()); $this->assertEquals(477, $box->pivot()->getY()); - $result = $box->alignPivot('bottom-right', 3, 3); + $result = $box->movePivot('bottom-right', 3, 3); $this->assertEquals(637, $box->pivot()->getX()); $this->assertEquals(477, $box->pivot()->getY()); @@ -199,40 +199,40 @@ public function testgetRelativePositionTo(): void { $container = new Rectangle(800, 600); $input = new Rectangle(200, 100); - $container->alignPivot('top-left'); - $input->alignPivot('top-left'); + $container->movePivot('top-left'); + $input->movePivot('top-left'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(0, $pos->getY()); $container = new Rectangle(800, 600); $input = new Rectangle(200, 100); - $container->alignPivot('center'); - $input->alignPivot('top-left'); + $container->movePivot('center'); + $input->movePivot('top-left'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(400, $pos->getX()); $this->assertEquals(300, $pos->getY()); $container = new Rectangle(800, 600); $input = new Rectangle(200, 100); - $container->alignPivot('bottom-right'); - $input->alignPivot('top-right'); + $container->movePivot('bottom-right'); + $input->movePivot('top-right'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(600, $pos->getX()); $this->assertEquals(600, $pos->getY()); $container = new Rectangle(800, 600); $input = new Rectangle(200, 100); - $container->alignPivot('center'); - $input->alignPivot('center'); + $container->movePivot('center'); + $input->movePivot('center'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(300, $pos->getX()); $this->assertEquals(250, $pos->getY()); $container = new Rectangle(100, 200); $input = new Rectangle(100, 100); - $container->alignPivot('center'); - $input->alignPivot('center'); + $container->movePivot('center'); + $input->movePivot('center'); $pos = $container->getRelativePositionTo($input); $this->assertEquals(0, $pos->getX()); $this->assertEquals(50, $pos->getY()); From 16cc19a5d0ac0c19df3e684d213fec0e4ff34201 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 19 Jul 2022 19:29:13 +0200 Subject: [PATCH 0364/1667] Remove unused methods from Polygon::class - Polygon::alignPivot() - Polygon::valignPivot() --- src/Geometry/Polygon.php | 63 ---------------------------------- tests/Geometry/PolygonTest.php | 36 ------------------- 2 files changed, 99 deletions(-) diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index f7d1eef33..71291f61f 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -254,69 +254,6 @@ public function getCenterPoint(): Point ); } - /** - * Align pivot point to given horizontal position - * - * @param string $position - * @return Polygon - */ - public function alignPivot(string $position): self - { - switch (strtolower($position)) { - case 'center': - $this->pivot->setX( - intval(($this->getMostRightPoint()->getX() + $this->getMostLeftPoint()->getX()) / 2) - ); - break; - - case 'right': - $this->pivot->setX( - $this->getMostRightPoint()->getX() - ); - break; - - case 'left': - $this->pivot->setX( - $this->getMostLeftPoint()->getX() - ); - break; - } - - return $this; - } - - /** - * Align pivot point to given vertical position - * - * @param string $position - * @return Polygon - */ - public function valignPivot(string $position): self - { - switch (strtolower($position)) { - case 'center': - case 'middle': - $this->pivot->setY( - intval(($this->getMostTopPoint()->getY() + $this->getMostBottomPoint()->getY()) / 2) - ); - break; - - case 'top': - $this->pivot->setY( - $this->getMostTopPoint()->getY() - ); - break; - - case 'bottom': - $this->pivot->setY( - $this->getMostBottomPoint()->getY() - ); - break; - } - - return $this; - } - /** * Align all points of polygon horizontally to given position around pivot point * diff --git a/tests/Geometry/PolygonTest.php b/tests/Geometry/PolygonTest.php index 3ed904de0..8428a0cf0 100644 --- a/tests/Geometry/PolygonTest.php +++ b/tests/Geometry/PolygonTest.php @@ -125,42 +125,6 @@ public function testGetPivotPoint(): void $this->assertInstanceOf(Point::class, $poly->getPivotPoint()); } - public function testAlignPivot(): void - { - $poly = new Polygon([ - new Point(12, 45), - new Point(-24, -49), - new Point(3, 566), - ]); - - $this->assertEquals(0, $poly->getPivotPoint()->getX()); - $this->assertEquals(0, $poly->getPivotPoint()->getY()); - - $result = $poly->alignPivot('center'); - $this->assertInstanceOf(Polygon::class, $result); - - $this->assertEquals(-6, $result->getPivotPoint()->getX()); - $this->assertEquals(0, $result->getPivotPoint()->getY()); - } - - public function testValignPivot(): void - { - $poly = new Polygon([ - new Point(12, 45), - new Point(-24, -50), - new Point(3, 566), - ]); - - $this->assertEquals(0, $poly->getPivotPoint()->getX()); - $this->assertEquals(0, $poly->getPivotPoint()->getY()); - - $result = $poly->valignPivot('middle'); - $this->assertInstanceOf(Polygon::class, $result); - - $this->assertEquals(0, $result->getPivotPoint()->getX()); - $this->assertEquals(258, $result->getPivotPoint()->getY()); - } - public function testGetMostLeftPoint(): void { $poly = new Polygon([ From b30fcc4a6ae90c7540553367e2150aee38dae16c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 16:02:49 +0200 Subject: [PATCH 0365/1667] Rename Resizer::class to RectangleResizer::class --- src/Geometry/Rectangle.php | 6 +- .../{Resizer.php => RectangleResizer.php} | 2 +- ...sizerTest.php => RectangleResizerTest.php} | 116 +++++++++--------- 3 files changed, 62 insertions(+), 62 deletions(-) rename src/Geometry/Tools/{Resizer.php => RectangleResizer.php} (99%) rename tests/Geometry/{ResizerTest.php => RectangleResizerTest.php} (84%) diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 57f9dc0b7..8e0ba2b7e 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Geometry; -use Intervention\Image\Geometry\Tools\Resizer; +use Intervention\Image\Geometry\Tools\RectangleResizer; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -200,9 +200,9 @@ public function isPortrait(): bool return $this->width() < $this->height(); } - protected function getResizer(?int $width = null, ?int $height = null): Resizer + protected function getResizer(?int $width = null, ?int $height = null): RectangleResizer { - return new Resizer($width, $height); + return new RectangleResizer($width, $height); } public function resize(?int $width = null, ?int $height = null): SizeInterface diff --git a/src/Geometry/Tools/Resizer.php b/src/Geometry/Tools/RectangleResizer.php similarity index 99% rename from src/Geometry/Tools/Resizer.php rename to src/Geometry/Tools/RectangleResizer.php index 62f0f0dbd..63dbf6a0b 100644 --- a/src/Geometry/Tools/Resizer.php +++ b/src/Geometry/Tools/RectangleResizer.php @@ -6,7 +6,7 @@ use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\SizeInterface; -class Resizer +class RectangleResizer { public function __construct( protected ?int $width = null, diff --git a/tests/Geometry/ResizerTest.php b/tests/Geometry/RectangleResizerTest.php similarity index 84% rename from tests/Geometry/ResizerTest.php rename to tests/Geometry/RectangleResizerTest.php index 47f4fea10..ea505fba9 100644 --- a/tests/Geometry/ResizerTest.php +++ b/tests/Geometry/RectangleResizerTest.php @@ -4,61 +4,61 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Rectangle; -use Intervention\Image\Geometry\Tools\Resizer; +use Intervention\Image\Geometry\Tools\RectangleResizer; use PHPUnit\Framework\TestCase; /** - * @covers \Intervention\Image\Geometry\Resizer + * @covers \Intervention\Image\Geometry\Tools\RectangleResizer */ -class ResizerTest extends TestCase +class RectangleRectangleResizerTest extends TestCase { public function testMake(): void { - $resizer = Resizer::to(); - $this->assertInstanceOf(Resizer::class, $resizer); + $resizer = RectangleResizer::to(); + $this->assertInstanceOf(RectangleResizer::class, $resizer); - $resizer = Resizer::to(height: 100); - $this->assertInstanceOf(Resizer::class, $resizer); + $resizer = RectangleResizer::to(height: 100); + $this->assertInstanceOf(RectangleResizer::class, $resizer); - $resizer = Resizer::to(100); - $this->assertInstanceOf(Resizer::class, $resizer); + $resizer = RectangleResizer::to(100); + $this->assertInstanceOf(RectangleResizer::class, $resizer); - $resizer = Resizer::to(100, 100); - $this->assertInstanceOf(Resizer::class, $resizer); + $resizer = RectangleResizer::to(100, 100); + $this->assertInstanceOf(RectangleResizer::class, $resizer); } public function testToWidth(): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $result = $resizer->toWidth(100); - $this->assertInstanceOf(Resizer::class, $result); + $this->assertInstanceOf(RectangleResizer::class, $result); } public function testToHeight(): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $result = $resizer->toHeight(100); - $this->assertInstanceOf(Resizer::class, $result); + $this->assertInstanceOf(RectangleResizer::class, $result); } public function testToSize(): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer = $resizer->toSize(new Rectangle(200, 100)); - $this->assertInstanceOf(Resizer::class, $resizer); + $this->assertInstanceOf(RectangleResizer::class, $resizer); } public function testResize() { $size = new Rectangle(300, 200); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(150); $result = $resizer->resize($size); $this->assertEquals(150, $result->width()); $this->assertEquals(200, $result->height()); $size = new Rectangle(300, 200); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(20); $resizer->toHeight(10); $result = $resizer->resize($size); @@ -66,13 +66,13 @@ public function testResize() $this->assertEquals(10, $result->height()); $size = new Rectangle(300, 200); - $resizer = new Resizer(width: 150); + $resizer = new RectangleResizer(width: 150); $result = $resizer->resize($size); $this->assertEquals(150, $result->width()); $this->assertEquals(200, $result->height()); $size = new Rectangle(300, 200); - $resizer = new Resizer(height: 10, width: 20); + $resizer = new RectangleResizer(height: 10, width: 20); $result = $resizer->resize($size); $this->assertEquals(20, $result->width()); $this->assertEquals(10, $result->height()); @@ -82,7 +82,7 @@ public function testResizeDown() { // 800x600 > 1000x2000 = 800x600 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->resizeDown($size); @@ -91,7 +91,7 @@ public function testResizeDown() // 800x600 > 400x1000 = 400x600 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); @@ -100,7 +100,7 @@ public function testResizeDown() // 800x600 > 1000x400 = 800x400 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(400); $result = $resizer->resizeDown($size); @@ -109,7 +109,7 @@ public function testResizeDown() // 800x600 > 400x300 = 400x300 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(300); $result = $resizer->resizeDown($size); @@ -118,7 +118,7 @@ public function testResizeDown() // 800x600 > 1000xnull = 800x600 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->width()); @@ -126,7 +126,7 @@ public function testResizeDown() // 800x600 > nullx1000 = 800x600 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); $this->assertEquals(800, $result->width()); @@ -137,7 +137,7 @@ public function testScale() { // 800x600 > 1000x2000 = 1000x750 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scale($size); @@ -146,7 +146,7 @@ public function testScale() // 800x600 > 2000x1000 = 1333x1000 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(2000); $resizer->toHeight(1000); $result = $resizer->scale($size); @@ -155,7 +155,7 @@ public function testScale() // // 800x600 > nullx3000 = 4000x3000 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(3000); $result = $resizer->scale($size); $this->assertEquals(4000, $result->width()); @@ -163,7 +163,7 @@ public function testScale() // // 800x600 > 8000xnull = 8000x6000 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(8000); $result = $resizer->scale($size); $this->assertEquals(8000, $result->width()); @@ -171,7 +171,7 @@ public function testScale() // // 800x600 > 100x400 = 100x75 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(100); $resizer->toHeight(400); $result = $resizer->scale($size); @@ -180,7 +180,7 @@ public function testScale() // // 800x600 > 400x100 = 133x100 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(100); $result = $resizer->scale($size); @@ -189,7 +189,7 @@ public function testScale() // // 800x600 > nullx300 = 400x300 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(300); $result = $resizer->scale($size); $this->assertEquals(400, $result->width()); @@ -197,7 +197,7 @@ public function testScale() // // 800x600 > 80xnull = 80x60 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(80); $result = $resizer->scale($size); $this->assertEquals(80, $result->width()); @@ -205,7 +205,7 @@ public function testScale() // // 640x480 > 225xnull = 225x169 $size = new Rectangle(640, 480); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(225); $result = $resizer->scale($size); $this->assertEquals(225, $result->width()); @@ -213,7 +213,7 @@ public function testScale() // // 640x480 > 223xnull = 223x167 $size = new Rectangle(640, 480); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(223); $result = $resizer->scale($size); $this->assertEquals(223, $result->width()); @@ -221,7 +221,7 @@ public function testScale() // // 600x800 > 300x300 = 225x300 $size = new Rectangle(600, 800); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scale($size); @@ -230,7 +230,7 @@ public function testScale() // // 800x600 > 400x10 = 13x10 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scale($size); @@ -239,7 +239,7 @@ public function testScale() // // 800x600 > 1000x1200 = 1000x750 $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(1200); $result = $resizer->scale($size); @@ -247,7 +247,7 @@ public function testScale() $this->assertEquals(750, $result->height()); $size = new Rectangle(12000, 12); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); @@ -255,7 +255,7 @@ public function testScale() $this->assertEquals(4, $result->height()); $size = new Rectangle(12, 12000); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); @@ -263,7 +263,7 @@ public function testScale() $this->assertEquals(3000, $result->height()); $size = new Rectangle(12000, 6000); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); @@ -274,7 +274,7 @@ public function testScale() public function testScaleDown() { $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scaleDown($size); @@ -282,7 +282,7 @@ public function testScaleDown() $this->assertEquals(600, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(600); $result = $resizer->scaleDown($size); @@ -290,7 +290,7 @@ public function testScaleDown() $this->assertEquals(600, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(300); $result = $resizer->scaleDown($size); @@ -298,7 +298,7 @@ public function testScaleDown() $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); @@ -306,42 +306,42 @@ public function testScaleDown() $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->width()); $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(300); $result = $resizer->scaleDown($size); $this->assertEquals(400, $result->width()); $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(1000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->width()); $this->assertEquals(600, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); $this->assertEquals(800, $result->width()); $this->assertEquals(600, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(100); $result = $resizer->scaleDown($size); $this->assertEquals(100, $result->width()); $this->assertEquals(75, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(200); $result = $resizer->scaleDown($size); @@ -349,7 +349,7 @@ public function testScaleDown() $this->assertEquals(200, $result->height()); $size = new Rectangle(600, 800); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scaleDown($size); @@ -357,7 +357,7 @@ public function testScaleDown() $this->assertEquals(300, $result->height()); $size = new Rectangle(800, 600); - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scaleDown($size); @@ -370,7 +370,7 @@ public function testScaleDown() */ public function testCover($origin, $target, $result): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->cover($origin); $this->assertEquals($result->width(), $resized->width()); @@ -397,7 +397,7 @@ public function coverDataProvider(): array */ public function testContain($origin, $target, $result): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->contain($origin); $this->assertEquals($result->width(), $resized->width()); @@ -424,7 +424,7 @@ public function containDataProvider(): array */ public function testCrop($origin, $target, $position, $result): void { - $resizer = new Resizer(); + $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->crop($origin, $position); $this->assertEquals($result->width(), $resized->width()); From 321ac466f3ebf4d4b8b3f23c4ab6f8d8cf48715c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 16:03:11 +0200 Subject: [PATCH 0366/1667] Remote unused trait --- src/Traits/CanResizeGeometrically.php | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/Traits/CanResizeGeometrically.php diff --git a/src/Traits/CanResizeGeometrically.php b/src/Traits/CanResizeGeometrically.php deleted file mode 100644 index 61db53ccf..000000000 --- a/src/Traits/CanResizeGeometrically.php +++ /dev/null @@ -1,14 +0,0 @@ - Date: Wed, 20 Jul 2022 16:45:18 +0200 Subject: [PATCH 0367/1667] Consolidate method naming --- src/Drivers/Abstract/AbstractFont.php | 4 +- .../Modifiers/AbstractFitModifier.php | 4 +- src/Drivers/Gd/Font.php | 4 +- src/Drivers/Gd/Modifiers/FillModifier.php | 4 +- src/Drivers/Gd/Modifiers/FitModifier.php | 20 +-- src/Drivers/Gd/Modifiers/ResizeModifier.php | 20 +-- src/Drivers/Imagick/Image.php | 4 +- src/Drivers/Imagick/Modifiers/FitModifier.php | 12 +- .../Imagick/Modifiers/PixelateModifier.php | 6 +- .../Imagick/Modifiers/ResizeModifier.php | 4 +- src/Geometry/Polygon.php | 32 ++-- src/Geometry/Rectangle.php | 50 +++--- src/Geometry/Tools/RectangleResizer.php | 84 ++++----- src/Interfaces/SizeInterface.php | 12 +- src/Typography/Line.php | 2 +- src/Typography/TextBlock.php | 2 +- tests/Drivers/Abstract/AbstractImageTest.php | 8 +- .../Modifiers/AbstractFitModifierTest.php | 16 +- .../Modifiers/AbstractPadModifierTest.php | 16 +- tests/Drivers/Gd/FrameTest.php | 4 +- tests/Geometry/PolygonTest.php | 12 +- tests/Geometry/RectangleResizerTest.php | 168 +++++++++--------- tests/Geometry/RectangleTest.php | 82 ++++----- tests/Typography/TextBlockTest.php | 8 +- 24 files changed, 289 insertions(+), 289 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 6f2530a3f..94ef0c4d2 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -121,11 +121,11 @@ public function leadingInPixels(): int public function capHeight(): int { - return $this->getBoxSize('T')->height(); + return $this->getBoxSize('T')->getHeight(); } public function fontSizeInPixels(): int { - return $this->getBoxSize('Hy')->height(); + return $this->getBoxSize('Hy')->getHeight(); } } diff --git a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php index 3c38bf8f6..3ba1bacb7 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractFitModifier.php @@ -22,8 +22,8 @@ protected function getCropSize(ImageInterface $image): SizeInterface $crop = new Rectangle($this->width, $this->height); $crop = $crop->contain( - $imagesize->width(), - $imagesize->height() + $imagesize->getWidth(), + $imagesize->getHeight() )->alignPivotTo($imagesize, $this->position); return $crop; diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 0371b9628..21cf3f24d 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -26,8 +26,8 @@ public function getBoxSize(string $text): Polygon $box = new Rectangle(0, 0); $chars = mb_strlen($text); if ($chars > 0) { - $box->withWidth($chars * $this->getGdFontWidth()); - $box->withHeight($this->getGdFontHeight()); + $box->setWidth($chars * $this->getGdFontWidth()); + $box->setHeight($this->getGdFontHeight()); } return $box; } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 65de556c9..0f7473428 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -45,8 +45,8 @@ protected function fillAllWithColor(Frame $frame): void $frame->getCore(), 0, 0, - $frame->getSize()->width() - 1, - $frame->getSize()->height() - 1, + $frame->getSize()->getWidth() - 1, + $frame->getSize()->getHeight() - 1, $this->color->toInt() ); } diff --git a/src/Drivers/Gd/Modifiers/FitModifier.php b/src/Drivers/Gd/Modifiers/FitModifier.php index 9da6944f7..cf55b8c0a 100644 --- a/src/Drivers/Gd/Modifiers/FitModifier.php +++ b/src/Drivers/Gd/Modifiers/FitModifier.php @@ -26,8 +26,8 @@ protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeI { // create new image $modified = imagecreatetruecolor( - $resize->width(), - $resize->height() + $resize->getWidth(), + $resize->getHeight() ); // get current image @@ -51,20 +51,20 @@ protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeI $current, 0, 0, - $crop->pivot()->getX(), - $crop->pivot()->getY(), - $resize->width(), - $resize->height(), - $crop->width(), - $crop->height() + $crop->getPivot()->getX(), + $crop->getPivot()->getY(), + $resize->getWidth(), + $resize->getHeight(), + $crop->getWidth(), + $crop->getHeight() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); - for ($y = 0; $y < $resize->height(); ++$y) { - for ($x = 0; $x < $resize->width(); ++$x) { + for ($y = 0; $y < $resize->getHeight(); ++$y) { + for ($x = 0; $x < $resize->getWidth(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 2b91e1c6c..512491f5d 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -34,8 +34,8 @@ protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): { // create new image $modified = imagecreatetruecolor( - $resizeTo->width(), - $resizeTo->height() + $resizeTo->getWidth(), + $resizeTo->getHeight() ); // get current image @@ -57,22 +57,22 @@ protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): imagecopyresampled( $modified, $current, - $resizeTo->pivot()->getX(), - $resizeTo->pivot()->getY(), + $resizeTo->getPivot()->getX(), + $resizeTo->getPivot()->getY(), 0, 0, - $resizeTo->width(), - $resizeTo->height(), - $frame->getSize()->width(), - $frame->getSize()->height() + $resizeTo->getWidth(), + $resizeTo->getHeight(), + $frame->getSize()->getWidth(), + $frame->getSize()->getHeight() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); - for ($y = 0; $y < $resizeTo->height(); ++$y) { - for ($x = 0; $x < $resizeTo->width(); ++$x) { + for ($y = 0; $y < $resizeTo->getHeight(); ++$y) { + for ($x = 0; $x < $resizeTo->getWidth(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 2bcbdec5e..906322afe 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -44,8 +44,8 @@ public function addFrame(FrameInterface $frame): ImageInterface $size = $frame->getSize(); $imagick->setImagePage( - $size->width(), - $size->height(), + $size->getWidth(), + $size->getHeight(), $frame->getOffsetLeft(), $frame->getOffsetTop() ); diff --git a/src/Drivers/Imagick/Modifiers/FitModifier.php b/src/Drivers/Imagick/Modifiers/FitModifier.php index 910d86e56..f5465c5f8 100644 --- a/src/Drivers/Imagick/Modifiers/FitModifier.php +++ b/src/Drivers/Imagick/Modifiers/FitModifier.php @@ -15,15 +15,15 @@ public function apply(ImageInterface $image): ImageInterface foreach ($image as $frame) { $frame->getCore()->extentImage( - $crop->width(), - $crop->height(), - $crop->pivot()->getX(), - $crop->pivot()->getY() + $crop->getWidth(), + $crop->getHeight(), + $crop->getPivot()->getX(), + $crop->getPivot()->getY() ); $frame->getCore()->scaleImage( - $resize->width(), - $resize->height() + $resize->getWidth(), + $resize->getHeight() ); } diff --git a/src/Drivers/Imagick/Modifiers/PixelateModifier.php b/src/Drivers/Imagick/Modifiers/PixelateModifier.php index 7552e4010..f59196f7c 100644 --- a/src/Drivers/Imagick/Modifiers/PixelateModifier.php +++ b/src/Drivers/Imagick/Modifiers/PixelateModifier.php @@ -27,10 +27,10 @@ protected function pixelateFrame(Frame $frame): void $size = $frame->getSize(); $frame->getCore()->scaleImage( - max(1, ($size->width() / $this->size)), - max(1, ($size->height() / $this->size)) + max(1, ($size->getWidth() / $this->size)), + max(1, ($size->getHeight() / $this->size)) ); - $frame->getCore()->scaleImage($size->width(), $size->height()); + $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); } } diff --git a/src/Drivers/Imagick/Modifiers/ResizeModifier.php b/src/Drivers/Imagick/Modifiers/ResizeModifier.php index 83d163c72..41f3ad683 100644 --- a/src/Drivers/Imagick/Modifiers/ResizeModifier.php +++ b/src/Drivers/Imagick/Modifiers/ResizeModifier.php @@ -19,8 +19,8 @@ public function apply(ImageInterface $image): ImageInterface foreach ($image as $frame) { $frame->getCore()->scaleImage( - $resizeTo->width(), - $resizeTo->height() + $resizeTo->getWidth(), + $resizeTo->getHeight() ); } diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index 71291f61f..732193f03 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -19,7 +19,7 @@ public function __construct( * * @return Point */ - public function getPivotPoint(): Point + public function getPivot(): Point { return $this->pivot; } @@ -30,7 +30,7 @@ public function getPivotPoint(): Point * @param Point $pivot * @return Polygon */ - public function setPivotPoint(Point $pivot): self + public function setPivot(Point $pivot): self { $this->pivot = $pivot; @@ -138,7 +138,7 @@ public function addPoint(Point $point): self * * @return int */ - public function width(): int + public function getWidth(): int { return abs($this->getMostLeftPoint()->getX() - $this->getMostRightPoint()->getX()); } @@ -148,7 +148,7 @@ public function width(): int * * @return int */ - public function height(): int + public function getHeight(): int { return abs($this->getMostBottomPoint()->getY() - $this->getMostTopPoint()->getY()); } @@ -249,8 +249,8 @@ public function getMostBottomPoint(): Point public function getCenterPoint(): Point { return new Point( - $this->getMostRightPoint()->getX() - (intval(round($this->width() / 2))), - $this->getMostTopPoint()->getY() - (intval(round($this->height() / 2))) + $this->getMostRightPoint()->getX() - (intval(round($this->getWidth() / 2))), + $this->getMostTopPoint()->getY() - (intval(round($this->getHeight() / 2))) ); } @@ -265,16 +265,16 @@ public function align(string $position): self switch (strtolower($position)) { case 'center': case 'middle': - $diff = ($this->getCenterPoint()->getX() - $this->pivot->getX()); + $diff = ($this->getCenterPoint()->getX() - $this->getPivot()->getX()); break; case 'right': - $diff = ($this->getMostRightPoint()->getX() - $this->pivot->getX()); + $diff = ($this->getMostRightPoint()->getX() - $this->getPivot()->getX()); break; default: case 'left': - $diff = ($this->getMostLeftPoint()->getX() - $this->pivot->getX()); + $diff = ($this->getMostLeftPoint()->getX() - $this->getPivot()->getX()); break; } @@ -296,16 +296,16 @@ public function valign(string $position): self switch (strtolower($position)) { case 'center': case 'middle': - $diff = ($this->getCenterPoint()->getY() - $this->pivot->getY()); + $diff = ($this->getCenterPoint()->getY() - $this->getPivot()->getY()); break; case 'top': - $diff = ($this->getMostTopPoint()->getY() - $this->pivot->getY()) - $this->height(); + $diff = ($this->getMostTopPoint()->getY() - $this->getPivot()->getY()) - $this->getHeight(); break; default: case 'bottom': - $diff = ($this->getMostBottomPoint()->getY() - $this->pivot->getY()) + $this->height(); + $diff = ($this->getMostBottomPoint()->getY() - $this->getPivot()->getY()) + $this->getHeight(); break; } @@ -329,16 +329,16 @@ public function rotate(float $angle): self foreach ($this->points as $point) { // translate point to pivot - $point->setX($point->getX() - $this->pivot->getX()); - $point->setY($point->getY() - $this->pivot->getY()); + $point->setX($point->getX() - $this->getPivot()->getX()); + $point->setY($point->getY() - $this->getPivot()->getY()); // rotate point $x = $point->getX() * $cos - $point->getY() * $sin; $y = $point->getX() * $sin + $point->getY() * $cos; // translate point back - $point->setX($x + $this->pivot->getX()); - $point->setY($y + $this->pivot->getY()); + $point->setX($x + $this->getPivot()->getX()); + $point->setY($y + $this->getPivot()->getY()); } return $this; diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 8e0ba2b7e..3e6e68fcc 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -20,7 +20,7 @@ public function __construct( $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } - public function withWidth(int $width): self + public function setWidth(int $width): self { $this[1]->setX($this[0]->getX() + $width); $this[2]->setX($this[3]->getX() + $width); @@ -28,7 +28,7 @@ public function withWidth(int $width): self return $this; } - public function withHeight(int $height): self + public function setHeight(int $height): self { $this[2]->setY($this[1]->getY() + $height); $this[3]->setY($this[0]->getY() + $height); @@ -36,12 +36,12 @@ public function withHeight(int $height): self return $this; } - public function pivot(): Point + public function getPivot(): Point { return $this->pivot; } - public function withPivot(PointInterface $pivot): self + public function setPivot(PointInterface $pivot): self { $this->pivot = $pivot; @@ -65,13 +65,13 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 case 'top-middle': case 'center-top': case 'middle-top': - $x = intval($this->width() / 2); + $x = intval($this->getWidth() / 2); $y = 0 + $offset_y; break; case 'top-right': case 'right-top': - $x = $this->width() - $offset_x; + $x = $this->getWidth() - $offset_x; $y = 0 + $offset_y; break; @@ -81,7 +81,7 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 case 'center-left': case 'middle-left': $x = 0 + $offset_x; - $y = intval($this->height() / 2); + $y = intval($this->getHeight() / 2); break; case 'right': @@ -89,14 +89,14 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 case 'right-middle': case 'center-right': case 'middle-right': - $x = $this->width() - $offset_x; - $y = intval($this->height() / 2); + $x = $this->getWidth() - $offset_x; + $y = intval($this->getHeight() / 2); break; case 'bottom-left': case 'left-bottom': $x = 0 + $offset_x; - $y = $this->height() - $offset_y; + $y = $this->getHeight() - $offset_y; break; case 'bottom': @@ -104,22 +104,22 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 case 'bottom-middle': case 'center-bottom': case 'middle-bottom': - $x = intval($this->width() / 2); - $y = $this->height() - $offset_y; + $x = intval($this->getWidth() / 2); + $y = $this->getHeight() - $offset_y; break; case 'bottom-right': case 'right-bottom': - $x = $this->width() - $offset_x; - $y = $this->height() - $offset_y; + $x = $this->getWidth() - $offset_x; + $y = $this->getHeight() - $offset_y; break; case 'center': case 'middle': case 'center-center': case 'middle-middle': - $x = intval($this->width() / 2) + $offset_x; - $y = intval($this->height() / 2) + $offset_y; + $x = intval($this->getWidth() / 2) + $offset_x; + $y = intval($this->getHeight() / 2) + $offset_y; break; default: @@ -137,10 +137,10 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 public function alignPivotTo(SizeInterface $size, string $position): self { - $reference = new self($size->width(), $size->height()); + $reference = new self($size->getWidth(), $size->getHeight()); $reference->movePivot($position); - $this->movePivot($position)->withPivot( + $this->movePivot($position)->setPivot( $reference->getRelativePositionTo($this) ); @@ -157,23 +157,23 @@ public function alignPivotTo(SizeInterface $size, string $position): self public function getRelativePositionTo(SizeInterface $rectangle): PointInterface { return new Point( - $this->pivot()->getX() - $rectangle->pivot()->getX(), - $this->pivot()->getY() - $rectangle->pivot()->getY() + $this->getPivot()->getX() - $rectangle->getPivot()->getX(), + $this->getPivot()->getY() - $rectangle->getPivot()->getY() ); } public function getAspectRatio(): float { - return $this->width() / $this->height(); + return $this->getWidth() / $this->getHeight(); } public function fitsInto(SizeInterface $size): bool { - if ($this->width() > $size->width()) { + if ($this->getWidth() > $size->getWidth()) { return false; } - if ($this->height() > $size->height()) { + if ($this->getHeight() > $size->getHeight()) { return false; } @@ -187,7 +187,7 @@ public function fitsInto(SizeInterface $size): bool */ public function isLandscape(): bool { - return $this->width() > $this->height(); + return $this->getWidth() > $this->getHeight(); } /** @@ -197,7 +197,7 @@ public function isLandscape(): bool */ public function isPortrait(): bool { - return $this->width() < $this->height(); + return $this->getWidth() < $this->getHeight(); } protected function getResizer(?int $width = null, ?int $height = null): RectangleResizer diff --git a/src/Geometry/Tools/RectangleResizer.php b/src/Geometry/Tools/RectangleResizer.php index 63dbf6a0b..f71f765b8 100644 --- a/src/Geometry/Tools/RectangleResizer.php +++ b/src/Geometry/Tools/RectangleResizer.php @@ -65,8 +65,8 @@ public function toHeight(int $height): self public function toSize(SizeInterface $size): self { - $this->width = $size->width(); - $this->height = $size->height(); + $this->width = $size->getWidth(); + $this->height = $size->getHeight(); return $this; } @@ -74,7 +74,7 @@ public function toSize(SizeInterface $size): self protected function getProportionalWidth(SizeInterface $size): int { if (! $this->hasTargetHeight()) { - return $size->width(); + return $size->getWidth(); } return (int) round($this->height * $size->getAspectRatio()); @@ -83,7 +83,7 @@ protected function getProportionalWidth(SizeInterface $size): int protected function getProportionalHeight(SizeInterface $size): int { if (! $this->hasTargetWidth()) { - return $size->height(); + return $size->getHeight(); } return (int) round($this->width / $size->getAspectRatio()); @@ -91,14 +91,14 @@ protected function getProportionalHeight(SizeInterface $size): int public function resize(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($width = $this->getTargetWidth()) { - $resized->withWidth($width); + $resized->setWidth($width); } if ($height = $this->getTargetHeight()) { - $resized->withHeight($height); + $resized->setHeight($height); } return $resized; @@ -106,17 +106,17 @@ public function resize(SizeInterface $size): SizeInterface public function resizeDown(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($width = $this->getTargetWidth()) { - $resized->withWidth( - min($width, $size->width()) + $resized->setWidth( + min($width, $size->getWidth()) ); } if ($height = $this->getTargetHeight()) { - $resized->withHeight( - min($height, $size->height()) + $resized->setHeight( + min($height, $size->getHeight()) ); } @@ -125,23 +125,23 @@ public function resizeDown(SizeInterface $size): SizeInterface public function scale(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $resized->withWidth(min( + $resized->setWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth() )); - $resized->withHeight(min( + $resized->setHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight() )); } elseif ($this->hasTargetWidth()) { - $resized->withWidth($this->getTargetWidth()); - $resized->withHeight($this->getProportionalHeight($size)); + $resized->setWidth($this->getTargetWidth()); + $resized->setHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { - $resized->withWidth($this->getProportionalWidth($size)); - $resized->withHeight($this->getTargetHeight()); + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->getTargetHeight()); } return $resized; @@ -149,36 +149,36 @@ public function scale(SizeInterface $size): SizeInterface public function scaleDown(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { - $resized->withWidth(min( + $resized->setWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth(), - $size->width() + $size->getWidth() )); - $resized->withHeight(min( + $resized->setHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight(), - $size->height() + $size->getHeight() )); } elseif ($this->hasTargetWidth()) { - $resized->withWidth(min( + $resized->setWidth(min( $this->getTargetWidth(), - $size->width() + $size->getWidth() )); - $resized->withHeight(min( + $resized->setHeight(min( $this->getProportionalHeight($size), - $size->height() + $size->getHeight() )); } elseif ($this->hasTargetHeight()) { - $resized->withWidth(min( + $resized->setWidth(min( $this->getProportionalWidth($size), - $size->width() + $size->getWidth() )); - $resized->withHeight(min( + $resized->setHeight(min( $this->getTargetHeight(), - $size->height() + $size->getHeight() )); } @@ -193,16 +193,16 @@ public function scaleDown(SizeInterface $size): SizeInterface */ public function cover(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); // auto height - $resized->withWidth($this->getTargetWidth()); - $resized->withHeight($this->getProportionalHeight($size)); + $resized->setWidth($this->getTargetWidth()); + $resized->setHeight($this->getProportionalHeight($size)); if ($resized->fitsInto($this->getTargetSize())) { // auto width - $resized->withWidth($this->getProportionalWidth($size)); - $resized->withHeight($this->getTargetHeight()); + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->getTargetHeight()); } return $resized; @@ -216,16 +216,16 @@ public function cover(SizeInterface $size): SizeInterface */ public function contain(SizeInterface $size): SizeInterface { - $resized = new Rectangle($size->width(), $size->height()); + $resized = new Rectangle($size->getWidth(), $size->getHeight()); // auto height - $resized->withWidth($this->getTargetWidth()); - $resized->withHeight($this->getProportionalHeight($size)); + $resized->setWidth($this->getTargetWidth()); + $resized->setHeight($this->getProportionalHeight($size)); if (!$resized->fitsInto($this->getTargetSize())) { // auto width - $resized->withWidth($this->getProportionalWidth($size)); - $resized->withHeight($this->getTargetHeight()); + $resized->setWidth($this->getProportionalWidth($size)); + $resized->setHeight($this->getTargetHeight()); } return $resized; diff --git a/src/Interfaces/SizeInterface.php b/src/Interfaces/SizeInterface.php index c8a29b8ea..58a6d412c 100644 --- a/src/Interfaces/SizeInterface.php +++ b/src/Interfaces/SizeInterface.php @@ -4,12 +4,12 @@ interface SizeInterface { - public function width(): int; - public function height(): int; - public function pivot(): PointInterface; - public function withWidth(int $width): SizeInterface; - public function withHeight(int $height): SizeInterface; - public function withPivot(PointInterface $pivot): SizeInterface; + public function getWidth(): int; + public function getHeight(): int; + public function getPivot(): PointInterface; + public function setWidth(int $width): SizeInterface; + public function setHeight(int $height): SizeInterface; + public function setPivot(PointInterface $pivot): SizeInterface; public function getAspectRatio(): float; public function fitsInto(SizeInterface $size): bool; public function isLandscape(): bool; diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 10e98665a..4f846674f 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -28,7 +28,7 @@ public function setPosition(Point $point): self public function widthInFont(FontInterface $font): int { - return $font->getBoxSize($this->text)->width(); + return $font->getBoxSize($this->text)->getWidth(); } public function __toString(): string diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index 4593d67f4..65e106881 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -28,7 +28,7 @@ public function getBoundingBox(FontInterface $font, Point $pivot = null): Polygo )); // set pivot - $box->setPivotPoint($pivot); + $box->setPivot($pivot); // align $box->align($font->getAlign()); diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index 21bdd8570..b6546ccbf 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -45,16 +45,16 @@ public function testGetSize(): void { $img = $this->abstractImageMock(); $this->assertInstanceOf(Rectangle::class, $img->getSize()); - $this->assertEquals(300, $img->getSize()->width()); - $this->assertEquals(200, $img->getSize()->height()); + $this->assertEquals(300, $img->getSize()->getWidth()); + $this->assertEquals(200, $img->getSize()->getHeight()); } public function testSizeAlias(): void { $img = $this->abstractImageMock(); $this->assertInstanceOf(Rectangle::class, $img->getSize()); - $this->assertEquals(300, $img->size()->width()); - $this->assertEquals(200, $img->size()->height()); + $this->assertEquals(300, $img->size()->getWidth()); + $this->assertEquals(200, $img->size()->getHeight()); } public function testModify(): void diff --git a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php index 28ebb3f6d..f8678f96e 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractFitModifierTest.php @@ -29,10 +29,10 @@ public function testGetCropSize(int $width, int $height, int $expectedWidth, int $image = (new ImageFactory())->newImage($width, $height); $size = $modifier->getCropSize($image); - static::assertSame($expectedWidth, $size->width()); - static::assertSame($expectedHeight, $size->height()); - static::assertSame($expectedX, $size->pivot()->getX()); - static::assertSame($expectedY, $size->pivot()->getY()); + static::assertSame($expectedWidth, $size->getWidth()); + static::assertSame($expectedHeight, $size->getHeight()); + static::assertSame($expectedX, $size->getPivot()->getX()); + static::assertSame($expectedY, $size->getPivot()->getY()); } public function testGetResizeSize(): void @@ -43,10 +43,10 @@ public function testGetResizeSize(): void $size = $modifier->getCropSize($image); $resize = $modifier->getResizeSize($size); - static::assertSame(200, $resize->width()); - static::assertSame(100, $resize->height()); - static::assertSame(0, $resize->pivot()->getX()); - static::assertSame(0, $resize->pivot()->getY()); + static::assertSame(200, $resize->getWidth()); + static::assertSame(100, $resize->getHeight()); + static::assertSame(0, $resize->getPivot()->getX()); + static::assertSame(0, $resize->getPivot()->getY()); } private function getModifier(int $width, int $height, string $position): AbstractFitModifier diff --git a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php index 860aa5466..4308a3bf5 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractPadModifierTest.php @@ -31,10 +31,10 @@ public function testGetCropSize(int $width, int $height, int $expectedWidth, int $image = (new ImageFactory())->newImage($width, $height); $size = $modifier->getCropSize($image); - static::assertSame($expectedWidth, $size->width()); - static::assertSame($expectedHeight, $size->height()); - static::assertSame($expectedX, $size->pivot()->getX()); - static::assertSame($expectedY, $size->pivot()->getY()); + static::assertSame($expectedWidth, $size->getWidth()); + static::assertSame($expectedHeight, $size->getHeight()); + static::assertSame($expectedX, $size->getPivot()->getX()); + static::assertSame($expectedY, $size->getPivot()->getY()); } public function testGetResizeSize(): void @@ -44,10 +44,10 @@ public function testGetResizeSize(): void $image = (new ImageFactory())->newImage(300, 200); $resize = $modifier->getResizeSize($image); - static::assertSame(200, $resize->width()); - static::assertSame(100, $resize->height()); - static::assertSame(0, $resize->pivot()->getX()); - static::assertSame(0, $resize->pivot()->getY()); + static::assertSame(200, $resize->getWidth()); + static::assertSame(100, $resize->getHeight()); + static::assertSame(0, $resize->getPivot()->getX()); + static::assertSame(0, $resize->getPivot()->getY()); } private function getModifier(int $width, int $height, $background, string $position): AbstractPadModifier diff --git a/tests/Drivers/Gd/FrameTest.php b/tests/Drivers/Gd/FrameTest.php index a27a42d3f..1f5dd2541 100644 --- a/tests/Drivers/Gd/FrameTest.php +++ b/tests/Drivers/Gd/FrameTest.php @@ -36,10 +36,10 @@ public function testSetCore(): void $core1 = imagecreatetruecolor(3, 2); $core2 = imagecreatetruecolor(3, 3); $frame = new Frame($core1); - $this->assertEquals(2, $frame->getSize()->height()); + $this->assertEquals(2, $frame->getSize()->getHeight()); $result = $frame->setCore($core2); $this->assertInstanceOf(Frame::class, $result); - $this->assertEquals(3, $frame->getSize()->height()); + $this->assertEquals(3, $frame->getSize()->getHeight()); } public function testGetSize(): void diff --git a/tests/Geometry/PolygonTest.php b/tests/Geometry/PolygonTest.php index 8428a0cf0..c1a4b938a 100644 --- a/tests/Geometry/PolygonTest.php +++ b/tests/Geometry/PolygonTest.php @@ -64,7 +64,7 @@ public function testGetCenterPoint(): void $this->assertEquals(-100, $result->getY()); } - public function testWidth(): void + public function testGetWidth(): void { $poly = new Polygon([ new Point(12, 45), @@ -72,10 +72,10 @@ public function testWidth(): void new Point(3, 566), ]); - $this->assertEquals($poly->width(), 35); + $this->assertEquals($poly->getWidth(), 35); } - public function testHeight(): void + public function testGetHeight(): void { $poly = new Polygon([ new Point(12, 45), @@ -83,7 +83,7 @@ public function testHeight(): void new Point(3, 566), ]); - $this->assertEquals(615, $poly->height()); + $this->assertEquals(615, $poly->getHeight()); $poly = new Polygon([ new Point(250, 207), @@ -92,7 +92,7 @@ public function testHeight(): void new Point(250, 250), ], new Point(250, 250)); - $this->assertEquals(43, $poly->height()); + $this->assertEquals(43, $poly->getHeight()); } public function testFirst(): void @@ -122,7 +122,7 @@ public function testLast(): void public function testGetPivotPoint(): void { $poly = new Polygon(); - $this->assertInstanceOf(Point::class, $poly->getPivotPoint()); + $this->assertInstanceOf(Point::class, $poly->getPivot()); } public function testGetMostLeftPoint(): void diff --git a/tests/Geometry/RectangleResizerTest.php b/tests/Geometry/RectangleResizerTest.php index ea505fba9..d45f02076 100644 --- a/tests/Geometry/RectangleResizerTest.php +++ b/tests/Geometry/RectangleResizerTest.php @@ -54,28 +54,28 @@ public function testResize() $resizer = new RectangleResizer(); $resizer->toWidth(150); $result = $resizer->resize($size); - $this->assertEquals(150, $result->width()); - $this->assertEquals(200, $result->height()); + $this->assertEquals(150, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); $size = new Rectangle(300, 200); $resizer = new RectangleResizer(); $resizer->toWidth(20); $resizer->toHeight(10); $result = $resizer->resize($size); - $this->assertEquals(20, $result->width()); - $this->assertEquals(10, $result->height()); + $this->assertEquals(20, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); $size = new Rectangle(300, 200); $resizer = new RectangleResizer(width: 150); $result = $resizer->resize($size); - $this->assertEquals(150, $result->width()); - $this->assertEquals(200, $result->height()); + $this->assertEquals(150, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); $size = new Rectangle(300, 200); $resizer = new RectangleResizer(height: 10, width: 20); $result = $resizer->resize($size); - $this->assertEquals(20, $result->width()); - $this->assertEquals(10, $result->height()); + $this->assertEquals(20, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); } public function testResizeDown() @@ -86,8 +86,8 @@ public function testResizeDown() $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); // 800x600 > 400x1000 = 400x600 $size = new Rectangle(800, 600); @@ -95,8 +95,8 @@ public function testResizeDown() $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); // 800x600 > 1000x400 = 800x400 $size = new Rectangle(800, 600); @@ -104,8 +104,8 @@ public function testResizeDown() $resizer->toWidth(1000); $resizer->toHeight(400); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(400, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(400, $result->getHeight()); // 800x600 > 400x300 = 400x300 $size = new Rectangle(800, 600); @@ -113,24 +113,24 @@ public function testResizeDown() $resizer->toWidth(400); $resizer->toHeight(300); $result = $resizer->resizeDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); // 800x600 > 1000xnull = 800x600 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); // 800x600 > nullx1000 = 800x600 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(1000); $result = $resizer->resizeDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); } public function testScale() @@ -141,8 +141,8 @@ public function testScale() $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scale($size); - $this->assertEquals(1000, $result->width()); - $this->assertEquals(750, $result->height()); + $this->assertEquals(1000, $result->getWidth()); + $this->assertEquals(750, $result->getHeight()); // 800x600 > 2000x1000 = 1333x1000 $size = new Rectangle(800, 600); @@ -150,24 +150,24 @@ public function testScale() $resizer->toWidth(2000); $resizer->toHeight(1000); $result = $resizer->scale($size); - $this->assertEquals(1333, $result->width()); - $this->assertEquals(1000, $result->height()); + $this->assertEquals(1333, $result->getWidth()); + $this->assertEquals(1000, $result->getHeight()); // // 800x600 > nullx3000 = 4000x3000 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->width()); - $this->assertEquals(3000, $result->height()); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(3000, $result->getHeight()); // // 800x600 > 8000xnull = 8000x6000 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(8000); $result = $resizer->scale($size); - $this->assertEquals(8000, $result->width()); - $this->assertEquals(6000, $result->height()); + $this->assertEquals(8000, $result->getWidth()); + $this->assertEquals(6000, $result->getHeight()); // // 800x600 > 100x400 = 100x75 $size = new Rectangle(800, 600); @@ -175,8 +175,8 @@ public function testScale() $resizer->toWidth(100); $resizer->toHeight(400); $result = $resizer->scale($size); - $this->assertEquals(100, $result->width()); - $this->assertEquals(75, $result->height()); + $this->assertEquals(100, $result->getWidth()); + $this->assertEquals(75, $result->getHeight()); // // 800x600 > 400x100 = 133x100 $size = new Rectangle(800, 600); @@ -184,40 +184,40 @@ public function testScale() $resizer->toWidth(400); $resizer->toHeight(100); $result = $resizer->scale($size); - $this->assertEquals(133, $result->width()); - $this->assertEquals(100, $result->height()); + $this->assertEquals(133, $result->getWidth()); + $this->assertEquals(100, $result->getHeight()); // // 800x600 > nullx300 = 400x300 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(300); $result = $resizer->scale($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); // // 800x600 > 80xnull = 80x60 $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(80); $result = $resizer->scale($size); - $this->assertEquals(80, $result->width()); - $this->assertEquals(60, $result->height()); + $this->assertEquals(80, $result->getWidth()); + $this->assertEquals(60, $result->getHeight()); // // 640x480 > 225xnull = 225x169 $size = new Rectangle(640, 480); $resizer = new RectangleResizer(); $resizer->toWidth(225); $result = $resizer->scale($size); - $this->assertEquals(225, $result->width()); - $this->assertEquals(169, $result->height()); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(169, $result->getHeight()); // // 640x480 > 223xnull = 223x167 $size = new Rectangle(640, 480); $resizer = new RectangleResizer(); $resizer->toWidth(223); $result = $resizer->scale($size); - $this->assertEquals(223, $result->width()); - $this->assertEquals(167, $result->height()); + $this->assertEquals(223, $result->getWidth()); + $this->assertEquals(167, $result->getHeight()); // // 600x800 > 300x300 = 225x300 $size = new Rectangle(600, 800); @@ -225,8 +225,8 @@ public function testScale() $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scale($size); - $this->assertEquals(225, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); // // 800x600 > 400x10 = 13x10 $size = new Rectangle(800, 600); @@ -234,8 +234,8 @@ public function testScale() $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scale($size); - $this->assertEquals(13, $result->width()); - $this->assertEquals(10, $result->height()); + $this->assertEquals(13, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); // // 800x600 > 1000x1200 = 1000x750 $size = new Rectangle(800, 600); @@ -243,32 +243,32 @@ public function testScale() $resizer->toWidth(1000); $resizer->toHeight(1200); $result = $resizer->scale($size); - $this->assertEquals(1000, $result->width()); - $this->assertEquals(750, $result->height()); + $this->assertEquals(1000, $result->getWidth()); + $this->assertEquals(750, $result->getHeight()); $size = new Rectangle(12000, 12); $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->width()); - $this->assertEquals(4, $result->height()); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(4, $result->getHeight()); $size = new Rectangle(12, 12000); $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(3, $result->width()); - $this->assertEquals(3000, $result->height()); + $this->assertEquals(3, $result->getWidth()); + $this->assertEquals(3000, $result->getHeight()); $size = new Rectangle(12000, 6000); $resizer = new RectangleResizer(); $resizer->toWidth(4000); $resizer->toHeight(3000); $result = $resizer->scale($size); - $this->assertEquals(4000, $result->width()); - $this->assertEquals(2000, $result->height()); + $this->assertEquals(4000, $result->getWidth()); + $this->assertEquals(2000, $result->getHeight()); } public function testScaleDown() @@ -278,91 +278,91 @@ public function testScaleDown() $resizer->toWidth(1000); $resizer->toHeight(2000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(600); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(1000); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(400); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(400, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(400, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toHeight(1000); $result = $resizer->scaleDown($size); - $this->assertEquals(800, $result->width()); - $this->assertEquals(600, $result->height()); + $this->assertEquals(800, $result->getWidth()); + $this->assertEquals(600, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(100); $result = $resizer->scaleDown($size); - $this->assertEquals(100, $result->width()); - $this->assertEquals(75, $result->height()); + $this->assertEquals(100, $result->getWidth()); + $this->assertEquals(75, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(200); $result = $resizer->scaleDown($size); - $this->assertEquals(267, $result->width()); - $this->assertEquals(200, $result->height()); + $this->assertEquals(267, $result->getWidth()); + $this->assertEquals(200, $result->getHeight()); $size = new Rectangle(600, 800); $resizer = new RectangleResizer(); $resizer->toWidth(300); $resizer->toHeight(300); $result = $resizer->scaleDown($size); - $this->assertEquals(225, $result->width()); - $this->assertEquals(300, $result->height()); + $this->assertEquals(225, $result->getWidth()); + $this->assertEquals(300, $result->getHeight()); $size = new Rectangle(800, 600); $resizer = new RectangleResizer(); $resizer->toWidth(400); $resizer->toHeight(10); $result = $resizer->scaleDown($size); - $this->assertEquals(13, $result->width()); - $this->assertEquals(10, $result->height()); + $this->assertEquals(13, $result->getWidth()); + $this->assertEquals(10, $result->getHeight()); } /** @@ -373,8 +373,8 @@ public function testCover($origin, $target, $result): void $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->cover($origin); - $this->assertEquals($result->width(), $resized->width()); - $this->assertEquals($result->height(), $resized->height()); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); } public function coverDataProvider(): array @@ -400,8 +400,8 @@ public function testContain($origin, $target, $result): void $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->contain($origin); - $this->assertEquals($result->width(), $resized->width()); - $this->assertEquals($result->height(), $resized->height()); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); } public function containDataProvider(): array @@ -427,10 +427,10 @@ public function testCrop($origin, $target, $position, $result): void $resizer = new RectangleResizer(); $resizer->toSize($target); $resized = $resizer->crop($origin, $position); - $this->assertEquals($result->width(), $resized->width()); - $this->assertEquals($result->height(), $resized->height()); - $this->assertEquals($result->pivot()->getX(), $resized->pivot()->getX()); - $this->assertEquals($result->pivot()->getY(), $resized->pivot()->getY()); + $this->assertEquals($result->getWidth(), $resized->getWidth()); + $this->assertEquals($result->getHeight(), $resized->getHeight()); + $this->assertEquals($result->getPivot()->getX(), $resized->getPivot()->getX()); + $this->assertEquals($result->getPivot()->getY(), $resized->getPivot()->getY()); } public function cropDataProvider(): array diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index ddc9e1020..87befb703 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -19,24 +19,24 @@ public function testConstructor(): void $this->assertEquals(-200, $rectangle[2]->getY()); $this->assertEquals(0, $rectangle[3]->getX()); $this->assertEquals(-200, $rectangle[3]->getY()); - $this->assertEquals(300, $rectangle->width()); - $this->assertEquals(200, $rectangle->height()); + $this->assertEquals(300, $rectangle->getWidth()); + $this->assertEquals(200, $rectangle->getHeight()); } public function testWithWidth(): void { $rectangle = new Rectangle(300, 200); - $this->assertEquals(300, $rectangle->width()); - $rectangle->withWidth(400); - $this->assertEquals(400, $rectangle->width()); + $this->assertEquals(300, $rectangle->getWidth()); + $rectangle->setWidth(400); + $this->assertEquals(400, $rectangle->getWidth()); } public function testWithHeight(): void { $rectangle = new Rectangle(300, 200); - $this->assertEquals(200, $rectangle->height()); - $rectangle->withHeight(800); - $this->assertEquals(800, $rectangle->height()); + $this->assertEquals(200, $rectangle->getHeight()); + $rectangle->setHeight(800); + $this->assertEquals(800, $rectangle->getHeight()); } public function testGetAspectRatio() @@ -109,55 +109,55 @@ public function testIsPortrait() public function testSetGetPivot(): void { $box = new Rectangle(800, 600); - $pivot = $box->pivot(); + $pivot = $box->getPivot(); $this->assertInstanceOf(Point::class, $pivot); $this->assertEquals(0, $pivot->getX()); - $result = $box->withPivot(new Point(10, 0)); + $result = $box->setPivot(new Point(10, 0)); $this->assertInstanceOf(Rectangle::class, $result); - $this->assertEquals(10, $box->pivot()->getX()); + $this->assertEquals(10, $box->getPivot()->getX()); } public function testAlignPivot(): void { $box = new Rectangle(640, 480); - $this->assertEquals(0, $box->pivot()->getX()); - $this->assertEquals(0, $box->pivot()->getY()); + $this->assertEquals(0, $box->getPivot()->getX()); + $this->assertEquals(0, $box->getPivot()->getY()); $box->movePivot('top-left', 3, 3); - $this->assertEquals(3, $box->pivot()->getX()); - $this->assertEquals(3, $box->pivot()->getY()); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('top', 3, 3); - $this->assertEquals(320, $box->pivot()->getX()); - $this->assertEquals(3, $box->pivot()->getY()); + $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('top-right', 3, 3); - $this->assertEquals(637, $box->pivot()->getX()); - $this->assertEquals(3, $box->pivot()->getY()); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('left', 3, 3); - $this->assertEquals(3, $box->pivot()->getX()); - $this->assertEquals(240, $box->pivot()->getY()); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(240, $box->getPivot()->getY()); $box->movePivot('center', 3, 3); - $this->assertEquals(323, $box->pivot()->getX()); - $this->assertEquals(243, $box->pivot()->getY()); + $this->assertEquals(323, $box->getPivot()->getX()); + $this->assertEquals(243, $box->getPivot()->getY()); $box->movePivot('right', 3, 3); - $this->assertEquals(637, $box->pivot()->getX()); - $this->assertEquals(240, $box->pivot()->getY()); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(240, $box->getPivot()->getY()); $box->movePivot('bottom-left', 3, 3); - $this->assertEquals(3, $box->pivot()->getX()); - $this->assertEquals(477, $box->pivot()->getY()); + $this->assertEquals(3, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); $box->movePivot('bottom', 3, 3); - $this->assertEquals(320, $box->pivot()->getX()); - $this->assertEquals(477, $box->pivot()->getY()); + $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); $result = $box->movePivot('bottom-right', 3, 3); - $this->assertEquals(637, $box->pivot()->getX()); - $this->assertEquals(477, $box->pivot()->getY()); + $this->assertEquals(637, $box->getPivot()->getX()); + $this->assertEquals(477, $box->getPivot()->getY()); $this->assertInstanceOf(Rectangle::class, $result); } @@ -167,32 +167,32 @@ public function testAlignPivotTo(): void $container = new Rectangle(800, 600); $size = new Rectangle(200, 100); $size->alignPivotTo($container, 'center'); - $this->assertEquals(300, $size->pivot()->getX()); - $this->assertEquals(250, $size->pivot()->getY()); + $this->assertEquals(300, $size->getPivot()->getX()); + $this->assertEquals(250, $size->getPivot()->getY()); $container = new Rectangle(800, 600); $size = new Rectangle(100, 100); $size->alignPivotTo($container, 'center'); - $this->assertEquals(350, $size->pivot()->getX()); - $this->assertEquals(250, $size->pivot()->getY()); + $this->assertEquals(350, $size->getPivot()->getX()); + $this->assertEquals(250, $size->getPivot()->getY()); $container = new Rectangle(800, 600); $size = new Rectangle(800, 600); $size->alignPivotTo($container, 'center'); - $this->assertEquals(0, $size->pivot()->getX()); - $this->assertEquals(0, $size->pivot()->getY()); + $this->assertEquals(0, $size->getPivot()->getX()); + $this->assertEquals(0, $size->getPivot()->getY()); $container = new Rectangle(100, 100); $size = new Rectangle(800, 600); $size->alignPivotTo($container, 'center'); - $this->assertEquals(-350, $size->pivot()->getX()); - $this->assertEquals(-250, $size->pivot()->getY()); + $this->assertEquals(-350, $size->getPivot()->getX()); + $this->assertEquals(-250, $size->getPivot()->getY()); $container = new Rectangle(100, 100); $size = new Rectangle(800, 600); $size->alignPivotTo($container, 'bottom-right'); - $this->assertEquals(-700, $size->pivot()->getX()); - $this->assertEquals(-500, $size->pivot()->getY()); + $this->assertEquals(-700, $size->getPivot()->getX()); + $this->assertEquals(-500, $size->getPivot()->getY()); } public function testgetRelativePositionTo(): void diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index 6f95d2121..8b3f6e3ab 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -71,9 +71,9 @@ public function testGetBoundingBox(): void $font->shouldReceive('capHeight')->andReturn(22); $box = $block->getBoundingBox($font, new Point(10, 15)); - $this->assertEquals(300, $box->width()); - $this->assertEquals(82, $box->height()); - $this->assertEquals(10, $box->getPivotPoint()->getX()); - $this->assertEquals(15, $box->getPivotPoint()->getY()); + $this->assertEquals(300, $box->getWidth()); + $this->assertEquals(82, $box->getHeight()); + $this->assertEquals(10, $box->getPivot()->getX()); + $this->assertEquals(15, $box->getPivot()->getY()); } } From 736d7546588aaef24f272af4d9092cfc77b9a161 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 17:14:16 +0200 Subject: [PATCH 0368/1667] Fix classname --- tests/Geometry/RectangleResizerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Geometry/RectangleResizerTest.php b/tests/Geometry/RectangleResizerTest.php index d45f02076..750d43ac6 100644 --- a/tests/Geometry/RectangleResizerTest.php +++ b/tests/Geometry/RectangleResizerTest.php @@ -10,7 +10,7 @@ /** * @covers \Intervention\Image\Geometry\Tools\RectangleResizer */ -class RectangleRectangleResizerTest extends TestCase +class RectangleResizerTest extends TestCase { public function testMake(): void { From 50445d78e32249293db86e92808f653504e54864 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 17:17:08 +0200 Subject: [PATCH 0369/1667] Removed constructor for AbstractFont --- src/Drivers/Abstract/AbstractFont.php | 7 ------- src/Drivers/Abstract/AbstractImage.php | 5 ++++- src/Traits/CanRunCallback.php | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 src/Traits/CanRunCallback.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 94ef0c4d2..4649cc9cd 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -18,13 +18,6 @@ abstract class AbstractFont implements FontInterface protected $valign = 'bottom'; protected $lineHeight = 1.25; - public function __construct(callable $init = null) - { - if (is_callable($init)) { - $init($this); - } - } - public function size(float $size): FontInterface { $this->size = $size; diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 4521b84d2..1c32b466a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -13,11 +13,13 @@ use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Traits\CanRunCallback; abstract class AbstractImage implements ImageInterface { use CanResolveDriverClass; use CanHandleInput; + use CanRunCallback; public function getSize(): SizeInterface { @@ -193,7 +195,8 @@ public function pickColors(int $x, int $y): CollectionInterface public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface { - $font = $this->resolveDriverClass('Font', $init); + $font = $this->runCallback($init, $this->resolveDriverClass('Font')); + $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font, $text); return $this->modify($modifier); diff --git a/src/Traits/CanRunCallback.php b/src/Traits/CanRunCallback.php new file mode 100644 index 000000000..b2da4b0da --- /dev/null +++ b/src/Traits/CanRunCallback.php @@ -0,0 +1,15 @@ + Date: Wed, 20 Jul 2022 17:24:41 +0200 Subject: [PATCH 0370/1667] Add method Rectangle::setSize() --- src/Geometry/Rectangle.php | 5 +++++ tests/Geometry/RectangleTest.php | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 3e6e68fcc..1a9cc547d 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -20,6 +20,11 @@ public function __construct( $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } + public function setSize(int $width, int $height): self + { + return $this->setWidth($width)->setHeight($height); + } + public function setWidth(int $width): self { $this[1]->setX($this[0]->getX() + $width); diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index 87befb703..858238e21 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -23,6 +23,14 @@ public function testConstructor(): void $this->assertEquals(200, $rectangle->getHeight()); } + public function testSetSize(): void + { + $rectangle = new Rectangle(300, 200); + $rectangle->setSize(12, 34); + $this->assertEquals(12, $rectangle->getWidth()); + $this->assertEquals(34, $rectangle->getHeight()); + } + public function testWithWidth(): void { $rectangle = new Rectangle(300, 200); From 67307ea8635d149b69a0427750523e7f917e78ea Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 20 Jul 2022 17:25:24 +0200 Subject: [PATCH 0371/1667] Rename test methods --- tests/Geometry/RectangleTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index 858238e21..a74a8a5e1 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -31,7 +31,7 @@ public function testSetSize(): void $this->assertEquals(34, $rectangle->getHeight()); } - public function testWithWidth(): void + public function testSetWidth(): void { $rectangle = new Rectangle(300, 200); $this->assertEquals(300, $rectangle->getWidth()); @@ -39,7 +39,7 @@ public function testWithWidth(): void $this->assertEquals(400, $rectangle->getWidth()); } - public function testWithHeight(): void + public function testSetHeight(): void { $rectangle = new Rectangle(300, 200); $this->assertEquals(200, $rectangle->getHeight()); From efe7d5bb67c1b7ec959d754dc2729f8dbbeb6877 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 23 Jul 2022 10:56:43 +0200 Subject: [PATCH 0372/1667] Add DrawRectangleModifier --- src/Drivers/Abstract/AbstractImage.php | 17 +++++ .../Modifiers/AbstractDrawModifier.php | 19 +++++ .../Gd/Modifiers/DrawRectangleModifier.php | 71 +++++++++++++++++ .../Modifiers/DrawRectangleModifier.php | 76 +++++++++++++++++++ src/Geometry/Rectangle.php | 33 +++++++- src/Geometry/Traits/HasBackgroundColor.php | 30 ++++++++ src/Geometry/Traits/HasBorder.php | 43 +++++++++++ src/Interfaces/DrawableInterface.php | 17 +++++ 8 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php create mode 100644 src/Drivers/Gd/Modifiers/DrawRectangleModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php create mode 100644 src/Geometry/Traits/HasBackgroundColor.php create mode 100644 src/Geometry/Traits/HasBorder.php create mode 100644 src/Interfaces/DrawableInterface.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1c32b466a..6626cad3d 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -21,6 +21,15 @@ abstract class AbstractImage implements ImageInterface use CanHandleInput; use CanRunCallback; + public function eachFrame(callable $callback): self + { + foreach ($this as $frame) { + $callback($frame); + } + + return $this; + } + public function getSize(): SizeInterface { return new Rectangle($this->getWidth(), $this->getHeight()); @@ -210,6 +219,14 @@ public function drawPixel(int $x, int $y, $color = null): ImageInterface return $this->modify($modifier); } + public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface + { + $rectangle = $this->runCallback($init, new Rectangle(0, 0)); + $modifier = $this->resolveDriverClass('Modifiers\DrawRectangleModifier', new Point($x, $y), $rectangle); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php new file mode 100644 index 000000000..1dfa0f131 --- /dev/null +++ b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php @@ -0,0 +1,19 @@ +eachFrame(function ($frame) { + // draw background + if ($this->rectangle()->hasBackgroundColor()) { + imagefilledrectangle( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), + $this->getBackgroundColor() + ); + } + + if ($this->rectangle()->hasBorder()) { + // draw border + imagesetthickness($frame->getCore(), $this->rectangle()->getBorderSize()); + imagerectangle( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), + $this->getBorderColor() + ); + } + }); + + return $image; + } + + private function rectangle(): Rectangle + { + return $this->drawable; + } + + private function getBackgroundColor(): int + { + try { + $color = $this->handleInput($this->rectangle()->getBackgroundColor()); + } catch (DecoderException $e) { + return 2130706432; // transparent + } + + return $color->toInt(); + } + + private function getBorderColor(): int + { + try { + $color = $this->handleInput($this->rectangle()->getBorderColor()); + } catch (DecoderException $e) { + return 2130706432; // transparent + } + + return $color->toInt(); + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php new file mode 100644 index 000000000..bb6f9be00 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -0,0 +1,76 @@ +setFillColor($this->getBackgroundColor()); + if ($this->rectangle()->hasBorder()) { + $drawing->setStrokeColor($this->getBorderColor()); + $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); + } + + // build rectangle + $drawing->rectangle( + $this->position->getX(), + $this->position->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY() + ); + + $image->eachFrame(function ($frame) use ($drawing) { + $frame->getCore()->drawImage($drawing); + }); + + return $image; + } + + private function rectangle(): Rectangle + { + return $this->drawable; + } + + private function getBackgroundColor(): ImagickPixel + { + try { + $color = $this->handleInput($this->rectangle()->getBackgroundColor()); + } catch (DecoderException $e) { + $color = null; + } + + return $this->filterImagickPixel($color); + } + + private function getBorderColor(): ImagickPixel + { + try { + $color = $this->handleInput($this->rectangle()->getBorderColor()); + } catch (DecoderException $e) { + $color = null; + } + + return $this->filterImagickPixel($color); + } + + private function filterImagickPixel($color): ImagickPixel + { + if (!is_a($color, Color::class)) { + return new ImagickPixel('transparent'); + } + + return $color->getPixel(); + } +} diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index 1a9cc547d..c7e5b1bbc 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -3,11 +3,17 @@ namespace Intervention\Image\Geometry; use Intervention\Image\Geometry\Tools\RectangleResizer; +use Intervention\Image\Geometry\Traits\HasBackgroundColor; +use Intervention\Image\Geometry\Traits\HasBorder; +use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Interfaces\SizeInterface; -class Rectangle extends Polygon implements SizeInterface +class Rectangle extends Polygon implements SizeInterface, DrawableInterface { + use HasBorder; + use HasBackgroundColor; + public function __construct( int $width, int $height, @@ -20,11 +26,26 @@ public function __construct( $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } + /** + * Set the rectangle dimensions to given width & height + * + * @param int $width + * @param int $height + * @return Rectangle + */ public function setSize(int $width, int $height): self { return $this->setWidth($width)->setHeight($height); } + /** + * Alias of self::setSize() + */ + public function size(int $width, int $height): self + { + return $this->setSize($width, $height); + } + public function setWidth(int $width): self { $this[1]->setX($this[0]->getX() + $width); @@ -205,6 +226,16 @@ public function isPortrait(): bool return $this->getWidth() < $this->getHeight(); } + public function topLeftPoint(): PointInterface + { + return $this->points[0]; + } + + public function bottomRightPoint(): PointInterface + { + return $this->points[2]; + } + protected function getResizer(?int $width = null, ?int $height = null): RectangleResizer { return new RectangleResizer($width, $height); diff --git a/src/Geometry/Traits/HasBackgroundColor.php b/src/Geometry/Traits/HasBackgroundColor.php new file mode 100644 index 000000000..5987a5168 --- /dev/null +++ b/src/Geometry/Traits/HasBackgroundColor.php @@ -0,0 +1,30 @@ +setBackgroundColor($color); + } + + public function setBackgroundColor($color): self + { + $this->backgroundColor = $color; + + return $this; + } + + public function getBackgroundColor() + { + return $this->backgroundColor; + } + + public function hasBackgroundColor(): bool + { + return !is_null($this->backgroundColor); + } +} diff --git a/src/Geometry/Traits/HasBorder.php b/src/Geometry/Traits/HasBorder.php new file mode 100644 index 000000000..f9df99008 --- /dev/null +++ b/src/Geometry/Traits/HasBorder.php @@ -0,0 +1,43 @@ +setBorderSize($size)->setBorderColor($color); + } + + public function setBorderSize(int $size): self + { + $this->borderSize = $size; + + return $this; + } + + public function getBorderSize(): int + { + return $this->borderSize; + } + + public function setBorderColor($color): self + { + $this->borderColor = $color; + + return $this; + } + + public function getBorderColor() + { + return $this->borderColor; + } + + public function hasBorder(): bool + { + return $this->borderSize > 0 && !is_null($this->borderColor); + } +} diff --git a/src/Interfaces/DrawableInterface.php b/src/Interfaces/DrawableInterface.php new file mode 100644 index 000000000..065d28ded --- /dev/null +++ b/src/Interfaces/DrawableInterface.php @@ -0,0 +1,17 @@ + Date: Sat, 23 Jul 2022 15:58:56 +0200 Subject: [PATCH 0373/1667] Add drawing methods - Image::drawCircle() - Image::drawEllipse() --- src/Drivers/Abstract/AbstractImage.php | 18 ++++++ .../Modifiers/AbstractDrawModifier.php | 29 ++++++++++ .../Gd/Modifiers/DrawEllipseModifier.php | 53 +++++++++++++++++ .../Gd/Modifiers/DrawRectangleModifier.php | 47 +++------------ .../Imagick/Modifiers/DrawEllipseModifier.php | 58 +++++++++++++++++++ .../Modifiers/DrawRectangleModifier.php | 53 ++++++----------- src/Geometry/Circle.php | 48 +++++++++++++++ src/Geometry/Ellipse.php | 55 ++++++++++++++++++ 8 files changed, 288 insertions(+), 73 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/DrawEllipseModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php create mode 100644 src/Geometry/Circle.php create mode 100644 src/Geometry/Ellipse.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 6626cad3d..01b702abc 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -4,6 +4,8 @@ use Intervention\Image\Collection; use Intervention\Image\EncodedImage; +use Intervention\Image\Geometry\Circle; +use Intervention\Image\Geometry\Ellipse; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; @@ -227,6 +229,22 @@ public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInte return $this->modify($modifier); } + public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface + { + $ellipse = $this->runCallback($init, new Ellipse(0, 0)); + $modifier = $this->resolveDriverClass('Modifiers\DrawEllipseModifier', new Point($x, $y), $ellipse); + + return $this->modify($modifier); + } + + public function drawCircle(int $x, int $y, ?callable $init = null): ImageInterface + { + $circle = $this->runCallback($init, new Circle(0)); + $modifier = $this->resolveDriverClass('Modifiers\DrawEllipseModifier', new Point($x, $y), $circle); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php index 1dfa0f131..04fe1f46f 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; +use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Traits\CanHandleInput; @@ -16,4 +18,31 @@ public function __construct( ) { // } + + public function drawable(): DrawableInterface + { + return $this->drawable; + } + + protected function getBackgroundColor(): ?ColorInterface + { + try { + $color = $this->handleInput($this->drawable->getBackgroundColor()); + } catch (DecoderException $e) { + return $this->handleInput('transparent'); + } + + return $color; + } + + protected function getBorderColor(): ?ColorInterface + { + try { + $color = $this->handleInput($this->drawable->getBorderColor()); + } catch (DecoderException $e) { + return $this->handleInput('transparent'); + } + + return $color; + } } diff --git a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php new file mode 100644 index 000000000..d7adef56a --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php @@ -0,0 +1,53 @@ +eachFrame(function ($frame) { + if ($this->drawable()->hasBorder()) { + // slightly smaller ellipse to keep 1px bordered edges clean + if ($this->drawable()->hasBackgroundColor()) { + imagefilledellipse( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->drawable()->getWidth() - 1, + $this->drawable()->getHeight() - 1, + $this->getBackgroundColor()->toInt() + ); + } + + imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize()); + + // gd's imageellipse doesn't respect imagesetthickness so i use + // imagearc with 359.9 degrees here. + imagearc( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->drawable()->getWidth(), + $this->drawable()->getHeight(), + 0, + 359.99, + $this->getBorderColor()->toInt() + ); + } else { + imagefilledellipse( + $frame->getCore(), + $this->position->getX(), + $this->position->getY(), + $this->drawable()->getWidth(), + $this->drawable()->getHeight(), + $this->getBackgroundColor()->toInt() + ); + } + }); + } +} diff --git a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php index 3f949aca4..a12c252f7 100644 --- a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php @@ -3,8 +3,6 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -14,58 +12,31 @@ public function apply(ImageInterface $image): ImageInterface { $image->eachFrame(function ($frame) { // draw background - if ($this->rectangle()->hasBackgroundColor()) { + if ($this->drawable()->hasBackgroundColor()) { imagefilledrectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(), - $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), - $this->getBackgroundColor() + $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), + $this->position->getY() + $this->drawable()->bottomRightPoint()->getY(), + $this->getBackgroundColor()->toInt() ); } - if ($this->rectangle()->hasBorder()) { + if ($this->drawable()->hasBorder()) { // draw border - imagesetthickness($frame->getCore(), $this->rectangle()->getBorderSize()); + imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize()); imagerectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(), - $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), - $this->getBorderColor() + $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), + $this->position->getY() + $this->drawable()->bottomRightPoint()->getY(), + $this->getBorderColor()->toInt() ); } }); return $image; } - - private function rectangle(): Rectangle - { - return $this->drawable; - } - - private function getBackgroundColor(): int - { - try { - $color = $this->handleInput($this->rectangle()->getBackgroundColor()); - } catch (DecoderException $e) { - return 2130706432; // transparent - } - - return $color->toInt(); - } - - private function getBorderColor(): int - { - try { - $color = $this->handleInput($this->rectangle()->getBorderColor()); - } catch (DecoderException $e) { - return 2130706432; // transparent - } - - return $color->toInt(); - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php new file mode 100644 index 000000000..075842ea9 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php @@ -0,0 +1,58 @@ +eachFrame(function ($frame) { + $drawing = new ImagickDraw(); + $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + + if ($this->drawable()->hasBorder()) { + $drawing->setStrokeWidth($this->drawable()->getBorderSize()); + $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + } + + $drawing->ellipse( + $this->position->getX(), + $this->position->getY(), + $this->drawable()->getWidth() / 2, + $this->drawable()->getHeight() / 2, + 0, + 360 + ); + + $frame->getCore()->drawImage($drawing); + }); + } + + protected function getBackgroundColor(): ColorInterface + { + $color = parent::getBackgroundColor(); + if (!is_a($color, Color::class)) { + throw new DecoderException('Unable to decode background color.'); + } + + return $color; + } + + protected function getBorderColor(): ColorInterface + { + $color = parent::getBorderColor(); + if (!is_a($color, Color::class)) { + throw new DecoderException('Unable to decode border color.'); + } + + return $color; + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index bb6f9be00..f16bdb5ac 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -3,13 +3,12 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; -use ImagickPixel; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Geometry\Rectangle; +use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\ColorInterface; class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface { @@ -17,18 +16,18 @@ public function apply(ImageInterface $image): ImageInterface { // setup rectangle $drawing = new ImagickDraw(); - $drawing->setFillColor($this->getBackgroundColor()); - if ($this->rectangle()->hasBorder()) { - $drawing->setStrokeColor($this->getBorderColor()); - $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); + $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + if ($this->drawable()->hasBorder()) { + $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeWidth($this->drawable()->getBorderSize()); } // build rectangle $drawing->rectangle( $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), - $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY() + $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), + $this->position->getY() + $this->drawable()->bottomRightPoint()->getY() ); $image->eachFrame(function ($frame) use ($drawing) { @@ -38,39 +37,23 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - private function rectangle(): Rectangle + protected function getBackgroundColor(): ColorInterface { - return $this->drawable; - } - - private function getBackgroundColor(): ImagickPixel - { - try { - $color = $this->handleInput($this->rectangle()->getBackgroundColor()); - } catch (DecoderException $e) { - $color = null; + $color = parent::getBackgroundColor(); + if (!is_a($color, Color::class)) { + throw new DecoderException('Unable to decode background color.'); } - return $this->filterImagickPixel($color); + return $color; } - private function getBorderColor(): ImagickPixel - { - try { - $color = $this->handleInput($this->rectangle()->getBorderColor()); - } catch (DecoderException $e) { - $color = null; - } - - return $this->filterImagickPixel($color); - } - - private function filterImagickPixel($color): ImagickPixel + protected function getBorderColor(): ColorInterface { + $color = parent::getBorderColor(); if (!is_a($color, Color::class)) { - return new ImagickPixel('transparent'); + throw new DecoderException('Unable to decode border color.'); } - return $color->getPixel(); + return $color; } } diff --git a/src/Geometry/Circle.php b/src/Geometry/Circle.php new file mode 100644 index 000000000..841d6a96c --- /dev/null +++ b/src/Geometry/Circle.php @@ -0,0 +1,48 @@ +setWidth($diameter); + $this->setHeight($diameter); + $this->pivot = $pivot ? $pivot : new Point(); + } + + public function diameter(int $diameter): self + { + return $this->setDiameter($diameter); + } + + public function setDiameter(int $diameter): self + { + $this->setWidth($diameter); + $this->setHeight($diameter); + + return $this; + } + + public function getDiameter(): int + { + return $this->diameter; + } + + public function radius(int $radius): self + { + return $this->setRadius($radius); + } + + public function setRadius(int $radius): self + { + return $this->diameter(intval($radius * 2)); + } + + public function getRadius(): int + { + return intval($this->diameter / 2); + } +} diff --git a/src/Geometry/Ellipse.php b/src/Geometry/Ellipse.php new file mode 100644 index 000000000..fb37dc7ef --- /dev/null +++ b/src/Geometry/Ellipse.php @@ -0,0 +1,55 @@ +pivot = $pivot ? $pivot : new Point(); + } + + public function size(int $width, int $height): self + { + return $this->setSize($width, $height); + } + + public function setSize(int $width, int $height): self + { + return $this->setWidth($width)->setHeight($height); + } + + public function setWidth(int $width): self + { + $this->width = $width; + + return $this; + } + + public function setHeight(int $height): self + { + $this->height = $height; + + return $this; + } + + public function getWidth(): int + { + return $this->width; + } + + public function getHeight(): int + { + return $this->height; + } +} From 39613b731a8f90ec688c361fe7c6301ef75de2ad Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 23 Jul 2022 16:47:26 +0200 Subject: [PATCH 0374/1667] Add DrawLineModifiers --- src/Drivers/Abstract/AbstractImage.php | 9 ++ src/Drivers/Gd/Modifiers/DrawLineModifier.php | 39 +++++++++ .../Imagick/Modifiers/DrawLineModifier.php | 42 +++++++++ src/Geometry/Line.php | 85 +++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/DrawLineModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawLineModifier.php create mode 100644 src/Geometry/Line.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 01b702abc..097ac5bbd 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -6,6 +6,7 @@ use Intervention\Image\EncodedImage; use Intervention\Image\Geometry\Circle; use Intervention\Image\Geometry\Ellipse; +use Intervention\Image\Geometry\Line; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; @@ -245,6 +246,14 @@ public function drawCircle(int $x, int $y, ?callable $init = null): ImageInterfa return $this->modify($modifier); } + public function drawLine(callable $init = null): ImageInterface + { + $line = $this->runCallback($init, new Line(new Point(), new Point())); + $modifier = $this->resolveDriverClass('Modifiers\DrawLineModifier', $line->getStart(), $line); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/DrawLineModifier.php b/src/Drivers/Gd/Modifiers/DrawLineModifier.php new file mode 100644 index 000000000..adcc455b7 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DrawLineModifier.php @@ -0,0 +1,39 @@ +eachFrame(function ($frame) { + imageline( + $frame->getCore(), + $this->drawable()->getStart()->getX(), + $this->drawable()->getStart()->getY(), + $this->drawable()->getEnd()->getX(), + $this->drawable()->getEnd()->getY(), + $this->getBackgroundColor()->toInt() + ); + }); + } + + public function drawable(): DrawableInterface + { + $drawable = parent::drawable(); + if (!is_a($drawable, Line::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Line::class, found ' . get_class($drawable) + ); + } + + return $drawable; + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php new file mode 100644 index 000000000..6c224a55d --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -0,0 +1,42 @@ +setStrokeColor($this->getBackgroundColor()->getPixel()); + $drawing->setStrokeWidth($this->drawable()->getWidth()); + $drawing->line( + $this->drawable()->getStart()->getX(), + $this->drawable()->getStart()->getY(), + $this->drawable()->getEnd()->getX(), + $this->drawable()->getEnd()->getY(), + ); + return $image->eachFrame(function ($frame) use ($drawing) { + $frame->getCore()->drawImage($drawing); + }); + } + + public function drawable(): DrawableInterface + { + $drawable = parent::drawable(); + if (!is_a($drawable, Line::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Line::class, found ' . get_class($drawable) + ); + } + + return $drawable; + } +} diff --git a/src/Geometry/Line.php b/src/Geometry/Line.php new file mode 100644 index 000000000..7ef5a8278 --- /dev/null +++ b/src/Geometry/Line.php @@ -0,0 +1,85 @@ +width; + } + + public function setWidth(int $width): self + { + $this->width = $width; + + return $this; + } + + public function width(int $width): self + { + return $this->setWidth($width); + } + + public function color($color): self + { + $this->setBackgroundColor($color); + + return $this; + } + + public function getStart(): Point + { + return $this->start; + } + + public function getEnd(): Point + { + return $this->end; + } + + public function setStart(Point $start): self + { + $this->start = $start; + + return $this; + } + + public function from(int $x, int $y): self + { + $this->getStart()->setX($x); + $this->getStart()->setY($y); + + return $this; + } + + public function to(int $x, int $y): self + { + $this->getEnd()->setX($x); + $this->getEnd()->setY($y); + + return $this; + } + + public function setEnd(Point $end): self + { + $this->end = $end; + + return $this; + } +} From 7a103cbfa8b36f73c82028e2030cb08e2bc2c6ab Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 25 Jul 2022 11:13:38 +0200 Subject: [PATCH 0375/1667] Implement IteratorAggregate in Polygon::class --- src/Geometry/Polygon.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index 732193f03..50cf3cb0d 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -3,10 +3,19 @@ namespace Intervention\Image\Geometry; use ArrayAccess; +use ArrayIterator; use Countable; +use Traversable; +use IteratorAggregate; +use Intervention\Image\Geometry\Traits\HasBackgroundColor; +use Intervention\Image\Geometry\Traits\HasBorder; +use Intervention\Image\Interfaces\DrawableInterface; -class Polygon implements Countable, ArrayAccess +class Polygon implements IteratorAggregate, Countable, ArrayAccess, DrawableInterface { + use HasBorder; + use HasBackgroundColor; + public function __construct( protected array $points = [], protected ?Point $pivot = null @@ -14,6 +23,11 @@ public function __construct( $this->pivot = $pivot ? $pivot : new Point(); } + public function getIterator(): Traversable + { + return new ArrayIterator($this->points); + } + /** * Return current pivot point * @@ -133,6 +147,13 @@ public function addPoint(Point $point): self return $this; } + public function point(int $x, int $y): self + { + $this->addPoint(new Point($x, $y)); + + return $this; + } + /** * Calculate total horizontal span of polygon * From 105e11d1d52524ca3289a8e199234ca33a501626 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 25 Jul 2022 11:14:10 +0200 Subject: [PATCH 0376/1667] Add Image::drawPolygon() --- src/Drivers/Abstract/AbstractImage.php | 9 ++++ .../Modifiers/AbstractDrawModifier.php | 49 +++++++++++++++++++ .../Gd/Modifiers/DrawPolygonModifier.php | 41 ++++++++++++++++ .../Imagick/Modifiers/DrawPolygonModifier.php | 47 ++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/DrawPolygonModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 097ac5bbd..9120d5c1a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -8,6 +8,7 @@ use Intervention\Image\Geometry\Ellipse; use Intervention\Image\Geometry\Line; use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\EncoderInterface; @@ -254,6 +255,14 @@ public function drawLine(callable $init = null): ImageInterface return $this->modify($modifier); } + public function drawPolygon(callable $init = null): ImageInterface + { + $polygon = $this->runCallback($init, new Polygon()); + $modifier = $this->resolveDriverClass('Modifiers\DrawPolygonModifier', $polygon); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php index 04fe1f46f..42e89effa 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php @@ -3,6 +3,11 @@ namespace Intervention\Image\Drivers\Abstract\Modifiers; use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Exceptions\GeometryException; +use Intervention\Image\Geometry\Ellipse; +use Intervention\Image\Geometry\Line; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; @@ -45,4 +50,48 @@ protected function getBorderColor(): ?ColorInterface return $color; } + + public function polygon(): Polygon + { + if (!is_a($this->drawable(), Polygon::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Polygon::class, found ' . get_class($this->drawable()) + ); + } + + return $this->drawable(); + } + + public function ellipse(): Ellipse + { + if (!is_a($this->drawable(), Ellipse::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Ellipse::class, found ' . get_class($this->drawable()) + ); + } + + return $this->drawable(); + } + + public function line(): Line + { + if (!is_a($this->drawable(), Line::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Line::class, found ' . get_class($this->drawable()) + ); + } + + return $this->drawable(); + } + + public function rectangle(): Rectangle + { + if (!is_a($this->drawable(), Rectangle::class)) { + throw new GeometryException( + 'Shape mismatch. Excepted Rectangle::class, found ' . get_class($this->drawable()) + ); + } + + return $this->drawable(); + } } diff --git a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php new file mode 100644 index 000000000..29dcf95e4 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php @@ -0,0 +1,41 @@ +eachFrame(function ($frame) { + if ($this->polygon()->hasBackgroundColor()) { + imagefilledpolygon( + $frame->getCore(), + $this->polygon()->toArray(), + $this->polygon()->count(), + $this->getBackgroundColor()->toInt() + ); + } + + if ($this->polygon()->hasBorder()) { + imagesetthickness($frame->getCore(), $this->polygon()->getBorderSize()); + imagepolygon( + $frame->getCore(), + $this->polygon()->toArray(), + $this->polygon()->count(), + $this->getBorderColor()->toInt() + ); + } + }); + } +} diff --git a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php new file mode 100644 index 000000000..8f2f7fdb6 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php @@ -0,0 +1,47 @@ +polygon()->hasBackgroundColor()) { + $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + } + + if ($this->polygon()->hasBorder()) { + $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeWidth($this->polygon()->getBorderSize()); + } + + $drawing->polygon($this->points()); + + return $image->eachFrame(function ($frame) use ($drawing) { + $frame->getCore()->drawImage($drawing); + }); + } + + private function points(): array + { + $points = []; + foreach ($this->polygon() as $point) { + $points[] = ['x' => $point->getX(), 'y' => $point->getY()]; + } + + return $points; + } +} From f54b18e01cbbf20ccca3142f269ddf37fb50f0a6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 25 Jul 2022 11:19:11 +0200 Subject: [PATCH 0377/1667] Refactor draw modifiers --- .../Gd/Modifiers/DrawEllipseModifier.php | 18 ++++++------- src/Drivers/Gd/Modifiers/DrawLineModifier.php | 23 +++------------- .../Gd/Modifiers/DrawRectangleModifier.php | 14 +++++----- .../Imagick/Modifiers/DrawEllipseModifier.php | 8 +++--- .../Imagick/Modifiers/DrawLineModifier.php | 26 +++++-------------- .../Modifiers/DrawRectangleModifier.php | 8 +++--- 6 files changed, 34 insertions(+), 63 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php index d7adef56a..c113b91de 100644 --- a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php @@ -11,20 +11,20 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf public function apply(ImageInterface $image): ImageInterface { return $image->eachFrame(function ($frame) { - if ($this->drawable()->hasBorder()) { + if ($this->ellipse()->hasBorder()) { // slightly smaller ellipse to keep 1px bordered edges clean - if ($this->drawable()->hasBackgroundColor()) { + if ($this->ellipse()->hasBackgroundColor()) { imagefilledellipse( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->drawable()->getWidth() - 1, - $this->drawable()->getHeight() - 1, + $this->ellipse()->getWidth() - 1, + $this->ellipse()->getHeight() - 1, $this->getBackgroundColor()->toInt() ); } - imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize()); + imagesetthickness($frame->getCore(), $this->ellipse()->getBorderSize()); // gd's imageellipse doesn't respect imagesetthickness so i use // imagearc with 359.9 degrees here. @@ -32,8 +32,8 @@ public function apply(ImageInterface $image): ImageInterface $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->drawable()->getWidth(), - $this->drawable()->getHeight(), + $this->ellipse()->getWidth(), + $this->ellipse()->getHeight(), 0, 359.99, $this->getBorderColor()->toInt() @@ -43,8 +43,8 @@ public function apply(ImageInterface $image): ImageInterface $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->drawable()->getWidth(), - $this->drawable()->getHeight(), + $this->ellipse()->getWidth(), + $this->ellipse()->getHeight(), $this->getBackgroundColor()->toInt() ); } diff --git a/src/Drivers/Gd/Modifiers/DrawLineModifier.php b/src/Drivers/Gd/Modifiers/DrawLineModifier.php index adcc455b7..677d6345b 100644 --- a/src/Drivers/Gd/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawLineModifier.php @@ -3,9 +3,6 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Exceptions\GeometryException; -use Intervention\Image\Geometry\Line; -use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -16,24 +13,12 @@ public function apply(ImageInterface $image): ImageInterface return $image->eachFrame(function ($frame) { imageline( $frame->getCore(), - $this->drawable()->getStart()->getX(), - $this->drawable()->getStart()->getY(), - $this->drawable()->getEnd()->getX(), - $this->drawable()->getEnd()->getY(), + $this->line()->getStart()->getX(), + $this->line()->getStart()->getY(), + $this->line()->getEnd()->getX(), + $this->line()->getEnd()->getY(), $this->getBackgroundColor()->toInt() ); }); } - - public function drawable(): DrawableInterface - { - $drawable = parent::drawable(); - if (!is_a($drawable, Line::class)) { - throw new GeometryException( - 'Shape mismatch. Excepted Line::class, found ' . get_class($drawable) - ); - } - - return $drawable; - } } diff --git a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php index a12c252f7..1b5e39dc2 100644 --- a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php @@ -12,26 +12,26 @@ public function apply(ImageInterface $image): ImageInterface { $image->eachFrame(function ($frame) { // draw background - if ($this->drawable()->hasBackgroundColor()) { + if ($this->rectangle()->hasBackgroundColor()) { imagefilledrectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), - $this->position->getY() + $this->drawable()->bottomRightPoint()->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), $this->getBackgroundColor()->toInt() ); } - if ($this->drawable()->hasBorder()) { + if ($this->rectangle()->hasBorder()) { // draw border - imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize()); + imagesetthickness($frame->getCore(), $this->rectangle()->getBorderSize()); imagerectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), - $this->position->getY() + $this->drawable()->bottomRightPoint()->getY(), + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), $this->getBorderColor()->toInt() ); } diff --git a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php index 075842ea9..b0ef639db 100644 --- a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php @@ -18,16 +18,16 @@ public function apply(ImageInterface $image): ImageInterface $drawing = new ImagickDraw(); $drawing->setFillColor($this->getBackgroundColor()->getPixel()); - if ($this->drawable()->hasBorder()) { - $drawing->setStrokeWidth($this->drawable()->getBorderSize()); + if ($this->ellipse()->hasBorder()) { + $drawing->setStrokeWidth($this->ellipse()->getBorderSize()); $drawing->setStrokeColor($this->getBorderColor()->getPixel()); } $drawing->ellipse( $this->position->getX(), $this->position->getY(), - $this->drawable()->getWidth() / 2, - $this->drawable()->getHeight() / 2, + $this->ellipse()->getWidth() / 2, + $this->ellipse()->getHeight() / 2, 0, 360 ); diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php index 6c224a55d..0cd6fcd8a 100644 --- a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -4,9 +4,6 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Exceptions\GeometryException; -use Intervention\Image\Geometry\Line; -use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -16,27 +13,16 @@ public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); $drawing->setStrokeColor($this->getBackgroundColor()->getPixel()); - $drawing->setStrokeWidth($this->drawable()->getWidth()); + $drawing->setStrokeWidth($this->line()->getWidth()); $drawing->line( - $this->drawable()->getStart()->getX(), - $this->drawable()->getStart()->getY(), - $this->drawable()->getEnd()->getX(), - $this->drawable()->getEnd()->getY(), + $this->line()->getStart()->getX(), + $this->line()->getStart()->getY(), + $this->line()->getEnd()->getX(), + $this->line()->getEnd()->getY(), ); + return $image->eachFrame(function ($frame) use ($drawing) { $frame->getCore()->drawImage($drawing); }); } - - public function drawable(): DrawableInterface - { - $drawable = parent::drawable(); - if (!is_a($drawable, Line::class)) { - throw new GeometryException( - 'Shape mismatch. Excepted Line::class, found ' . get_class($drawable) - ); - } - - return $drawable; - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index f16bdb5ac..330f78743 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -17,17 +17,17 @@ public function apply(ImageInterface $image): ImageInterface // setup rectangle $drawing = new ImagickDraw(); $drawing->setFillColor($this->getBackgroundColor()->getPixel()); - if ($this->drawable()->hasBorder()) { + if ($this->rectangle()->hasBorder()) { $drawing->setStrokeColor($this->getBorderColor()->getPixel()); - $drawing->setStrokeWidth($this->drawable()->getBorderSize()); + $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); } // build rectangle $drawing->rectangle( $this->position->getX(), $this->position->getY(), - $this->position->getX() + $this->drawable()->bottomRightPoint()->getX(), - $this->position->getY() + $this->drawable()->bottomRightPoint()->getY() + $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), + $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY() ); $image->eachFrame(function ($frame) use ($drawing) { From 7be06787a3ae44d2ac73a5237f69402c19845232 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:27:59 +0200 Subject: [PATCH 0378/1667] Refactor draw modifiers --- src/Drivers/Abstract/AbstractImage.php | 7 ++--- .../Gd/Modifiers/DrawPixelModifier.php | 19 +++++++----- .../Imagick/Modifiers/DrawPixelModifier.php | 30 +++++++------------ tests/Drivers/Abstract/AbstractImageTest.php | 1 + .../Gd/Modifiers/DrawPixelModifierTest.php | 3 +- .../Modifiers/DrawPixelModifierTest.php | 4 +-- 6 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 9120d5c1a..fc2d6aadf 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -217,10 +217,9 @@ public function text(string $text, int $x, int $y, ?callable $init = null): Imag public function drawPixel(int $x, int $y, $color = null): ImageInterface { - $color = $this->handleInput($color); - $modifier = $this->resolveDriverClass('Modifiers\DrawPixelModifier', new Point($x, $y), $color); - - return $this->modify($modifier); + return $this->modify( + $this->resolveDriverClass('Modifiers\DrawPixelModifier', new Point($x, $y), $color) + ); } public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface diff --git a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php index 3f44a23b5..bab245798 100644 --- a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php @@ -3,28 +3,31 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Geometry\Point; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class DrawPixelModifier implements ModifierInterface { - public function __construct(protected Point $position, protected ColorInterface $color) - { + use CanHandleInput; + + public function __construct( + protected Point $position, + protected $color + ) { // } public function apply(ImageInterface $image): ImageInterface { - foreach ($image as $frame) { + $color = $this->handleInput($this->color); + return $image->eachFrame(function ($frame) use ($color) { imagesetpixel( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->color->toInt() + $color->toInt() ); - } - - return $image; + }); } } diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php index 00f34ba4a..1b5edc9d0 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -3,39 +3,31 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; -use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Geometry\Point; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanHandleInput; class DrawPixelModifier implements ModifierInterface { - public function __construct(protected Point $position, protected ColorInterface $color) - { + use CanHandleInput; + + public function __construct( + protected Point $position, + protected $color + ) { // } public function apply(ImageInterface $image): ImageInterface { + $color = $this->handleInput($this->color); $pixel = new ImagickDraw(); - $pixel->setFillColor($this->getColor()->getPixel()); + $pixel->setFillColor($color->getPixel()); $pixel->point($this->position->getX(), $this->position->getY()); - foreach ($image as $frame) { + return $image->eachFrame(function ($frame) use ($pixel) { $frame->getCore()->drawImage($pixel); - } - - return $image; - } - - public function getColor(): Color - { - if (!is_a($this->color, Color::class)) { - throw new DecoderException('Unable to decode given pixel color.'); - } - - return $this->color; + }); } } diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index b6546ccbf..9b5a943cd 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -11,6 +11,7 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Tests\TestCase; use Mockery; diff --git a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php index 0b5521163..e6ef8f5d0 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Modifiers\DrawPixelModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; @@ -20,7 +19,7 @@ public function testApply(): void { $image = $this->createTestImage('trim.png'); $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); - $image->modify(new DrawPixelModifier(new Point(14, 14), new Color(16777215))); + $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php index c0f3cf541..90c076d72 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php @@ -2,8 +2,6 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use ImagickPixel; -use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Drivers\Imagick\Modifiers\DrawPixelModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; @@ -21,7 +19,7 @@ public function testApply(): void { $image = $this->createTestImage('trim.png'); $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); - $image->modify(new DrawPixelModifier(new Point(14, 14), new Color(new ImagickPixel('#ffffff')))); + $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } From b5ae8a27237adb9415b450d2fae0e203bd6c708e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:30:01 +0200 Subject: [PATCH 0379/1667] Add tests for DrawRectangleModifier --- .../Modifiers/DrawRectangleModifierTest.php | 28 +++++++++++++++++++ .../Modifiers/DrawRectangleModifierTest.php | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php new file mode 100644 index 000000000..2368c6ee5 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php @@ -0,0 +1,28 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $rectangle = new Rectangle(300, 200); + $rectangle->background('ffffff'); + $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php new file mode 100644 index 000000000..876397f81 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $rectangle = new Rectangle(300, 200); + $rectangle->background('ffffff'); + $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + } +} From 71e94564a1f37f8a180378331e10960e814b0d4e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:38:47 +0200 Subject: [PATCH 0380/1667] Add tests for DrawLineModifier --- .../Gd/Modifiers/DrawLineModiferTest.php | 28 +++++++++++++++++++ .../Modifiers/DrawLineModifierTest.php | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php new file mode 100644 index 000000000..e726f3117 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php @@ -0,0 +1,28 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $line = new Line(new Point(0, 0), new Point(10, 0), 4); + $line->background('b53517'); + $image->modify(new DrawLineModifier(new Point(0, 0), $line)); + $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php new file mode 100644 index 000000000..423e27dae --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $line = new Line(new Point(0, 0), new Point(10, 0), 4); + $line->background('b53517'); + $image->modify(new DrawLineModifier(new Point(0, 0), $line)); + $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); + } +} From fe05a1e35d6e8b524050f2688f31e5276ddb2224 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:44:59 +0200 Subject: [PATCH 0381/1667] Add tests for DrawEllipseModifiers --- .../Gd/Modifiers/DrawEllipseModiferTest.php | 28 +++++++++++++++++++ .../Modifiers/DrawEllipseModifierTest.php | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php new file mode 100644 index 000000000..1c95cd3c6 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php @@ -0,0 +1,28 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $drawable = new Ellipse(10, 10); + $drawable->background('b53717'); + $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php new file mode 100644 index 000000000..a2bed0d8a --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $drawable = new Ellipse(10, 10); + $drawable->background('b53717'); + $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + } +} From de1eb734b6061dc7aa781a52f674b111817da634 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 17:50:08 +0200 Subject: [PATCH 0382/1667] Add test for DrawPolygonModifiers --- .../Gd/Modifiers/DrawPolygonModifierTest.php | 28 +++++++++++++++++++ .../Modifiers/DrawPolygonModifierTest.php | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php diff --git a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php new file mode 100644 index 000000000..1b62a24ae --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php @@ -0,0 +1,28 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); + $drawable->background('b53717'); + $image->modify(new DrawPolygonModifier($drawable)); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php new file mode 100644 index 000000000..02ee6b2be --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); + $drawable->background('b53717'); + $image->modify(new DrawPolygonModifier($drawable)); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + } +} From ece5ffa358affed67a20836fbde7835e9bf58569 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 19:02:23 +0200 Subject: [PATCH 0383/1667] Fix download uri in test environment Fix download uri in test environment for Imagemagick releases. --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ae9bec459..96edee487 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,7 +50,7 @@ jobs: - name: Install ImageMagick if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | - curl -o /tmp/ImageMagick.tar.gz -sL https://imagemagick.org/archive/ImageMagick-${{ matrix.imagemagick }}.tar.gz + curl -o /tmp/ImageMagick.tar.gz -sL https://imagemagick.org/archive/releases/ImageMagick-${{ matrix.imagemagick }}.tar.gz ( cd /tmp || exit 1 tar xf ImageMagick.tar.gz From 996560fff28272470e71139df547198d21a61f78 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 12 Aug 2022 19:16:27 +0200 Subject: [PATCH 0384/1667] Add missing methods to ImageInterface --- src/Interfaces/ImageInterface.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 4c1c9a5e3..9f4220bb5 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -38,6 +38,13 @@ public function fitDown(int $width, int $height, string $position = 'center'): I public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function drawPixel(int $x, int $y, $color = null): ImageInterface; + public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface; + public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface; + public function drawLine(callable $init = null): ImageInterface; + public function drawPolygon(callable $init = null): ImageInterface; + public function sharpen(int $amount = 10): ImageInterface; + public function flip(): ImageInterface; + public function flop(): ImageInterface; public function getWidth(): int; public function getHeight(): int; public function destroy(): void; From 4374d159317507e92401f6ddc58b7c2e78cd16d7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 23 Aug 2022 20:21:00 +0200 Subject: [PATCH 0385/1667] Add encoders for MS Windows Bitmap format (BMP) --- src/Drivers/Abstract/AbstractImage.php | 12 +++++++ src/Drivers/Gd/Encoders/BmpEncoder.php | 20 +++++++++++ src/Drivers/Imagick/Encoders/BmpEncoder.php | 26 ++++++++++++++ tests/Drivers/Gd/Encoders/BmpEncoderTest.php | 33 +++++++++++++++++ .../Imagick/Encoders/BmpEncoderTest.php | 36 +++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 src/Drivers/Gd/Encoders/BmpEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/BmpEncoder.php create mode 100644 tests/Drivers/Gd/Encoders/BmpEncoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/BmpEncoderTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index fc2d6aadf..1bd0f078c 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -82,6 +82,18 @@ public function toPng(): EncodedImage ); } + public function toBitmap(): EncodedImage + { + return $this->encode( + $this->resolveDriverClass('Encoders\BmpEncoder') + ); + } + + public function toBmp(): EncodedImage + { + return $this->toBitmap(); + } + public function greyscale(): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Encoders/BmpEncoder.php b/src/Drivers/Gd/Encoders/BmpEncoder.php new file mode 100644 index 000000000..422c8b5bd --- /dev/null +++ b/src/Drivers/Gd/Encoders/BmpEncoder.php @@ -0,0 +1,20 @@ +getBuffered(function () use ($image) { + imagebmp($image->getFrame()->getCore(), null, false); + }); + + return new EncodedImage($data, 'image/bmp'); + } +} diff --git a/src/Drivers/Imagick/Encoders/BmpEncoder.php b/src/Drivers/Imagick/Encoders/BmpEncoder.php new file mode 100644 index 000000000..6f6bd932b --- /dev/null +++ b/src/Drivers/Imagick/Encoders/BmpEncoder.php @@ -0,0 +1,26 @@ +getFrame()->getCore(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + + return new EncodedImage($imagick->getImagesBlob(), 'image/bmp'); + } +} diff --git a/tests/Drivers/Gd/Encoders/BmpEncoderTest.php b/tests/Drivers/Gd/Encoders/BmpEncoderTest.php new file mode 100644 index 000000000..d5e2480de --- /dev/null +++ b/tests/Drivers/Gd/Encoders/BmpEncoderTest.php @@ -0,0 +1,33 @@ +getTestImage(); + $encoder = new BmpEncoder(); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageBmp)); + } +} diff --git a/tests/Drivers/Imagick/Encoders/BmpEncoderTest.php b/tests/Drivers/Imagick/Encoders/BmpEncoderTest.php new file mode 100644 index 000000000..2ae1dd510 --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/BmpEncoderTest.php @@ -0,0 +1,36 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + + return new Image($imagick); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new BmpEncoder(); + $result = $encoder->encode($image); + $this->assertTrue( + MimeSniffer::createFromString($result)->matches(new ImageBmp()) + ); + } +} From b4d3ecb018b84a39ffedb77112162f307dc00c0a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 9 Jun 2023 15:57:58 +0200 Subject: [PATCH 0386/1667] Add missing property declaration --- src/Geometry/Circle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Geometry/Circle.php b/src/Geometry/Circle.php index 841d6a96c..0180d82b0 100644 --- a/src/Geometry/Circle.php +++ b/src/Geometry/Circle.php @@ -5,7 +5,7 @@ class Circle extends Ellipse { public function __construct( - int $diameter, + protected int $diameter, protected ?Point $pivot = null ) { $this->setWidth($diameter); From 33be0b1b78898c02d341e8faa6aead6901f06896 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 9 Jun 2023 15:58:29 +0200 Subject: [PATCH 0387/1667] Change minimum plattform requirement --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e121a8510..909da5f29 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "php": "^8", + "php": "^8.1", "intervention/gif": "^3.0", "intervention/mimesniffer": "^0.4.2" }, From f44205538b15731b91a2df723a8f43198ff268a1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 9 Jun 2023 15:59:06 +0200 Subject: [PATCH 0388/1667] Adjust phpunit setup --- phpunit.xml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 63454441b..0fa3ad89e 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,14 @@ From 84d46278f75a8fa7e36883340207bd3253c9928c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 28 Sep 2023 17:48:25 +0200 Subject: [PATCH 0389/1667] Add docker test environment --- Dockerfile | 35 +++++++++++++++++++++++++++++++++++ docker-compose.yml | 9 +++++++++ 2 files changed, 44 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..f3471b6cc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +FROM php:8-cli + +RUN apt update \ + && apt install -y \ + libpng-dev \ + libicu-dev \ + libpq-dev \ + libzip-dev \ + zip \ + zlib1g-dev \ + locales \ + locales-all \ + libmagickwand-dev \ + libwebp-dev \ + && pecl install imagick \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-enable imagick \ + && docker-php-ext-install \ + intl \ + opcache \ + pdo \ + pdo_pgsql \ + pdo_mysql \ + pgsql \ + fileinfo \ + mysqli \ + gd \ + bcmath \ + exif \ + zip \ + && apt-get clean + +# install composer +# +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..75a62748d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' + +services: + tests: + build: ./ + working_dir: /project + command: bash -c "composer install && ./vendor/bin/phpunit -vvv" + volumes: + - ./:/project From 703def91809307b1a328655d26e7d49d61084ddb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:34:00 +0200 Subject: [PATCH 0390/1667] Change ImageMagick file extension in Github workflows --- .github/workflows/run-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 96edee487..7d7d4ab91 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,10 +50,10 @@ jobs: - name: Install ImageMagick if: ( steps.cache-imagemagick.outputs.cache-hit != 'true' || steps.cache-imagemagick-exists.outputs.files_exists != 'true' ) run: | - curl -o /tmp/ImageMagick.tar.gz -sL https://imagemagick.org/archive/releases/ImageMagick-${{ matrix.imagemagick }}.tar.gz + curl -o /tmp/ImageMagick.tar.xz -sL https://imagemagick.org/archive/releases/ImageMagick-${{ matrix.imagemagick }}.tar.xz ( cd /tmp || exit 1 - tar xf ImageMagick.tar.gz + tar xf ImageMagick.tar.xz cd ImageMagick-${{ matrix.imagemagick }} sudo ./configure --prefix=/home/runner/im/imagemagick-${{ matrix.imagemagick }} sudo make -j$(nproc) From 32fd9481cca3d69b653b49da6d5d2333a15b43ff Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:40:04 +0200 Subject: [PATCH 0391/1667] Add PHP 8.1 to Github workflows, remove 8.0 support --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 7d7d4ab91..032093c18 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.0', '8.1' ] + php: [ '8.1', '8.2' ] imagemagick: [ '6.9.12-55', '7.1.0-40' ] imagick: [ '3.7.0' ] stability: [ prefer-stable ] From 4f51d8f0984dc72faae6daa2b8ca2744588e34e0 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:46:55 +0200 Subject: [PATCH 0392/1667] Fix deprecation warnings for PHP 8.1 --- src/Drivers/Abstract/AbstractFont.php | 2 +- src/Drivers/Gd/Modifiers/DrawPolygonModifier.php | 1 - src/Drivers/Imagick/Image.php | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 4649cc9cd..9c8784ce7 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -56,7 +56,7 @@ public function getFilename(): ?string public function hasFilename(): bool { - return is_file($this->filename); + return !is_null($this->filename) && is_file($this->filename); } public function color($color): FontInterface diff --git a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php index 29dcf95e4..cb4c930f7 100644 --- a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php @@ -22,7 +22,6 @@ public function apply(ImageInterface $image): ImageInterface imagefilledpolygon( $frame->getCore(), $this->polygon()->toArray(), - $this->polygon()->count(), $this->getBackgroundColor()->toInt() ); } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 906322afe..e7b2a9ab0 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -77,14 +77,14 @@ public function count(): int return $this->imagick->getNumberImages(); } - public function current() + public function current(): mixed { $this->imagick->setIteratorIndex($this->iteratorIndex); return new Frame($this->imagick->current()); } - public function key() + public function key(): mixed { return $this->iteratorIndex; } From 166d2f3f92bfdb5ab94816c4d02f69f9a353e527 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:50:21 +0200 Subject: [PATCH 0393/1667] Fix rounding error in RectangleTest --- tests/Geometry/RectangleTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index a74a8a5e1..7eebd9966 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -50,13 +50,13 @@ public function testSetHeight(): void public function testGetAspectRatio() { $size = new Rectangle(800, 600); - $this->assertEquals(1.33333333333, $size->getAspectRatio()); + $this->assertEquals(1.333, round($size->getAspectRatio(), 3)); $size = new Rectangle(100, 100); $this->assertEquals(1, $size->getAspectRatio()); $size = new Rectangle(1920, 1080); - $this->assertEquals(1.777777777778, $size->getAspectRatio()); + $this->assertEquals(1.778, round($size->getAspectRatio(), 3)); } public function testFitsInto() From d7fd99e4ab4a6f8d76f2db9681bd7423ac6699eb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 15:54:13 +0200 Subject: [PATCH 0394/1667] Fix deprecation warnings --- src/Geometry/Polygon.php | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index 50cf3cb0d..e7bee7bc5 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -300,7 +300,9 @@ public function align(string $position): self } foreach ($this->points as $point) { - $point->setX($point->getX() - $diff); + $point->setX( + intval($point->getX() - $diff) + ); } return $this; @@ -331,7 +333,9 @@ public function valign(string $position): self } foreach ($this->points as $point) { - $point->setY($point->getY() - $diff); + $point->setY( + intval($point->getY() - $diff), + ); } return $this; @@ -350,16 +354,24 @@ public function rotate(float $angle): self foreach ($this->points as $point) { // translate point to pivot - $point->setX($point->getX() - $this->getPivot()->getX()); - $point->setY($point->getY() - $this->getPivot()->getY()); + $point->setX( + intval($point->getX() - $this->getPivot()->getX()), + ); + $point->setY( + intval($point->getY() - $this->getPivot()->getY()), + ); // rotate point $x = $point->getX() * $cos - $point->getY() * $sin; $y = $point->getX() * $sin + $point->getY() * $cos; // translate point back - $point->setX($x + $this->getPivot()->getX()); - $point->setY($y + $this->getPivot()->getY()); + $point->setX( + intval($x + $this->getPivot()->getX()), + ); + $point->setY( + intval($y + $this->getPivot()->getY()), + ); } return $this; From 834d5cb2ba39900d1b6c2dc9f71120d5387194dc Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 16:14:11 +0200 Subject: [PATCH 0395/1667] Rename parameter --- src/Drivers/Gd/Image.php | 4 +-- src/Drivers/Imagick/Image.php | 4 +-- src/Interfaces/ImageInterface.php | 43 ++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index fdacd9d73..ad9b3754d 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -44,9 +44,9 @@ public function setLoops(int $count): self return $this; } - public function getFrame(int $key = 0): ?FrameInterface + public function getFrame(int $position = 0): ?FrameInterface { - return $this->frames->get($key); + return $this->frames->get($position); } public function addFrame(FrameInterface $frame): ImageInterface diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index e7b2a9ab0..e5cf937f4 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -24,10 +24,10 @@ public function getImagick(): Imagick return $this->imagick; } - public function getFrame(int $key = 0): ?FrameInterface + public function getFrame(int $position = 0): ?FrameInterface { try { - $this->imagick->setIteratorIndex($key); + $this->imagick->setIteratorIndex($position); } catch (ImagickException $e) { return null; } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 9f4220bb5..0eac33c3c 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -8,18 +8,59 @@ interface ImageInterface extends Traversable, Countable { - public function getFrame(int $key = 0): ?FrameInterface; + /** + * Get frame of animation image at given position starting with zero + * + * @param int $key + * @return null|FrameInterface + */ + public function getFrame(int $position = 0): ?FrameInterface; public function addFrame(FrameInterface $frame): ImageInterface; public function setLoops(int $count): ImageInterface; public function getLoops(): int; public function getSize(): SizeInterface; public function isAnimated(): bool; public function modify(ModifierInterface $modifier): ImageInterface; + + /** + * Encode image with given encoder + * + * @param EncoderInterface $encoder + * @return EncodedImage + */ public function encode(EncoderInterface $encoder): EncodedImage; + + /** + * Encode image to jpeg format + * + * @param int $quality + * @return EncodedImage + */ public function toJpeg(int $quality = 75): EncodedImage; + + /** + * Encode image to webp format + * + * @param int $quality + * @return EncodedImage + */ public function toWebp(int $quality = 75): EncodedImage; + + /** + * Encode image to gif format + * + * @return EncodedImage + */ public function toGif(): EncodedImage; + + + /** + * Encode image to png format + * + * @return EncodedImage + */ public function toPng(): EncodedImage; + public function pickColors(int $x, int $y): CollectionInterface; public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface; public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; From af9fea016ae6255bc13eab2cce9dbec1d6aefcb2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 16:22:40 +0200 Subject: [PATCH 0396/1667] Add doc blocks --- src/Interfaces/ImageInterface.php | 116 +++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 0eac33c3c..34262ad3a 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -15,11 +15,51 @@ interface ImageInterface extends Traversable, Countable * @return null|FrameInterface */ public function getFrame(int $position = 0): ?FrameInterface; + + /** + * Add frame to animated image + * + * @param FrameInterface $frame + * @return ImageInterface + */ public function addFrame(FrameInterface $frame): ImageInterface; + + /** + * Set loop count of animated image + * + * @param int $count + * @return ImageInterface + */ public function setLoops(int $count): ImageInterface; + + /** + * Return loop count of animated image + * + * @return int + */ public function getLoops(): int; + + /** + * Return size of current image + * + * @return SizeInterface + */ public function getSize(): SizeInterface; + + /** + * Determine if current image is animated + * + * @return bool + */ public function isAnimated(): bool; + + + /** + * Apply given modifier to current image + * + * @param ModifierInterface $modifier + * @return ImageInterface + */ public function modify(ModifierInterface $modifier): ImageInterface; /** @@ -61,10 +101,24 @@ public function toGif(): EncodedImage; */ public function toPng(): EncodedImage; + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function pickColors(int $x, int $y): CollectionInterface; public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface; - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + + /** + * Turn image into a greyscale version + * + * @return void + */ public function greyscale(): ImageInterface; + + + /** + * Blur current image by given strength + * + * @param int $amount + * @return ImageInterface + */ public function blur(int $amount = 5): ImageInterface; public function rotate(float $angle, $background = 'ffffff'): ImageInterface; public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface; @@ -80,13 +134,73 @@ public function pad(int $width, int $height, $background = 'ffffff', string $pos public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; public function drawPixel(int $x, int $y, $color = null): ImageInterface; public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface; + + /** + * Draw ellipse ot given position on current image + * + * @param int $x + * @param int $y + * @param null|callable $init + * @return ImageInterface + */ public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface; + + /** + * Draw line on image + * + * @param callable|null $init + * @return ImageInterface + */ public function drawLine(callable $init = null): ImageInterface; + + /** + * Draw polygon on image + * + * @param callable|null $init + * @return ImageInterface + */ public function drawPolygon(callable $init = null): ImageInterface; + + /** + * Sharpen the current image with given strength + * + * @param int $amount + * @return ImageInterface + */ public function sharpen(int $amount = 10): ImageInterface; + + /** + * Mirror the current image horizontally + * + * @return void + */ public function flip(): ImageInterface; + + /** + * Mirror the current image vertically + * + * @return void + */ public function flop(): ImageInterface; + + /** + * Return image width in pixels + * + * @return int + */ public function getWidth(): int; + + /** + * Return image height in pixels + * + * @return int + */ public function getHeight(): int; + + /** + * Destroy current image instance and free up memory + * + * @return void + */ public function destroy(): void; } From b8f753b33f4b3601a4a7088a92b99bf9e81f427f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 16:25:33 +0200 Subject: [PATCH 0397/1667] Fix deprecation warning --- src/Geometry/Polygon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Geometry/Polygon.php b/src/Geometry/Polygon.php index e7bee7bc5..244dd7ce4 100644 --- a/src/Geometry/Polygon.php +++ b/src/Geometry/Polygon.php @@ -106,7 +106,7 @@ public function offsetExists($offset): bool * @param mixed $offset * @return Point */ - public function offsetGet($offset) + public function offsetGet($offset): mixed { return $this->points[$offset]; } From cdee873d8a02dc8355d6548b1c7fade0543a3d6a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 29 Sep 2023 17:06:31 +0200 Subject: [PATCH 0398/1667] Change code examples and requirements in readme --- readme.md | 4 +-- src/Interfaces/ImageInterface.php | 43 ++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 7704f23e1..0cd1f4d54 100644 --- a/readme.md +++ b/readme.md @@ -23,7 +23,7 @@ $manager = new ImageManager('gd') $image = $manager->make('images/example.gif'); // resize image instance -$image->resize(320, 240); +$image->resize(height: 300); // insert a watermark $image->place('images/watermark.png'); @@ -37,7 +37,7 @@ $encoded->save('images/example.jpg'); ## Requirements -- PHP >=8.0 +- PHP >= 8.1 ## Supported Image Libraries diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 34262ad3a..2e28106fa 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -103,6 +103,16 @@ public function toPng(): EncodedImage; public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function pickColors(int $x, int $y): CollectionInterface; + + /** + * Draw text on image + * + * @param string $text + * @param int $x + * @param int $y + * @param null|callable $init + * @return ImageInterface + */ public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface; /** @@ -120,8 +130,29 @@ public function greyscale(): ImageInterface; * @return ImageInterface */ public function blur(int $amount = 5): ImageInterface; + + + /** + * Rotate current image by given angle + * + * @param float $angle + * @param string $background + * @return ImageInterface + */ public function rotate(float $angle, $background = 'ffffff'): ImageInterface; + + + /** + * Place another image into the current image instance + * + * @param mixed $element + * @param string $position + * @param int $offset_x + * @param int $offset_y + * @return ImageInterface + */ public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface; + public function fill($color, ?int $x = null, ?int $y = null): ImageInterface; public function pixelate(int $size): ImageInterface; public function resize(?int $width = null, ?int $height = null): ImageInterface; @@ -138,9 +169,9 @@ public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInte /** * Draw ellipse ot given position on current image * - * @param int $x - * @param int $y - * @param null|callable $init + * @param int $x + * @param int $y + * @param null|callable $init * @return ImageInterface */ public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface; @@ -148,7 +179,7 @@ public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterf /** * Draw line on image * - * @param callable|null $init + * @param callable|null $init * @return ImageInterface */ public function drawLine(callable $init = null): ImageInterface; @@ -156,7 +187,7 @@ public function drawLine(callable $init = null): ImageInterface; /** * Draw polygon on image * - * @param callable|null $init + * @param callable|null $init * @return ImageInterface */ public function drawPolygon(callable $init = null): ImageInterface; @@ -164,7 +195,7 @@ public function drawPolygon(callable $init = null): ImageInterface; /** * Sharpen the current image with given strength * - * @param int $amount + * @param int $amount * @return ImageInterface */ public function sharpen(int $amount = 10): ImageInterface; From 78b968a28c95993d5ae26cbdcfa1541baca304ee Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 10:19:01 +0200 Subject: [PATCH 0399/1667] Refactorin input handler chain building process --- src/Drivers/Abstract/AbstractInputHandler.php | 35 ++++++++++++++++++- src/Drivers/Gd/InputHandler.php | 35 +++++++------------ src/Drivers/Imagick/InputHandler.php | 35 +++++++------------ 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php index fce61f859..0ed0e0bbd 100644 --- a/src/Drivers/Abstract/AbstractInputHandler.php +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -3,13 +3,46 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; abstract class AbstractInputHandler { - abstract protected function chain(): AbstractDecoder; + /** + * Array of decoders which will be stacked into to the input handler chain + */ + protected $decoders = []; + /** + * Stack the decoder array into a nested decoder object + * + * @return AbstractDecoder + */ + protected function chain(): AbstractDecoder + { + if (count($this->decoders) == 0) { + throw new DecoderException('No decoders found in ' . get_class($this)); + } + + // get instance of last decoder in stack + list($classname) = array_slice(array_reverse($this->decoders), 0, 1); + $chain = new $classname(); + + // build decoder chain + foreach (array_slice(array_reverse($this->decoders), 1) as $classname) { + $chain = new $classname($chain); + } + + return $chain; + } + + /** + * Try to decode the given input with each decoder of the the handler chain + * + * @param mixed $var + * @return ImageInterface|ColorInterface + */ public function handle($input): ImageInterface|ColorInterface { return $this->chain()->handle($input); diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index f32612e6f..89dccc71a 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -3,30 +3,19 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; -use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; class InputHandler extends AbstractInputHandler { - protected function chain(): AbstractDecoder - { - return new Decoders\ImageObjectDecoder( - new Decoders\ArrayColorDecoder( - new Decoders\HtmlColorNameDecoder( - new Decoders\RgbStringColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() - ) - ) - ) - ) - ) - ) - ) - ) - ); - } + protected $decoders = [ + Decoders\ImageObjectDecoder::class, + Decoders\ArrayColorDecoder::class, + Decoders\HtmlColorNameDecoder::class, + Decoders\RgbStringColorDecoder::class, + Decoders\HexColorDecoder::class, + Decoders\TransparentColorDecoder::class, + Decoders\FilePathImageDecoder::class, + Decoders\BinaryImageDecoder::class, + Decoders\DataUriImageDecoder::class, + Decoders\Base64ImageDecoder::class, + ]; } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 90577ff19..05a4100ec 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -3,30 +3,19 @@ namespace Intervention\Image\Drivers\Imagick; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; -use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; class InputHandler extends AbstractInputHandler { - protected function chain(): AbstractDecoder - { - return new Decoders\ImageObjectDecoder( - new Decoders\ArrayColorDecoder( - new Decoders\HexColorDecoder( - new Decoders\HtmlColorNameDecoder( - new Decoders\RgbStringColorDecoder( - new Decoders\TransparentColorDecoder( - new Decoders\FilePathImageDecoder( - new Decoders\BinaryImageDecoder( - new Decoders\DataUriImageDecoder( - new Decoders\Base64ImageDecoder() - ) - ) - ) - ) - ) - ) - ) - ) - ); - } + protected $decoders = [ + Decoders\ImageObjectDecoder::class, + Decoders\ArrayColorDecoder::class, + Decoders\HexColorDecoder::class, + Decoders\HtmlColorNameDecoder::class, + Decoders\RgbStringColorDecoder::class, + Decoders\TransparentColorDecoder::class, + Decoders\FilePathImageDecoder::class, + Decoders\BinaryImageDecoder::class, + Decoders\DataUriImageDecoder::class, + Decoders\Base64ImageDecoder::class + ]; } From 1922fae048d47efd2d8be673fc1462bb39e00818 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 10:32:34 +0200 Subject: [PATCH 0400/1667] Add doc block documentation --- src/Interfaces/ImageInterface.php | 73 ++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 2e28106fa..7930a826d 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -153,16 +153,85 @@ public function rotate(float $angle, $background = 'ffffff'): ImageInterface; */ public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface; - public function fill($color, ?int $x = null, ?int $y = null): ImageInterface; - public function pixelate(int $size): ImageInterface; + /** + * Stretch the image to the desired size + * + * @param null|int $width + * @param null|int $height + * @return ImageInterface + */ public function resize(?int $width = null, ?int $height = null): ImageInterface; + + /** + * Stretch the image to the desired size but do not exceed the original size + * + * @param null|int $width + * @param null|int $height + * @return ImageInterface + */ public function resizeDown(?int $width = null, ?int $height = null): ImageInterface; + + /** + * Resize the image and keep the image aspect ration proportions + * + * @param null|int $width + * @param null|int $height + * @return ImageInterface + */ public function scale(?int $width = null, ?int $height = null): ImageInterface; + + /** + * Resize the image and keep the image aspect ration proportions but do not exceed the original size + * + * @param null|int $width + * @param null|int $height + * @return ImageInterface + */ public function scaleDown(?int $width = null, ?int $height = null): ImageInterface; + + /** + * + * Takes the given dimensions and scales it to the largest possible size matching + * the original size. Then this size is positioned on the original and cut out + * before being resized to the desired size from the arguments + * + * @param int $width + * @param int $height + * @param string $position + * @return ImageInterface + */ public function fit(int $width, int $height, string $position = 'center'): ImageInterface; + + /** + * Same as fit() but do not exceeds the original image size + * + * @param int $width + * @param int $height + * @param string $position + * @return ImageInterface + */ public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface; + + /** + * @param int $width + * @param int $height + * @param string $background + * @param string $position + * @return ImageInterface + */ public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + + /** + * @param int $width + * @param int $height + * @param string $background + * @param string $position + * @return ImageInterface + */ public function padDown(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface; + + public function fill($color, ?int $x = null, ?int $y = null): ImageInterface; + public function pixelate(int $size): ImageInterface; public function drawPixel(int $x, int $y, $color = null): ImageInterface; public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface; From 50f7ebce783d8dda3282485cb1e665b562550b17 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 10:56:30 +0200 Subject: [PATCH 0401/1667] Fix random bugs --- src/Drivers/Abstract/AbstractImage.php | 2 +- src/Drivers/Abstract/AbstractInputHandler.php | 2 +- src/Drivers/Gd/Modifiers/PadDownModifier.php | 2 +- .../Imagick/Modifiers/DrawPixelModifier.php | 21 ++++++++++++---- .../Imagick/Modifiers/DrawPolygonModifier.php | 24 +++++++++++++++++++ .../Modifiers/DrawRectangleModifier.php | 5 ++-- .../Imagick/Modifiers/PadDownModifier.php | 2 +- src/Drivers/Imagick/Modifiers/TextWriter.php | 18 +------------- src/Geometry/Rectangle.php | 4 ++-- src/Interfaces/ImageInterface.php | 17 +++++++++---- 10 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1bd0f078c..25a6cf35a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -25,7 +25,7 @@ abstract class AbstractImage implements ImageInterface use CanHandleInput; use CanRunCallback; - public function eachFrame(callable $callback): self + public function eachFrame(callable $callback): ImageInterface { foreach ($this as $frame) { $callback($frame); diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php index 0ed0e0bbd..d0f25c9ba 100644 --- a/src/Drivers/Abstract/AbstractInputHandler.php +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -40,7 +40,7 @@ protected function chain(): AbstractDecoder /** * Try to decode the given input with each decoder of the the handler chain * - * @param mixed $var + * @param mixed $input * @return ImageInterface|ColorInterface */ public function handle($input): ImageInterface|ColorInterface diff --git a/src/Drivers/Gd/Modifiers/PadDownModifier.php b/src/Drivers/Gd/Modifiers/PadDownModifier.php index 53247d6c1..a819158ed 100644 --- a/src/Drivers/Gd/Modifiers/PadDownModifier.php +++ b/src/Drivers/Gd/Modifiers/PadDownModifier.php @@ -13,7 +13,7 @@ protected function getCropSize(ImageInterface $image): SizeInterface $resize = $this->getResizeSize($image); return $image->getSize() - ->contain($resize->width(), $resize->height()) + ->contain($resize->getWidth(), $resize->getHeight()) ->alignPivotTo($resize, $this->position); } diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php index 1b5edc9d0..d975f93f8 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -3,6 +3,8 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; +use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -12,16 +14,14 @@ class DrawPixelModifier implements ModifierInterface { use CanHandleInput; - public function __construct( - protected Point $position, - protected $color - ) { + public function __construct(protected Point $position, protected mixed $color) + { // } public function apply(ImageInterface $image): ImageInterface { - $color = $this->handleInput($this->color); + $color = $this->decodeColor(); $pixel = new ImagickDraw(); $pixel->setFillColor($color->getPixel()); $pixel->point($this->position->getX(), $this->position->getY()); @@ -30,4 +30,15 @@ public function apply(ImageInterface $image): ImageInterface $frame->getCore()->drawImage($pixel); }); } + + private function decodeColor(): Color + { + $color = $this->handleInput($this->color); + + if (!is_a($color, Color::class)) { + throw new TypeException('Color is not compatible to current driver.'); + } + + return $color; + } } diff --git a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php index 8f2f7fdb6..8c39081bd 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php @@ -4,6 +4,8 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -44,4 +46,26 @@ private function points(): array return $points; } + + protected function getBackgroundColor(): ?Color + { + $color = parent::getBackgroundColor(); + + if (!is_a($color, Color::class)) { + throw new TypeException('Color is not compatible to current driver.'); + } + + return $color; + } + + protected function getBorderColor(): ?Color + { + $color = parent::getBorderColor(); + + if (!is_a($color, Color::class)) { + throw new TypeException('Color is not compatible to current driver.'); + } + + return $color; + } } diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index 330f78743..8993de8aa 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -8,7 +8,6 @@ use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Interfaces\ColorInterface; class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface { @@ -37,7 +36,7 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - protected function getBackgroundColor(): ColorInterface + protected function getBackgroundColor(): Color { $color = parent::getBackgroundColor(); if (!is_a($color, Color::class)) { @@ -47,7 +46,7 @@ protected function getBackgroundColor(): ColorInterface return $color; } - protected function getBorderColor(): ColorInterface + protected function getBorderColor(): Color { $color = parent::getBorderColor(); if (!is_a($color, Color::class)) { diff --git a/src/Drivers/Imagick/Modifiers/PadDownModifier.php b/src/Drivers/Imagick/Modifiers/PadDownModifier.php index f6de2fc4f..c212134aa 100644 --- a/src/Drivers/Imagick/Modifiers/PadDownModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadDownModifier.php @@ -13,7 +13,7 @@ protected function getCropSize(ImageInterface $image): SizeInterface $resize = $this->getResizeSize($image); return $image->getSize() - ->contain($resize->width(), $resize->height()) + ->contain($resize->getWidth(), $resize->getHeight()) ->alignPivotTo($resize, $this->position); } diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index b0e1572c3..904699831 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -5,7 +5,6 @@ use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Imagick\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter @@ -23,27 +22,12 @@ public function apply(ImageInterface $image): ImageInterface $line ); } - - // debug - // $lines = new TextBlock($this->text); - // $box = $lines->getBoundingBox($this->font, $this->position); - // $points = []; - // foreach (array_chunk($box->toArray(), 2) as $p) { - // $points[] = ['x' => $p[0], 'y' => $p[1]]; - // } - // $draw = new \ImagickDraw(); - // $draw->setStrokeOpacity(1); - // $draw->setStrokeColor('black'); - // $draw->setFillColor('transparent'); - // $draw->polygon($points); - // $frame->getCore()->drawImage($draw); - } return $image; } - protected function getFont(): FontInterface + protected function getFont(): Font { if (!is_a($this->font, Font::class)) { throw new FontException('Font is not compatible to current driver.'); diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index c7e5b1bbc..b244a60c5 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -177,8 +177,8 @@ public function alignPivotTo(SizeInterface $size, string $position): self * Calculate the relative position to another Size * based on the pivot point settings of both sizes. * - * @param Size $size - * @return Point + * @param SizeInterface $rectangle + * @return PointInterface */ public function getRelativePositionTo(SizeInterface $rectangle): PointInterface { diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 7930a826d..4b04f7619 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -11,7 +11,7 @@ interface ImageInterface extends Traversable, Countable /** * Get frame of animation image at given position starting with zero * - * @param int $key + * @param int $position * @return null|FrameInterface */ public function getFrame(int $position = 0): ?FrameInterface; @@ -24,6 +24,15 @@ public function getFrame(int $position = 0): ?FrameInterface; */ public function addFrame(FrameInterface $frame): ImageInterface; + + /** + * Apply given callback to each frame of the image + * + * @param callable $callback + * @return ImageInterface + */ + public function eachFrame(callable $callback): ImageInterface; + /** * Set loop count of animated image * @@ -118,7 +127,7 @@ public function text(string $text, int $x, int $y, ?callable $init = null): Imag /** * Turn image into a greyscale version * - * @return void + * @return ImageInterface */ public function greyscale(): ImageInterface; @@ -272,14 +281,14 @@ public function sharpen(int $amount = 10): ImageInterface; /** * Mirror the current image horizontally * - * @return void + * @return ImageInterface */ public function flip(): ImageInterface; /** * Mirror the current image vertically * - * @return void + * @return ImageInterface */ public function flop(): ImageInterface; From 25572c0ef9f694c3acfbc97173b48a294f5a5656 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 11:30:03 +0200 Subject: [PATCH 0402/1667] Refactor checking for driver objects --- src/Drivers/Abstract/AbstractFont.php | 2 ++ src/Drivers/Abstract/AbstractTextWriter.php | 3 ++ .../Modifiers/AbstractDrawModifier.php | 2 ++ .../Modifiers/AbstractPadModifier.php | 3 ++ .../Modifiers/AbstractRotateModifier.php | 2 ++ src/Drivers/Gd/Modifiers/TextWriter.php | 27 +++++----------- src/Drivers/Imagick/Font.php | 16 ++-------- .../Imagick/Modifiers/DrawEllipseModifier.php | 31 ++++--------------- .../Imagick/Modifiers/DrawLineModifier.php | 4 ++- .../Imagick/Modifiers/DrawPixelModifier.php | 20 +++++------- .../Imagick/Modifiers/DrawPolygonModifier.php | 30 +++--------------- .../Modifiers/DrawRectangleModifier.php | 30 ++++-------------- src/Drivers/Imagick/Modifiers/PadModifier.php | 7 +---- .../Imagick/Modifiers/RotateModifier.php | 6 +--- src/Drivers/Imagick/Modifiers/TextWriter.php | 15 +++------ src/Traits/CanCheckType.php | 17 ++++++++++ src/Traits/CanHandleInput.php | 11 +++++-- .../Modifiers/AbstractRotateModifierTest.php | 2 +- 18 files changed, 82 insertions(+), 146 deletions(-) create mode 100644 src/Traits/CanCheckType.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index 9c8784ce7..dd39739cd 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -4,11 +4,13 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FontInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Traits\CanHandleInput; abstract class AbstractFont implements FontInterface { use CanHandleInput; + use CanCheckType; protected $size = 12; protected $angle = 0; diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php index 502f41726..29078bd7e 100644 --- a/src/Drivers/Abstract/AbstractTextWriter.php +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -5,10 +5,13 @@ use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Typography\TextBlock; abstract class AbstractTextWriter implements ModifierInterface { + use CanCheckType; + public function __construct( protected Point $position, protected FontInterface $font, diff --git a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php index 42e89effa..50d67c0ab 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php @@ -11,11 +11,13 @@ use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\PointInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Traits\CanHandleInput; class AbstractDrawModifier { use CanHandleInput; + use CanCheckType; public function __construct( protected PointInterface $position, diff --git a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php index 521dab302..0ae8e7256 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractPadModifier.php @@ -5,9 +5,12 @@ use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Traits\CanCheckType; abstract class AbstractPadModifier { + use CanCheckType; + public function __construct( protected int $width, protected int $height, diff --git a/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php b/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php index 016211e35..113a4ba1c 100644 --- a/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php +++ b/src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php @@ -5,11 +5,13 @@ use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Traits\CanHandleInput; abstract class AbstractRotateModifier { use CanHandleInput; + use CanCheckType; public function __construct(protected float $angle, protected $background) { diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 8a404b448..fd117bca1 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -4,8 +4,6 @@ use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; -use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter @@ -13,30 +11,27 @@ class TextWriter extends AbstractTextWriter public function apply(ImageInterface $image): ImageInterface { $lines = $this->getAlignedTextBlock(); + $font = $this->failIfNotClass($this->getFont(), Font::class); + foreach ($image as $frame) { if ($this->font->hasFilename()) { foreach ($lines as $line) { imagettftext( $frame->getCore(), - $this->getFont()->getSize(), - $this->getFont()->getAngle() * (-1), + $font->getSize(), + $font->getAngle() * (-1), $line->getPosition()->getX(), $line->getPosition()->getY(), - $this->getFont()->getColor()->toInt(), - $this->getFont()->getFilename(), + $font->getColor()->toInt(), + $font->getFilename(), $line ); } - - // debug - // $lines = new TextBlock($this->text); - // $box = $lines->getBoundingBox($this->font, $this->position); - // imagepolygon($frame->getCore(), $box->toArray(), 0); } else { foreach ($lines as $line) { imagestring( $frame->getCore(), - $this->getFont()->getGdFont(), + $font->getGdFont(), $line->getPosition()->getX(), $line->getPosition()->getY(), $line, @@ -48,12 +43,4 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - - protected function getFont(): FontInterface - { - if (!is_a($this->font, Font::class)) { - throw new FontException('Font is not compatible to current driver.'); - } - return $this->font; - } } diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 8e2394af2..2e8441bf1 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -8,7 +8,6 @@ use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; -use Intervention\Image\Interfaces\ColorInterface; class Font extends AbstractFont { @@ -18,28 +17,19 @@ public function toImagickDraw(): ImagickDraw throw new FontException('No font file specified.'); } + $color = $this->failIfNotClass($this->getColor(), Color::class); + $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); - $draw->setFillColor($this->getColor()->getPixel()); + $draw->setFillColor($color->getPixel()); $draw->setTextAlignment(Imagick::ALIGN_LEFT); return $draw; } - public function getColor(): ?ColorInterface - { - $color = parent::getColor(); - - if (!is_a($color, Color::class)) { - throw new FontException('Font is not compatible to current driver.'); - } - - return $color; - } - /** * Calculate box size of current font * diff --git a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php index b0ef639db..289500c2a 100644 --- a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php @@ -5,8 +5,6 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -14,13 +12,16 @@ class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterf { public function apply(ImageInterface $image): ImageInterface { - return $image->eachFrame(function ($frame) { + $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); + $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + + return $image->eachFrame(function ($frame) use ($background_color, $border_color) { $drawing = new ImagickDraw(); - $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + $drawing->setFillColor($background_color->getPixel()); if ($this->ellipse()->hasBorder()) { $drawing->setStrokeWidth($this->ellipse()->getBorderSize()); - $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeColor($border_color->getPixel()); } $drawing->ellipse( @@ -35,24 +36,4 @@ public function apply(ImageInterface $image): ImageInterface $frame->getCore()->drawImage($drawing); }); } - - protected function getBackgroundColor(): ColorInterface - { - $color = parent::getBackgroundColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode background color.'); - } - - return $color; - } - - protected function getBorderColor(): ColorInterface - { - $color = parent::getBorderColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode border color.'); - } - - return $color; - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php index 0cd6fcd8a..17c4ac760 100644 --- a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -4,6 +4,7 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Imagick\Color; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -12,7 +13,8 @@ class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); - $drawing->setStrokeColor($this->getBackgroundColor()->getPixel()); + $color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); + $drawing->setStrokeColor($color->getPixel()); $drawing->setStrokeWidth($this->line()->getWidth()); $drawing->line( $this->line()->getStart()->getX(), diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php index d975f93f8..208889918 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -4,15 +4,16 @@ use ImagickDraw; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Traits\CanCheckType; use Intervention\Image\Traits\CanHandleInput; class DrawPixelModifier implements ModifierInterface { use CanHandleInput; + use CanCheckType; public function __construct(protected Point $position, protected mixed $color) { @@ -21,7 +22,11 @@ public function __construct(protected Point $position, protected mixed $color) public function apply(ImageInterface $image): ImageInterface { - $color = $this->decodeColor(); + $color = $this->failIfNotClass( + $this->handleInput($this->color), + Color::class, + ); + $pixel = new ImagickDraw(); $pixel->setFillColor($color->getPixel()); $pixel->point($this->position->getX(), $this->position->getY()); @@ -30,15 +35,4 @@ public function apply(ImageInterface $image): ImageInterface $frame->getCore()->drawImage($pixel); }); } - - private function decodeColor(): Color - { - $color = $this->handleInput($this->color); - - if (!is_a($color, Color::class)) { - throw new TypeException('Color is not compatible to current driver.'); - } - - return $color; - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php index 8c39081bd..ea8b511c1 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php @@ -5,7 +5,6 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\TypeException; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -21,12 +20,15 @@ public function __construct( public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); + $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); + $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + if ($this->polygon()->hasBackgroundColor()) { - $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + $drawing->setFillColor($background_color->getPixel()); } if ($this->polygon()->hasBorder()) { - $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeColor($border_color->getPixel()); $drawing->setStrokeWidth($this->polygon()->getBorderSize()); } @@ -46,26 +48,4 @@ private function points(): array return $points; } - - protected function getBackgroundColor(): ?Color - { - $color = parent::getBackgroundColor(); - - if (!is_a($color, Color::class)) { - throw new TypeException('Color is not compatible to current driver.'); - } - - return $color; - } - - protected function getBorderColor(): ?Color - { - $color = parent::getBorderColor(); - - if (!is_a($color, Color::class)) { - throw new TypeException('Color is not compatible to current driver.'); - } - - return $color; - } } diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index 8993de8aa..2baf97210 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -5,7 +5,6 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -13,11 +12,14 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte { public function apply(ImageInterface $image): ImageInterface { - // setup rectangle + // setup $drawing = new ImagickDraw(); - $drawing->setFillColor($this->getBackgroundColor()->getPixel()); + $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); + $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + + $drawing->setFillColor($background_color->getPixel()); if ($this->rectangle()->hasBorder()) { - $drawing->setStrokeColor($this->getBorderColor()->getPixel()); + $drawing->setStrokeColor($border_color->getPixel()); $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); } @@ -35,24 +37,4 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - - protected function getBackgroundColor(): Color - { - $color = parent::getBackgroundColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode background color.'); - } - - return $color; - } - - protected function getBorderColor(): Color - { - $color = parent::getBorderColor(); - if (!is_a($color, Color::class)) { - throw new DecoderException('Unable to decode border color.'); - } - - return $color; - } } diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index 94d44a652..3b0d38e25 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -6,7 +6,6 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -22,11 +21,7 @@ public function apply(ImageInterface $image): ImageInterface { $resize = $this->getResizeSize($image); $crop = $this->getCropSize($image); - $background = $this->handleInput($this->background); - - if (!is_a($background, Color::class)) { - throw new DecoderException('Unable to decode backgroud color.'); - } + $background = $this->failIfNotClass($this->handleInput($this->background), Color::class); foreach ($image as $frame) { // resize current core diff --git a/src/Drivers/Imagick/Modifiers/RotateModifier.php b/src/Drivers/Imagick/Modifiers/RotateModifier.php index 304585e75..ff908c04c 100644 --- a/src/Drivers/Imagick/Modifiers/RotateModifier.php +++ b/src/Drivers/Imagick/Modifiers/RotateModifier.php @@ -4,7 +4,6 @@ use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -12,10 +11,7 @@ class RotateModifier extends AbstractRotateModifier implements ModifierInterface { public function apply(ImageInterface $image): ImageInterface { - $background = $this->backgroundColor(); - if (!is_a($background, Color::class)) { - throw new DecoderException('Unable to decode given background color.'); - } + $background = $this->failIfNotClass($this->backgroundColor(), Color::class); foreach ($image as $frame) { $frame->getCore()->rotateImage( diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php index 904699831..4bf3af51c 100644 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -4,7 +4,6 @@ use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Imagick\Font; -use Intervention\Image\Exceptions\FontException; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter @@ -12,13 +11,15 @@ class TextWriter extends AbstractTextWriter public function apply(ImageInterface $image): ImageInterface { $lines = $this->getAlignedTextBlock(); + $font = $this->failIfNotClass($this->getFont(), Font::class); + foreach ($image as $frame) { foreach ($lines as $line) { $frame->getCore()->annotateImage( - $this->getFont()->toImagickDraw(), + $font->toImagickDraw(), $line->getPosition()->getX(), $line->getPosition()->getY(), - $this->getFont()->getAngle(), + $font->getAngle(), $line ); } @@ -26,12 +27,4 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - - protected function getFont(): Font - { - if (!is_a($this->font, Font::class)) { - throw new FontException('Font is not compatible to current driver.'); - } - return $this->font; - } } diff --git a/src/Traits/CanCheckType.php b/src/Traits/CanCheckType.php new file mode 100644 index 000000000..f70e85bde --- /dev/null +++ b/src/Traits/CanCheckType.php @@ -0,0 +1,17 @@ +resolveDriverClass('InputHandler')->handle($input); + $result = $this->resolveDriverClass('InputHandler')->handle($input); + + if (!is_null($check_result_against_classname) && get_class($result) != $check_result_against_classname) { + throw new DecoderException('Decoded result is not an instance of ' . $check_result_against_classname); + } + + return $result; } } diff --git a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php index 42de945b5..554e95f92 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php @@ -64,7 +64,7 @@ public function backgroundColor(): ColorInterface return parent::backgroundColor(); } - public function handleInput($input): ImageInterface|ColorInterface + public function handleInput($input, ?string $check_result_against_classname = null): ImageInterface|ColorInterface { if ($this->background === 'bad value') { throw new DecoderException(); From 20ff41b60619706b9e619f4b8753f754c7b698c0 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 11:48:42 +0200 Subject: [PATCH 0403/1667] Add static analyzer dev environment --- docker-compose.yml | 6 ++++++ readme.md | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 75a62748d..8d90fbff8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,3 +7,9 @@ services: command: bash -c "composer install && ./vendor/bin/phpunit -vvv" volumes: - ./:/project + analysis: + build: ./ + working_dir: /project + command: bash -c "composer install && ./vendor/bin/phpstan analyze --level=4 ./src" + volumes: + - ./:/project diff --git a/readme.md b/readme.md index 0cd1f4d54..059f3f625 100644 --- a/readme.md +++ b/readme.md @@ -54,6 +54,20 @@ composer require intervention/image Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/v3/). +## Development & Testing + +With this package comes a Docker image to build a test suite and analysis container. To build this container you have to have Docker installed on your system. You can run all tests with this command. + +```bash +docker-compose run --rm --build tests +``` + +Run the static analyzer on the code base. + +```bash +docker-compose run --rm --build analysis +``` + ## License Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). From baf1652e0b518b04fab75bf61b04ab62251e065f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 11:50:30 +0200 Subject: [PATCH 0404/1667] Add analyzer to Github workflow --- .github/workflows/run-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 032093c18..93a42fa8c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -94,3 +94,6 @@ jobs: - name: Execute tests run: vendor/bin/phpunit --no-coverage + + - name: Run analyzer + run: vendor/bin/phpstan analyze --level=4 ./src From 7577986980b9e80473e3e0f3d00e01a55e1087f5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 16:48:34 +0200 Subject: [PATCH 0405/1667] Add filter method to Collection --- src/Collection.php | 15 +++++++++++++++ tests/CollectionTest.php | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index 5434f83bc..76e35b139 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -134,6 +134,21 @@ public function map(callable $callback): self return new self($items); } + /** + * Run callback on each item of the collection an remove it if it does not return true + * + * @param callable $callback + * @return Collection + */ + public function filter(callable $callback): self + { + $items = array_filter($this->items, function ($item) use ($callback) { + return $callback($item); + }); + + return new self($items); + } + public function pushEach(array $data, ?callable $callback = null): CollectionInterface { if (! is_iterable($data)) { diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 9703486ef..67488a62e 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -45,6 +45,16 @@ public function testCount() $this->assertEquals(3, count($collection)); } + public function testFilter() + { + $collection = new Collection(['foo', 'bar', 'baz']); + $this->assertEquals(3, $collection->count()); + $collection = $collection->filter(function ($text) { + return substr($text, 0, 1) == 'b'; + }); + $this->assertEquals(2, $collection->count()); + } + public function testFirstLast() { $collection = new Collection(['foo', 'bar', 'baz']); From 635567e9c20a73ccb7fc07d1efe7db63c53d5ef9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 30 Sep 2023 17:00:22 +0200 Subject: [PATCH 0406/1667] Add remove animation modifiers --- src/Drivers/Abstract/AbstractImage.php | 7 ++++ .../Gd/Modifiers/RemoveAnimationModifier.php | 35 +++++++++++++++++++ src/Drivers/Imagick/Image.php | 10 +++--- .../Modifiers/RemoveAnimationModifier.php | 35 +++++++++++++++++++ src/Interfaces/ImageInterface.php | 7 ++++ .../Modifiers/RemoveAnimationModifierTest.php | 24 +++++++++++++ .../Modifiers/RemoveAnimationModifierTest.php | 24 +++++++++++++ 7 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/RemoveAnimationModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 25a6cf35a..7fbe5b4a6 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -334,6 +334,13 @@ public function padDown( ); } + public function removeAnimation(int $position = 0): ImageInterface + { + return $this->modify( + $this->resolveDriverClass('Modifiers\RemoveAnimationModifier', $position) + ); + } + public function destroy(): void { $this->modify( diff --git a/src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php b/src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php new file mode 100644 index 000000000..7802f08b8 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php @@ -0,0 +1,35 @@ +isAnimated()) { + throw new RuntimeException('Image is not animated.'); + } + + $frames = new Collection(); + foreach ($image as $key => $frame) { + if ($this->position == $key) { + $frames->push($frame); + } else { + imagedestroy($frame->getCore()); + } + } + + return new Image($frames); + } +} diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index e5cf937f4..03f2743ab 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -26,13 +26,13 @@ public function getImagick(): Imagick public function getFrame(int $position = 0): ?FrameInterface { - try { - $this->imagick->setIteratorIndex($position); - } catch (ImagickException $e) { - return null; + foreach ($this->imagick as $core) { + if ($core->getIteratorIndex() == $position) { + return new Frame($core); + } } - return new Frame($this->imagick->current()); + return null; } public function addFrame(FrameInterface $frame): ImageInterface diff --git a/src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php b/src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php new file mode 100644 index 000000000..60a21f1a2 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php @@ -0,0 +1,35 @@ +isAnimated()) { + throw new RuntimeException('Image is not animated.'); + } + + $imagick = new Imagick(); + foreach ($image as $frame) { + if ($frame->getCore()->getIteratorIndex() == $this->position) { + $imagick->addImage($frame->getCore()->getImage()); + } + } + + $image->destroy(); + + return new Image($imagick); + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 4b04f7619..aac0d79b1 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -62,6 +62,13 @@ public function getSize(): SizeInterface; */ public function isAnimated(): bool; + /** + * Remove all frames but keep the one at the specified position + * + * @param int $position + * @return ImageInterface + */ + public function removeAnimation(int $position = 0): ImageInterface; /** * Apply given modifier to current image diff --git a/tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php b/tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php new file mode 100644 index 000000000..a48473ab7 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('animation.gif'); + $this->assertEquals(8, count($image)); + $image = $image->modify(new RemoveAnimationModifier(2)); + $this->assertEquals(1, count($image)); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/RemoveAnimationModifierTest.php b/tests/Drivers/Imagick/Modifiers/RemoveAnimationModifierTest.php new file mode 100644 index 000000000..8f0591a4b --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/RemoveAnimationModifierTest.php @@ -0,0 +1,24 @@ +createTestImage('animation.gif'); + $this->assertEquals(8, count($image)); + $image = $image->modify(new RemoveAnimationModifier(2)); + $this->assertEquals(1, count($image)); + } +} From 764c6f3843e4a66b1e2e259a7fb2d43de65e47a4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 1 Oct 2023 09:59:05 +0200 Subject: [PATCH 0407/1667] Rename ImageManager::make to ImageManager::read --- readme.md | 2 +- src/ImageManager.php | 2 +- tests/ImageManagerTest.php | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 059f3f625..7e3fbe720 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,7 @@ Intervention Image is a **image handling and manipulation library written in PHP $manager = new ImageManager('gd') // open an image file -$image = $manager->make('images/example.gif'); +$image = $manager->read('images/example.gif'); // resize image instance $image->resize(height: 300); diff --git a/src/ImageManager.php b/src/ImageManager.php index 5cd249906..fef130797 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -32,7 +32,7 @@ public function create(int $width, int $height): ImageInterface * @param mixed $source * @return ImageInterface */ - public function make($source): ImageInterface + public function read($source): ImageInterface { return $this->resolveDriverClass('InputHandler')->handle($source); } diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index b95dc0584..1bbbe6453 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -25,10 +25,10 @@ public function testCreateGd() } /** @requires extension gd */ - public function testMakeGd() + public function testReadGd() { $manager = new ImageManager('gd'); - $image = $manager->make(__DIR__ . '/images/red.gif'); + $image = $manager->read(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } @@ -41,10 +41,10 @@ public function testCreateImagick() } /** @requires extension imagick */ - public function testMakeImagick() + public function testReadImagick() { $manager = new ImageManager('imagick'); - $image = $manager->make(__DIR__ . '/images/red.gif'); + $image = $manager->read(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } } From 2f2f80cd7703fef521fd74825f2a981453a2bdd2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 1 Oct 2023 15:35:11 +0200 Subject: [PATCH 0408/1667] Remove redundant code --- tests/Traits/CanCreateImagickTestImage.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Traits/CanCreateImagickTestImage.php b/tests/Traits/CanCreateImagickTestImage.php index c097c50ee..efc630161 100644 --- a/tests/Traits/CanCreateImagickTestImage.php +++ b/tests/Traits/CanCreateImagickTestImage.php @@ -2,9 +2,7 @@ namespace Intervention\Image\Tests\Traits; -use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder; -use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; trait CanCreateImagickTestImage From 41efb893732cef109d3961283975435c384c01a3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 1 Oct 2023 16:07:41 +0200 Subject: [PATCH 0409/1667] Add optimizeImageLayers call to GifEncoder --- src/Drivers/Imagick/Encoders/GifEncoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index d180dc11c..10b61d59a 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -26,6 +26,7 @@ public function encode(ImageInterface $image): EncodedImage $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); + $imagick->optimizeImageLayers(); $imagick = $imagick->deconstructImages(); return new EncodedImage($imagick->getImagesBlob(), 'image/gif'); From b57717da3dc03ad286d53f1148c7f0319fb726d6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 10:08:37 +0200 Subject: [PATCH 0410/1667] Move methods to global parent TestCase --- tests/TestCase.php | 10 ++++++++++ tests/Traits/CanCreateGdTestImage.php | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 8985b4e80..a3d0b2ba8 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -7,6 +7,16 @@ abstract class TestCase extends MockeryTestCase { + public function getTestImagePath($filename = 'test.jpg'): string + { + return sprintf('%s/images/%s', __DIR__, $filename); + } + + public function getTestImageData($filename = 'test.jpg'): string + { + return file_get_contents($this->getTestImagePath($filename)); + } + protected function assertColor($r, $g, $b, $a, ColorInterface $color) { $this->assertEquals($r, $color->red()); diff --git a/tests/Traits/CanCreateGdTestImage.php b/tests/Traits/CanCreateGdTestImage.php index 9d12ed2b7..56921887d 100644 --- a/tests/Traits/CanCreateGdTestImage.php +++ b/tests/Traits/CanCreateGdTestImage.php @@ -9,16 +9,6 @@ trait CanCreateGdTestImage { - public function getTestImagePath($filename = 'test.jpg'): string - { - return sprintf('%s/../images/%s', __DIR__, $filename); - } - - public function getTestImageData($filename = 'test.jpg'): string - { - return file_get_contents($this->getTestImagePath($filename)); - } - public function createTestImage($filename = 'test.jpg'): Image { return $this->createWithImageDecoder()->handle( From bc69b936c5513e1ed4968dfa05813883519b5e5a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 10:11:42 +0200 Subject: [PATCH 0411/1667] Remove type check --- src/Traits/CanHandleInput.php | 11 ++--------- .../Abstract/Modifiers/AbstractRotateModifierTest.php | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Traits/CanHandleInput.php b/src/Traits/CanHandleInput.php index 9ff2f75bd..42edc18a2 100644 --- a/src/Traits/CanHandleInput.php +++ b/src/Traits/CanHandleInput.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Traits; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -10,14 +9,8 @@ trait CanHandleInput { use CanResolveDriverClass; - public function handleInput($input, ?string $check_result_against_classname = null): ImageInterface|ColorInterface + public function handleInput($input): ImageInterface|ColorInterface { - $result = $this->resolveDriverClass('InputHandler')->handle($input); - - if (!is_null($check_result_against_classname) && get_class($result) != $check_result_against_classname) { - throw new DecoderException('Decoded result is not an instance of ' . $check_result_against_classname); - } - - return $result; + return $this->resolveDriverClass('InputHandler')->handle($input); } } diff --git a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php index 554e95f92..42de945b5 100644 --- a/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php +++ b/tests/Drivers/Abstract/Modifiers/AbstractRotateModifierTest.php @@ -64,7 +64,7 @@ public function backgroundColor(): ColorInterface return parent::backgroundColor(); } - public function handleInput($input, ?string $check_result_against_classname = null): ImageInterface|ColorInterface + public function handleInput($input): ImageInterface|ColorInterface { if ($this->background === 'bad value') { throw new DecoderException(); From 8eb1394b9908aebee0d750abf4e6fcd6a3aaad75 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 10:18:17 +0200 Subject: [PATCH 0412/1667] Add methods to create new animated images --- src/Drivers/Gd/ImageFactory.php | 33 +++++++++++++++++++ src/Drivers/Imagick/ImageFactory.php | 37 ++++++++++++++++++++++ src/ImageManager.php | 11 +++++++ tests/Drivers/Gd/ImageFactoryTest.php | 11 +++++++ tests/Drivers/Imagick/ImageFactoryTest.php | 11 +++++++ 5 files changed, 103 insertions(+) diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 7dbc2f4ea..1d92a3689 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -3,11 +3,16 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Collection; +use Intervention\Image\Drivers\Gd\Frame; +use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Traits\CanHandleInput; class ImageFactory implements FactoryInterface { + use CanHandleInput; + public function newImage(int $width, int $height): ImageInterface { return new Image( @@ -17,6 +22,34 @@ public function newImage(int $width, int $height): ImageInterface ); } + public function newAnimation(callable $callback): ImageInterface + { + $frames = new Collection(); + + $animation = new class ($frames) extends ImageFactory + { + public function __construct(public Collection $frames) + { + // + } + + public function add($source, float $delay = 1): self + { + $this->frames->push( + $this->handleInput($source) + ->getFrame() + ->setDelay($delay) + ); + + return $this; + } + }; + + $callback($animation); + + return new Image($frames); + } + public function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index cc54048b7..773f1a532 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -4,16 +4,53 @@ use Imagick; use ImagickPixel; +use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Interfaces\FactoryInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Traits\CanCheckType; +use Intervention\Image\Traits\CanHandleInput; class ImageFactory implements FactoryInterface { + use CanHandleInput; + use CanCheckType; + public function newImage(int $width, int $height): ImageInterface { return new Image($this->newCore($width, $height)); } + public function newAnimation(callable $callback): ImageInterface + { + $imagick = new Imagick(); + $imagick->setFormat('gif'); + + $animation = new class ($imagick) extends ImageFactory + { + public function __construct(public Imagick $imagick) + { + // + } + + public function add($source, float $delay = 1): self + { + $imagick = $this->failIfNotClass( + $this->handleInput($source), + Image::class, + )->getImagick(); + $imagick->setImageDelay($delay * 100); + + $this->imagick->addImage($imagick); + + return $this; + } + }; + + $callback($animation); + + return new Image($animation->imagick); + } + public function newCore(int $width, int $height) { $imagick = new Imagick(); diff --git a/src/ImageManager.php b/src/ImageManager.php index fef130797..1703cf84e 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -26,6 +26,17 @@ public function create(int $width, int $height): ImageInterface return $this->resolveDriverClass('ImageFactory')->newImage($width, $height); } + /** + * Create new animated image from sources + * + * @param callable $callback + * @return ImageInterface + */ + public function animate(callable $callback): ImageInterface + { + return $this->resolveDriverClass('ImageFactory')->newAnimation($callback); + } + /** * Create new image instance from source * diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 32cf83d46..38060e45e 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -20,6 +20,17 @@ public function testNewImage(): void $this->assertInstanceOf(Image::class, $image); } + public function testNewAnimation(): void + { + $factory = new ImageFactory(); + $image = $factory->newAnimation(function ($animation) { + $animation->add($this->getTestImagePath('blue.gif'), 1.2); + $animation->add($this->getTestImagePath('red.gif'), 1.2); + }); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(2, $image->count()); + } + public function testNewCore(): void { $factory = new ImageFactory(); diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php index 75e40e244..635139e54 100644 --- a/tests/Drivers/Imagick/ImageFactoryTest.php +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -20,6 +20,17 @@ public function testNewImage(): void $this->assertInstanceOf(Image::class, $image); } + public function testNewAnimation(): void + { + $factory = new ImageFactory(); + $image = $factory->newAnimation(function ($animation) { + $animation->add($this->getTestImagePath('blue.gif'), 1.2); + $animation->add($this->getTestImagePath('red.gif'), 1.2); + }); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(2, $image->count()); + } + public function testNewCore(): void { $factory = new ImageFactory(); From af2ee6c11efef2d1e2143235c111a27fa938f687 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 17:24:52 +0200 Subject: [PATCH 0413/1667] Add avif encoding --- Dockerfile | 3 +- composer.json | 2 +- src/Drivers/Abstract/AbstractImage.php | 7 ++++ src/Drivers/Gd/Encoders/AvifEncoder.php | 25 ++++++++++++++ src/Drivers/Imagick/Encoders/AvifEncoder.php | 26 ++++++++++++++ src/Interfaces/ImageInterface.php | 6 ++++ tests/Drivers/Gd/Encoders/AvifEncoderTest.php | 33 ++++++++++++++++++ .../Imagick/Encoders/AvifEncoderTest.php | 34 +++++++++++++++++++ 8 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 src/Drivers/Gd/Encoders/AvifEncoder.php create mode 100644 src/Drivers/Imagick/Encoders/AvifEncoder.php create mode 100644 tests/Drivers/Gd/Encoders/AvifEncoderTest.php create mode 100644 tests/Drivers/Imagick/Encoders/AvifEncoderTest.php diff --git a/Dockerfile b/Dockerfile index f3471b6cc..b17cf7c36 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ RUN apt update \ && apt install -y \ libpng-dev \ libicu-dev \ + libavif-dev \ libpq-dev \ libzip-dev \ zip \ @@ -13,7 +14,7 @@ RUN apt update \ libmagickwand-dev \ libwebp-dev \ && pecl install imagick \ - && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp --with-avif \ && docker-php-ext-enable imagick \ && docker-php-ext-install \ intl \ diff --git a/composer.json b/composer.json index 909da5f29..b2591f2e1 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "require": { "php": "^8.1", "intervention/gif": "^3.0", - "intervention/mimesniffer": "^0.4.2" + "intervention/mimesniffer": "^0.5" }, "require-dev": { "phpunit/phpunit": "^9", diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7fbe5b4a6..3b70d93a2 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -94,6 +94,13 @@ public function toBmp(): EncodedImage return $this->toBitmap(); } + public function toAvif(): EncodedImage + { + return $this->encode( + $this->resolveDriverClass('Encoders\AvifEncoder') + ); + } + public function greyscale(): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Encoders/AvifEncoder.php b/src/Drivers/Gd/Encoders/AvifEncoder.php new file mode 100644 index 000000000..aaa39ea5b --- /dev/null +++ b/src/Drivers/Gd/Encoders/AvifEncoder.php @@ -0,0 +1,25 @@ +quality = $quality; + } + + public function encode(ImageInterface $image): EncodedImage + { + $data = $this->getBuffered(function () use ($image) { + imageavif($image->getFrame()->getCore(), null, $this->quality); + }); + + return new EncodedImage($data, 'image/avif'); + } +} diff --git a/src/Drivers/Imagick/Encoders/AvifEncoder.php b/src/Drivers/Imagick/Encoders/AvifEncoder.php new file mode 100644 index 000000000..f1aef7170 --- /dev/null +++ b/src/Drivers/Imagick/Encoders/AvifEncoder.php @@ -0,0 +1,26 @@ +getFrame()->getCore(); + $imagick->setFormat($format); + $imagick->setImageFormat($format); + $imagick->setCompression($compression); + $imagick->setImageCompression($compression); + + return new EncodedImage($imagick->getImagesBlob(), 'image/avif'); + } +} diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index aac0d79b1..34b42f12f 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -109,6 +109,12 @@ public function toWebp(int $quality = 75): EncodedImage; */ public function toGif(): EncodedImage; + /** + * Encode image to avif format + * + * @return EncodedImage + */ + public function toAvif(): EncodedImage; /** * Encode image to png format diff --git a/tests/Drivers/Gd/Encoders/AvifEncoderTest.php b/tests/Drivers/Gd/Encoders/AvifEncoderTest.php new file mode 100644 index 000000000..b2d1fa0da --- /dev/null +++ b/tests/Drivers/Gd/Encoders/AvifEncoderTest.php @@ -0,0 +1,33 @@ +getTestImage(); + $encoder = new AvifEncoder(10); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageAvif())); + } +} diff --git a/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php b/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php new file mode 100644 index 000000000..e3fa42cac --- /dev/null +++ b/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php @@ -0,0 +1,34 @@ +newImage(3, 2, new ImagickPixel('red'), 'png'); + + return new Image($imagick); + } + + public function testEncode(): void + { + $image = $this->getTestImage(); + $encoder = new AvifEncoder(10); + $result = $encoder->encode($image); + $this->assertTrue(MimeSniffer::createFromString($result)->matches(new ImageAvif())); + } +} From 70fe7fe98f33bcf8a265583560d8c4f36154f8e3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 17:36:32 +0200 Subject: [PATCH 0414/1667] Add libavif-dev to Github testing environment --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 93a42fa8c..f71b12ee2 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,7 +30,7 @@ jobs: run: | sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev sudo apt-get update - sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev + sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev libavif-dev sudo apt-get install -y libmagickwand-dev - name: Cache ImageMagick From 7535eb64291197ee52a5a00d032f17cbd7d617a3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 18:00:55 +0200 Subject: [PATCH 0415/1667] Change format of AvifEncoder --- src/Drivers/Imagick/Encoders/AvifEncoder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Imagick/Encoders/AvifEncoder.php b/src/Drivers/Imagick/Encoders/AvifEncoder.php index f1aef7170..0cc856042 100644 --- a/src/Drivers/Imagick/Encoders/AvifEncoder.php +++ b/src/Drivers/Imagick/Encoders/AvifEncoder.php @@ -12,7 +12,7 @@ class AvifEncoder extends AbstractEncoder implements EncoderInterface { public function encode(ImageInterface $image): EncodedImage { - $format = 'avif'; + $format = 'AVIF'; $compression = Imagick::COMPRESSION_ZIP; $imagick = $image->getFrame()->getCore(); From 9eb44b0ea5237c767da127c4e2e8263deb4be465 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 3 Oct 2023 18:04:31 +0200 Subject: [PATCH 0416/1667] Add debugging info to Github workflow --- .github/workflows/run-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f71b12ee2..68d7bfbfc 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -92,6 +92,9 @@ jobs: - name: Which Imagick Version run: php -r 'var_dump(Imagick::getVersion());' + - name: Supported Imagick Formats + run: php -r 'var_dump(Imagick::queryFormats());' + - name: Execute tests run: vendor/bin/phpunit --no-coverage From 82865c6755cb45750474b247e05ceb8455565d6c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 15:45:23 +0200 Subject: [PATCH 0417/1667] Add libheif-dev to Github workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 68d7bfbfc..43cbf28ac 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,7 +30,7 @@ jobs: run: | sudo apt-get -y remove imagemagick imagemagick-6-common libmagic-dev sudo apt-get update - sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev libavif-dev + sudo apt-get install -y libjpeg-dev libgif-dev libtiff-dev libpng-dev libwebp-dev libavif-dev libheif-dev sudo apt-get install -y libmagickwand-dev - name: Cache ImageMagick From defc71240dc47b1613d6739f6fdb60d0d42fdc31 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 15:49:26 +0200 Subject: [PATCH 0418/1667] Fix doc block reference --- tests/Drivers/Imagick/Encoders/AvifEncoderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php b/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php index e3fa42cac..8f2206725 100644 --- a/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php +++ b/tests/Drivers/Imagick/Encoders/AvifEncoderTest.php @@ -12,7 +12,7 @@ /** * @requires extension imagick - * @covers \Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder + * @covers \Intervention\Image\Drivers\Imagick\Encoders\AvifEncoder */ class AvifEncoderTest extends TestCase { From 1c45941711f237cc75819e44c2611d0f84a6e5b8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 16:29:28 +0200 Subject: [PATCH 0419/1667] Add file pointer image decoder --- .../Gd/Decoders/FilePointerImageDecoder.php | 25 +++++++++++++++++++ src/Drivers/Gd/InputHandler.php | 1 + .../Decoders/FilePointerImageDecoder.php | 25 +++++++++++++++++++ src/Drivers/Imagick/InputHandler.php | 1 + .../Decoders/FilePointerImageDecoderTest.php | 25 +++++++++++++++++++ .../Decoders/FilePointerImageDecoderTest.php | 25 +++++++++++++++++++ 6 files changed, 102 insertions(+) create mode 100644 src/Drivers/Gd/Decoders/FilePointerImageDecoder.php create mode 100644 src/Drivers/Imagick/Decoders/FilePointerImageDecoder.php create mode 100644 tests/Drivers/Gd/Decoders/FilePointerImageDecoderTest.php create mode 100644 tests/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php diff --git a/src/Drivers/Gd/Decoders/FilePointerImageDecoder.php b/src/Drivers/Gd/Decoders/FilePointerImageDecoder.php new file mode 100644 index 000000000..80860797d --- /dev/null +++ b/src/Drivers/Gd/Decoders/FilePointerImageDecoder.php @@ -0,0 +1,25 @@ +getTestImagePath('test.jpg'), 'r'); + $result = $decoder->decode($fp); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php b/tests/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php new file mode 100644 index 000000000..aeb4bc89f --- /dev/null +++ b/tests/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php @@ -0,0 +1,25 @@ +getTestImagePath('test.jpg'), 'r'); + $result = $decoder->decode($fp); + $this->assertInstanceOf(Image::class, $result); + } +} From 8381a5940334e18010848e452b500c66b35543d9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 16:56:03 +0200 Subject: [PATCH 0420/1667] Add EncodedImage::toFilePointer() --- src/EncodedImage.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/EncodedImage.php b/src/EncodedImage.php index a0461d844..d10144978 100644 --- a/src/EncodedImage.php +++ b/src/EncodedImage.php @@ -38,6 +38,15 @@ public function toString(): string return $this->data; } + public function toFilePointer() + { + $pointer = fopen('php://temp', 'rw'); + fputs($pointer, $this->toString()); + rewind($pointer); + + return $pointer; + } + public function __toString(): string { return $this->toString(); From 4fbc7f5840d7a7895fb38c9e77ea13582ed1a389 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 4 Oct 2023 17:09:13 +0200 Subject: [PATCH 0421/1667] Add Image::readExif() --- src/Drivers/Abstract/AbstractImage.php | 38 ++++++++++++++++++++++++ src/Exceptions/NotSupportedException.php | 8 +++++ 2 files changed, 46 insertions(+) create mode 100644 src/Exceptions/NotSupportedException.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3b70d93a2..7565dcf65 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -2,8 +2,11 @@ namespace Intervention\Image\Drivers\Abstract; +use Exception; +use Intervention\Gif\Exception\NotReadableException; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; +use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\Geometry\Circle; use Intervention\Image\Geometry\Ellipse; use Intervention\Image\Geometry\Line; @@ -348,6 +351,41 @@ public function removeAnimation(int $position = 0): ImageInterface ); } + /** + * Read exif data from current image instance + * + * Returns value of given key or null if key was not found. If no + * parameter is given an array of all available data is returned. + * + * @param null|string $tag + * @return mixed + * @throws NotSupportedException + * @throws NotReadableException + */ + public function readExif(?string $tag = null): mixed + { + if (!function_exists('exif_read_data')) { + throw new NotSupportedException( + 'Reading Exif data is not supported by this PHP installation.' + ); + } + + try { + $pointer = $this->toJpeg()->toFilePointer(); + $data = @exif_read_data($pointer); + } catch (Exception $e) { + throw new NotReadableException('Unable to read Exif data from this image.'); + } + + fclose($pointer); + + if (!is_null($tag) && is_array($data)) { + $data = array_key_exists($tag, $data) ? $data[$tag] : null; + } + + return is_array($data) ? new Collection($data) : $data; + } + public function destroy(): void { $this->modify( diff --git a/src/Exceptions/NotSupportedException.php b/src/Exceptions/NotSupportedException.php new file mode 100644 index 000000000..e4c884680 --- /dev/null +++ b/src/Exceptions/NotSupportedException.php @@ -0,0 +1,8 @@ + Date: Thu, 5 Oct 2023 16:28:19 +0200 Subject: [PATCH 0422/1667] Change signature of Collection - Removed method query() - Method get replaces query() --- src/Collection.php | 45 +++++++++++++++++------- src/Interfaces/CollectionInterface.php | 3 +- tests/CollectionTest.php | 47 +++++++++++--------------- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 76e35b139..eca7ce262 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -96,33 +96,54 @@ public function last() } /** - * Return item with given key + * Return item at given position starting at 0 * * @param integer $key * @return mixed */ - public function get(int $key = 0, $default = null) + public function getAtPosition(int $key = 0, $default = null) { - if (! array_key_exists($key, $this->items)) { + if ($this->count() == 0) { return $default; } - return $this->items[$key]; - } + $positions = array_values($this->items); + if (! array_key_exists($key, $positions)) { + return $default; + } - public function has(int $key): bool - { - return array_key_exists($key, $this->items); + return $positions[$key]; } - public function query(string $query, $default = null) + public function get(int|string $query, $default = null) { - $items = $this->getItemsFlat(); - if (!array_key_exists($query, $items)) { + if ($this->count() == 0) { return $default; } - return $items[$query]; + if (is_int($query) && array_key_exists($query, $this->items)) { + return $this->items[$query]; + } + + if (is_string($query) && strpos($query, '.') === false) { + return array_key_exists($query, $this->items) ? $this->items[$query] : $default; + } + + $query = explode('.', $query); + + $result = $default; + $items = $this->items; + foreach ($query as $key) { + if (!is_array($items) || !array_key_exists($key, $items)) { + $result = $default; + break; + } + + $result = $items[$key]; + $items = $result; + } + + return $result; } public function map(callable $callback): self diff --git a/src/Interfaces/CollectionInterface.php b/src/Interfaces/CollectionInterface.php index 9cfcbae36..f42769a3c 100644 --- a/src/Interfaces/CollectionInterface.php +++ b/src/Interfaces/CollectionInterface.php @@ -7,8 +7,7 @@ interface CollectionInterface extends Traversable { public function push($item): CollectionInterface; - public function get(int $key, $default = null); - public function has(int $key); + public function get(int|string $key, $default = null); public function first(); public function last(); public function count(): int; diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 67488a62e..228575264 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -86,24 +86,6 @@ public function testPushEach() $this->assertEquals('BAZ', $collection->get(2)); } - public function testGet() - { - $collection = new Collection(['foo', 'bar', 'baz']); - $this->assertEquals('foo', $collection->get(0)); - $this->assertEquals('bar', $collection->get(1)); - $this->assertEquals('baz', $collection->get(2)); - $this->assertNull($collection->get(3)); - $this->assertEquals('test', $collection->get(3, 'test')); - } - - public function testHas(): void - { - $collection = new Collection(['foo', 'bar']); - $this->assertTrue($collection->has(0)); - $this->assertTrue($collection->has(1)); - $this->assertFalse($collection->has(2)); - } - public function testToArray() { $collection = new Collection(['foo', 'bar', 'baz']); @@ -122,11 +104,14 @@ public function testMap(): void $this->assertEquals(['foo', 'bar', 'baz'], $mapped->toArray()); } - public function testQuery(): void + public function testGet(): void { $collection = new Collection([ - 'foo' => 'FOO', - 'bar' => 'BAR', + 'first', + 'second', + ['testx' => 'x'], + 'foo' => 'foo_value', + 'bar' => 'bar_value', 'baz' => [ 'test1' => '1', 'test2' => '2', @@ -136,12 +121,18 @@ public function testQuery(): void ] ]); - $this->assertEquals('FOO', $collection->query('foo')); - $this->assertEquals('BAR', $collection->query('bar')); - $this->assertEquals('1', $collection->query('baz.test1')); - $this->assertEquals('2', $collection->query('baz.test2')); - $this->assertEquals('value', $collection->query('baz.test3.example')); - $this->assertEquals('value', $collection->query('baz.test3.example', 'default')); - $this->assertEquals('default', $collection->query('baz.test3.no', 'default')); + $this->assertEquals('first', $collection->get(0)); + $this->assertEquals('second', $collection->get(1)); + $this->assertEquals('first', $collection->get('0')); + $this->assertEquals('second', $collection->get('1')); + $this->assertEquals('x', $collection->get('2.testx')); + $this->assertEquals('foo_value', $collection->get('foo')); + $this->assertEquals('bar_value', $collection->get('bar')); + $this->assertEquals('1', $collection->get('baz.test1')); + $this->assertEquals('2', $collection->get('baz.test2')); + $this->assertEquals('value', $collection->get('baz.test3.example')); + $this->assertEquals('value', $collection->get('baz.test3.example', 'default')); + $this->assertEquals('default', $collection->get('baz.test3.no', 'default')); + $this->assertEquals(['example' => 'value'], $collection->get('baz.test3')); } } From a62859fbf46ca74ed92fa2a90193ba3a6a8df9d8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:20:15 +0200 Subject: [PATCH 0423/1667] Add EXIF data decoding --- src/Drivers/Abstract/AbstractImage.php | 43 ++++++------------- .../Abstract/Decoders/AbstractDecoder.php | 20 +++++++++ .../Gd/Decoders/BinaryImageDecoder.php | 5 ++- .../Imagick/Decoders/BinaryImageDecoder.php | 1 + 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7565dcf65..ba849194d 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Drivers\Abstract; -use Exception; use Intervention\Gif\Exception\NotReadableException; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; @@ -21,6 +20,7 @@ use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; use Intervention\Image\Traits\CanRunCallback; +use ReflectionProperty; abstract class AbstractImage implements ImageInterface { @@ -28,6 +28,10 @@ abstract class AbstractImage implements ImageInterface use CanHandleInput; use CanRunCallback; + protected Collection $exif; + + + public function eachFrame(callable $callback): ImageInterface { foreach ($this as $frame) { @@ -351,39 +355,16 @@ public function removeAnimation(int $position = 0): ImageInterface ); } - /** - * Read exif data from current image instance - * - * Returns value of given key or null if key was not found. If no - * parameter is given an array of all available data is returned. - * - * @param null|string $tag - * @return mixed - * @throws NotSupportedException - * @throws NotReadableException - */ - public function readExif(?string $tag = null): mixed + public function setExif(array $data): ImageInterface { - if (!function_exists('exif_read_data')) { - throw new NotSupportedException( - 'Reading Exif data is not supported by this PHP installation.' - ); - } + $this->exif = new Collection($data); - try { - $pointer = $this->toJpeg()->toFilePointer(); - $data = @exif_read_data($pointer); - } catch (Exception $e) { - throw new NotReadableException('Unable to read Exif data from this image.'); - } - - fclose($pointer); - - if (!is_null($tag) && is_array($data)) { - $data = array_key_exists($tag, $data) ? $data[$tag] : null; - } + return $this; + } - return is_array($data) ? new Collection($data) : $data; + public function getExif(?string $query = null): mixed + { + return is_null($query) ? $this->exif : $this->exif->get($query); } public function destroy(): void diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 61e350d21..61fee9f3e 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Abstract\Decoders; +use Exception; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; @@ -31,6 +32,25 @@ final public function handle($input): ImageInterface|ColorInterface return $decoded; } + protected function decodeExifData(string $image_data): array + { + if (! function_exists('exif_read_data')) { + return []; + } + + try { + $pointer = fopen('php://temp', 'rw'); + fputs($pointer, $image_data); + rewind($pointer); + + $data = @exif_read_data($pointer, null, true); + } catch (Exception $e) { + $data = []; + } + + return is_array($data) ? $data : []; + } + protected function hasSuccessor(): bool { return $this->successor !== null; diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 467493b0e..60157efcf 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -42,7 +42,10 @@ public function decode($input): ImageInterface|ColorInterface imagesavealpha($gd, true); - return new Image(new Collection([new Frame($gd)])); + $image = new Image(new Collection([new Frame($gd)])); + $image->setExif($this->decodeExifData($input)); + + return $image; } protected function decodeGif($input): ImageInterface diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 5cb315967..ac4f1488c 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -28,6 +28,7 @@ public function decode($input): ImageInterface|ColorInterface $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); + $image->setExif($this->decodeExifData($input)); return $image; } From f3122ae4367b7cdab972e02804195b0ebd91aa7b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:27:51 +0200 Subject: [PATCH 0424/1667] Fix image orientation with GD driver --- .../Abstract/Decoders/AbstractDecoder.php | 22 +++++++++---------- .../Gd/Decoders/BinaryImageDecoder.php | 13 ++++++++++- src/Interfaces/ImageInterface.php | 15 +++++++++++++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/Drivers/Abstract/Decoders/AbstractDecoder.php b/src/Drivers/Abstract/Decoders/AbstractDecoder.php index 61fee9f3e..52bcd1b0b 100644 --- a/src/Drivers/Abstract/Decoders/AbstractDecoder.php +++ b/src/Drivers/Abstract/Decoders/AbstractDecoder.php @@ -32,6 +32,16 @@ final public function handle($input): ImageInterface|ColorInterface return $decoded; } + protected function hasSuccessor(): bool + { + return $this->successor !== null; + } + + protected function inputType($input): AbstractType + { + return MimeSniffer::createFromString($input)->getType(); + } + protected function decodeExifData(string $image_data): array { if (! function_exists('exif_read_data')) { @@ -42,22 +52,12 @@ protected function decodeExifData(string $image_data): array $pointer = fopen('php://temp', 'rw'); fputs($pointer, $image_data); rewind($pointer); - $data = @exif_read_data($pointer, null, true); + fclose($pointer); } catch (Exception $e) { $data = []; } return is_array($data) ? $data : []; } - - protected function hasSuccessor(): bool - { - return $this->successor !== null; - } - - protected function inputType($input): AbstractType - { - return MimeSniffer::createFromString($input)->getType(); - } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 60157efcf..ae90dd86b 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -42,10 +42,21 @@ public function decode($input): ImageInterface|ColorInterface imagesavealpha($gd, true); + // build image instance $image = new Image(new Collection([new Frame($gd)])); $image->setExif($this->decodeExifData($input)); - return $image; + // fix image orientation + return match ($image->getExif('IFD0.Orientation')) { + 2 => $image->flip(), + 3 => $image->rotate(180), + 4 => $image->rotate(180)->flip(), + 5 => $image->rotate(270)->flip(), + 6 => $image->rotate(270), + 7 => $image->rotate(90)->flip(), + 8 => $image->rotate(90), + default => $image + }; } protected function decodeGif($input): ImageInterface diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 34b42f12f..45b257db5 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Interfaces; use Countable; +use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Traversable; @@ -55,6 +56,20 @@ public function getLoops(): int; */ public function getSize(): SizeInterface; + /** + * Return exif data of current image + * + * @return mixed + */ + public function getExif(?string $query = null): mixed; + + /** + * Set exif data on current image (will not be written in final image) + * + * @return ImageInterface + */ + public function setExif(array $data): ImageInterface; + /** * Determine if current image is animated * From 79f0b3a70fe42fed89a8a88fcd33008b84ab5d6e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:35:33 +0200 Subject: [PATCH 0425/1667] Remove unused code --- src/Collection.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index eca7ce262..314e5f588 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -182,22 +182,4 @@ public function pushEach(array $data, ?callable $callback = null): CollectionInt return $this; } - - private function getItemsFlat(): array - { - $iterator = new RecursiveIteratorIterator( - new RecursiveArrayIterator($this->items) - ); - - $items = []; - foreach ($iterator as $value) { - $keys = []; - foreach (range(0, $iterator->getDepth()) as $depth) { - $keys[] = $iterator->getSubIterator($depth)->key(); - } - $items[join('.', $keys)] = $value; - } - - return $items; - } } From 26af25da1383458484356bca426d8ce8c1a00ddf Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:36:55 +0200 Subject: [PATCH 0426/1667] Remove unused code --- src/Collection.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 314e5f588..4b27a669e 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -8,8 +8,6 @@ use Countable; use Traversable; use IteratorAggregate; -use RecursiveIteratorIterator; -use RecursiveArrayIterator; class Collection implements CollectionInterface, IteratorAggregate, Countable { From d317c478717ce9117862e535228340a063bb2b8d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:42:56 +0200 Subject: [PATCH 0427/1667] Add tests for Image::getExif() & Image::setExif() --- tests/Drivers/Abstract/AbstractImageTest.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index 9b5a943cd..e4f64cd29 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -11,7 +11,6 @@ use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; -use Intervention\Image\Interfaces\PointInterface; use Intervention\Image\Tests\TestCase; use Mockery; @@ -427,6 +426,15 @@ public function testPadDown(): void $this->assertInstanceOf(ImageInterface::class, $result); } + public function testSetGetExif(): void + { + $img = $this->abstractImageMock(); + $img->setExif((['test' => 'value'])); + + $this->assertInstanceOf(Collection::class, $img->getExif()); + $this->assertEquals('value', $img->getExif('test')); + } + public function testDestroy(): void { $img = $this->abstractImageMock(); From f15a30932c64fb87955e5db36433a5c3d2cfbf33 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 5 Oct 2023 17:54:19 +0200 Subject: [PATCH 0428/1667] Add tests for EXIF data reading --- .../Gd/Decoders/BinaryImageDecoderTest.php | 17 ++++++++++++++--- .../Decoders/BinaryImageDecoderTest.php | 17 ++++++++++++++--- tests/images/exif.jpg | Bin 0 -> 7791 bytes 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 tests/images/exif.jpg diff --git a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index 632f03634..f59f04961 100644 --- a/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -15,7 +15,7 @@ class BinaryImageDecoderTest extends TestCase public function testDecodePng(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('tile.png'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); @@ -25,7 +25,7 @@ public function testDecodePng(): void public function testDecodeGif(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('red.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); @@ -35,10 +35,21 @@ public function testDecodeGif(): void public function testDecodeAnimatedGif(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('cats.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(75, $image->getWidth()); $this->assertEquals(50, $image->getHeight()); $this->assertCount(4, $image); } + + public function testDecodeJpegWithExif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('exif.jpg'))); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); + $this->assertCount(1, $image); + $this->assertEquals('Oliver Vogel', $image->getExif('IFD0.Artist')); + } } diff --git a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php index 455bdcc7b..b3d20b4a5 100644 --- a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php @@ -11,7 +11,7 @@ class BinaryImageDecoderTest extends TestCase public function testDecodePng(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/tile.png')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('tile.png'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); @@ -21,7 +21,7 @@ public function testDecodePng(): void public function testDecodeGif(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/red.gif')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('red.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); @@ -31,10 +31,21 @@ public function testDecodeGif(): void public function testDecodeAnimatedGif(): void { $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents(__DIR__ . '/../../../images/cats.gif')); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('cats.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(75, $image->getWidth()); $this->assertEquals(50, $image->getHeight()); $this->assertCount(4, $image); } + + public function testDecodeJpegWithExif(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('exif.jpg'))); + $this->assertInstanceOf(Image::class, $image); + $this->assertEquals(16, $image->getWidth()); + $this->assertEquals(16, $image->getHeight()); + $this->assertCount(1, $image); + $this->assertEquals('Oliver Vogel', $image->getExif('IFD0.Artist')); + } } diff --git a/tests/images/exif.jpg b/tests/images/exif.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ab98706c205e3054afaf64359c11890fb3d45214 GIT binary patch literal 7791 zcmeGhTW}lI_3o}-mSjhYZDIn$EXteK*2`9Gfk`acu}!1Ik>tF7I;-84ykxbj?5=Dj z9~}sxw9Irm389p9h9LoPzvopn-FN}V@5squ689k=24h_ z)z{wpIOp7R&pCVU*?V>BFVrtE(nL)vB9zIXn-D@Cpl0HHTb)DUx zr#3cdXrPHAs1Xa#r>Gd|_dx$4>SogKg#OukB;wDw7?Wf)?vW+WHIi zH;|ub^vQGvQT4x|E;2&G9hk2E8*~);JswYo$KTQ65Bj{mVDAdQe?@O!cXw}hcVEy? zN~19ci)*UW=j-h140Lq`db+y0dT`a%V@?Tv5C-+9P|$+{D8Nu5LcS_ zsVyL-lOhEK#)Ux??Qpu>o(``M9HSjcA)2v}0Yo_{hIY_Sx69*X{Bb}A8OMz)qt3Bi zTzBZ;oiSI>u~Uzy)~xN#K3#|pYDb<+yVo7hUAl0Y*N3h-efF;PEMyPv%O*7QRijchV2F1zn$eA1K~-f!F|8P)s8q`s zf%~{k%Nic9Aaiq44O65f1JA)|lc%Wd;L2itVs9Qm=3?fA8BJA-Ink(Ab{1~u0qI9G zNJA?86%mVaNJIvzq6%h(ycL@oMubgDHk5`${z6rf4M`y^0pCHW)0usrFfDg8WkR}95c0+cLh_8?o*Gxi{@De2}wF`9$z1zEQ-J4(j9J(wuV z!l4ZUwjL3paDb+C4vaBkl#uUGW z!6LN4J1uxtD$TVbd=>&^v+40(VcsxhXoPLrYRNuYPjFq0MWNYp2QvMwlfdwPtr5y= z?ePtS>lHHYAT?AJnFXIXT)d4<0uD=P-zLy06fPD4BSQ@zA}CY?a_Yg8HY7&i`=PdI zh6n_Eegi^yz5`=~cUFDL!n}sjOQPx@1xIY*!Mv?~NN)u-;c3|I2HYc`Pt%U8@sOE@ z%9I`t&AHfCUNnmoh!GMzGDHgj^`j`j8`}qy+NBkS=bE;*(MwOlM2(3`RmL$5pI%%A zwW#cHl<55PWUHowr;feRH=3NuM&K zkpmyhO^2q)EKHB+I8=B`U`NGI#QZ+N@wN}ngUiR+<>Tzt$63?6ZUk>Z5V663umi0? zV-T=aRDi(E!ohD2`UX5Y=~aND`+X=_l!ilM0y8vMFuxIQ^ZG@h`i0KPIm4(V!(l}q z;PAC^fLF`m8dnKN2O?o~%a&S&;}3`iTM!{aj)wmF-S3B3Nf-_78OlU5m9#h~O&!+6 z+~FO0{_p{QL`{fM>xS%jnNQD zYN}AK*ld@yl|?A&vIOK-2qOMjoUYwW*G1uV5D#^4aa&Vr_!&%mPQO!%&qF)tS zW{7YA7Dq$)XNLaBaDOzBho2eXe<=2`NGutNn4}q1kcx+vA|<~_w2^|(2ym!=Ut)nz z7FDgxfyPpqD~aLTD`F|c+J;P$FA1y+;>C#=nM`t8(G5=F#p&_U5NJIhNkTFb9|kAf zJQ&#=izJ4l(c#E&d@M3Jyg88ucxuF&JFfCo{9diO!hbQ>JYjJ1hvET#56bf93uBTdmNu+%8*S*#gTJShm2j1(q%Fe`JB?rASoZXt4lSo|dc>T(%lnDVM|Ha5`L0 zr>nySkI&&R_JbzpKOBckR&=6Wbnna`*GI z8&|*i@)ew!603LLZ)T_7B{?-bwRf)enJ+%}tslSe+B=&@ruRvQKKtNTzx|UJ|MZWW zlDF-@{qVgfzxJJ4eOh!^1FTas|hUv@z!{kl1zb&SSAtk6-F}`nd~h zQoY%Nb~(OwFn#2jFnzH8SfCRT3{jM>d^o_XS#jhmi- q`O47~k388f^0t Date: Fri, 6 Oct 2023 16:17:42 +0200 Subject: [PATCH 0429/1667] Return empty collection if no exif data is set --- src/Drivers/Abstract/AbstractImage.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index ba849194d..3b46865fa 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -2,10 +2,8 @@ namespace Intervention\Image\Drivers\Abstract; -use Intervention\Gif\Exception\NotReadableException; use Intervention\Image\Collection; use Intervention\Image\EncodedImage; -use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\Geometry\Circle; use Intervention\Image\Geometry\Ellipse; use Intervention\Image\Geometry\Line; @@ -20,7 +18,6 @@ use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; use Intervention\Image\Traits\CanRunCallback; -use ReflectionProperty; abstract class AbstractImage implements ImageInterface { @@ -30,8 +27,6 @@ abstract class AbstractImage implements ImageInterface protected Collection $exif; - - public function eachFrame(callable $callback): ImageInterface { foreach ($this as $frame) { @@ -364,6 +359,10 @@ public function setExif(array $data): ImageInterface public function getExif(?string $query = null): mixed { + if (!isset($this->exif)) { + return new Collection(); + } + return is_null($query) ? $this->exif : $this->exif->get($query); } From ea79f4b669124759a2a07f020272b834a9253966 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 6 Oct 2023 16:20:50 +0200 Subject: [PATCH 0430/1667] Fix type error --- src/Drivers/Gd/Modifiers/DrawEllipseModifier.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php index c113b91de..8f956c719 100644 --- a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php @@ -26,8 +26,8 @@ public function apply(ImageInterface $image): ImageInterface imagesetthickness($frame->getCore(), $this->ellipse()->getBorderSize()); - // gd's imageellipse doesn't respect imagesetthickness so i use - // imagearc with 359.9 degrees here. + // gd's imageellipse ignores imagesetthickness so i use + // imagearc with 360 degrees instead. imagearc( $frame->getCore(), $this->position->getX(), @@ -35,7 +35,7 @@ public function apply(ImageInterface $image): ImageInterface $this->ellipse()->getWidth(), $this->ellipse()->getHeight(), 0, - 359.99, + 360, $this->getBorderColor()->toInt() ); } else { From 153725e4f7ad4715dab53116de848a0c293d0aa7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 6 Oct 2023 16:27:02 +0200 Subject: [PATCH 0431/1667] Fix bug of Image::setLoops() beiing ignored with imagick --- src/Drivers/Imagick/Image.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 03f2743ab..4dc8ca6a5 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -57,6 +57,7 @@ public function addFrame(FrameInterface $frame): ImageInterface public function setLoops(int $count): ImageInterface { + $this->imagick = $this->imagick->coalesceImages(); $this->imagick->setImageIterations($count); return $this; From ba01e87883b18a8da728db8bcab8f50429315ede Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 7 Oct 2023 09:31:08 +0200 Subject: [PATCH 0432/1667] Rename methods of ImageInterface Rename pickColor() to getColor() and pickColors() to getColors(). --- src/Drivers/Abstract/AbstractImage.php | 4 ++-- src/Drivers/Gd/Image.php | 2 +- src/Drivers/Imagick/Image.php | 2 +- src/Interfaces/ImageInterface.php | 23 ++++++++++++++++--- tests/Drivers/Abstract/AbstractImageTest.php | 6 ++--- tests/Drivers/Gd/ImageTest.php | 14 +++++------ .../Drivers/Gd/Modifiers/BlurModifierTest.php | 4 ++-- .../Gd/Modifiers/BrightnessModifierTest.php | 4 ++-- .../Gd/Modifiers/ContrastModifierTest.php | 4 ++-- .../Gd/Modifiers/DrawEllipseModiferTest.php | 4 ++-- .../Gd/Modifiers/DrawLineModiferTest.php | 4 ++-- .../Gd/Modifiers/DrawPixelModifierTest.php | 4 ++-- .../Gd/Modifiers/DrawPolygonModifierTest.php | 4 ++-- .../Modifiers/DrawRectangleModifierTest.php | 4 ++-- .../Drivers/Gd/Modifiers/FillModifierTest.php | 16 ++++++------- .../Drivers/Gd/Modifiers/FitModifierTest.php | 8 +++---- .../Gd/Modifiers/FlipFlopModifierTest.php | 8 +++---- .../Gd/Modifiers/GammaModifierTest.php | 4 ++-- .../Gd/Modifiers/GreyscaleModifierTest.php | 4 ++-- .../Gd/Modifiers/InvertModifierTest.php | 8 +++---- .../Gd/Modifiers/PixelateModifierTest.php | 8 +++---- .../Gd/Modifiers/PlaceModifierTest.php | 4 ++-- .../Gd/Modifiers/ResizeModifierTest.php | 8 +++---- .../Gd/Modifiers/SharpenModifierTest.php | 4 ++-- .../Imagick/Modifiers/BlurModifierTest.php | 4 ++-- .../Modifiers/BrightnessModifierTest.php | 4 ++-- .../Modifiers/ContrastModifierTest.php | 4 ++-- .../Modifiers/DrawEllipseModifierTest.php | 4 ++-- .../Modifiers/DrawLineModifierTest.php | 4 ++-- .../Modifiers/DrawPixelModifierTest.php | 4 ++-- .../Modifiers/DrawPolygonModifierTest.php | 4 ++-- .../Modifiers/DrawRectangleModifierTest.php | 4 ++-- .../Imagick/Modifiers/FillModifierTest.php | 16 ++++++------- .../Imagick/Modifiers/FitModifierTest.php | 8 +++---- .../Modifiers/FlipFlopModifierTest.php | 8 +++---- .../Imagick/Modifiers/GammaModifierTest.php | 4 ++-- .../Modifiers/GreyscaleModifierTest.php | 4 ++-- .../Imagick/Modifiers/InvertModifierTest.php | 8 +++---- .../Modifiers/PixelateModifierTest.php | 8 +++---- .../Imagick/Modifiers/PlaceModifierTest.php | 4 ++-- .../Imagick/Modifiers/ResizeModifierTest.php | 8 +++---- .../Imagick/Modifiers/SharpenModifierTest.php | 4 ++-- 42 files changed, 138 insertions(+), 121 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3b46865fa..cc3ac4883 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -217,11 +217,11 @@ public function sharpen(int $amount = 10): ImageInterface ); } - public function pickColors(int $x, int $y): CollectionInterface + public function getColors(int $x, int $y): CollectionInterface { $colors = new Collection(); foreach ($this as $key => $frame) { - $colors->push($this->pickColor($x, $y, $key)); + $colors->push($this->getColor($x, $y, $key)); } return $colors; diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index ad9b3754d..52a29f1ca 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -66,7 +66,7 @@ public function getHeight(): int return imagesy($this->getFrame()->getCore()); } - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return new Color(imagecolorat($frame->getCore(), $x, $y)); diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 4dc8ca6a5..0ef864e63 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -121,7 +121,7 @@ public function getHeight(): int return $this->getFrame()->getCore()->getImageHeight(); } - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return new Color($frame->getCore()->getImagePixelColor($x, $y)); diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 45b257db5..6020f963a 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Interfaces; use Countable; -use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Traversable; @@ -138,8 +137,26 @@ public function toAvif(): EncodedImage; */ public function toPng(): EncodedImage; - public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; - public function pickColors(int $x, int $y): CollectionInterface; + /** + * Return color of pixel at the given position of the image + * For animated image pass the key of the animation frame. + * + * @param int $x + * @param int $y + * @param int $frame_key + * @return null|ColorInterface + */ + public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + + /** + * Return a collection with the color of the pixel at the given position + * For animated images all pixel colors for all frames are returned. + * + * @param int $x + * @param int $y + * @return CollectionInterface + */ + public function getColors(int $x, int $y): CollectionInterface; /** * Draw text on image diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index e4f64cd29..54092d3fc 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -297,12 +297,12 @@ public function testSharpen(): void $this->assertInstanceOf(ImageInterface::class, $result); } - public function testPickColors(): void + public function testGetColors(): void { $color = Mockery::mock(ColorInterface::class); $img = $this->abstractImageMock(); - $img->shouldReceive('pickColor')->times(3)->andReturn($color); - $result = $img->pickColors(1, 2); + $img->shouldReceive('getColor')->times(3)->andReturn($color); + $result = $img->getColors(1, 2); $this->assertInstanceOf(Collection::class, $result); } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 927450fd2..07cc42335 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -92,33 +92,33 @@ public function testGetSize(): void $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } - public function testPickColor(): void + public function testGetColor(): void { - $color = $this->image->pickColor(0, 0); + $color = $this->image->getColor(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); + $color = $this->image->getColor(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); + $color = $this->image->getColor(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); + $color = $this->image->getColor(0, 0, 3); $this->assertNull($color); } - public function testPickColors(): void + public function testGetColors(): void { - $colors = $this->image->pickColors(0, 0); + $colors = $this->image->getColors(0, 0); $this->assertInstanceOf(Collection::class, $colors); $this->assertCount(3, $colors); diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php index 5fa2328a1..237868c6b 100644 --- a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -17,8 +17,8 @@ class BlurModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new BlurModifier(30)); - $this->assertEquals('4fa68d', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('4fa68d', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php index 243129789..3879a0054 100644 --- a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -17,8 +17,8 @@ class BrightnessModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new BrightnessModifier(30)); - $this->assertEquals('4cfaff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('4cfaff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php index 66afbf577..74059c1b6 100644 --- a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -17,8 +17,8 @@ class ContrastModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new ContrastModifier(30)); - $this->assertEquals('00ceff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00ceff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php index 1c95cd3c6..a9dfa0bd3 100644 --- a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php @@ -19,10 +19,10 @@ class DrawEllipseModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $drawable = new Ellipse(10, 10); $drawable->background('b53717'); $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); - $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php index e726f3117..392db16f6 100644 --- a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php @@ -19,10 +19,10 @@ class DrawLineModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $line = new Line(new Point(0, 0), new Point(10, 0), 4); $line->background('b53517'); $image->modify(new DrawLineModifier(new Point(0, 0), $line)); - $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b53517', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php index e6ef8f5d0..04c44096d 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php @@ -18,8 +18,8 @@ class DrawPixelModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); - $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php index 1b62a24ae..eaf7663af 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php @@ -19,10 +19,10 @@ class DrawPolygonModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); $drawable->background('b53717'); $image->modify(new DrawPolygonModifier($drawable)); - $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php index 2368c6ee5..f5b2d1b6d 100644 --- a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php @@ -19,10 +19,10 @@ class DrawRectangleModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $rectangle = new Rectangle(300, 200); $rectangle->background('ffffff'); $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); - $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index 380b746a5..f2263ad98 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -19,20 +19,20 @@ class FillModifierTest extends TestCase public function testFloodFillColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(13421772), new Point(540, 400))); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); } public function testFillAllColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(13421772))); - $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('cccccc', $image->getColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index 58bd8094d..b6e02fba1 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); - $this->assertTransparency($image->pickColor(90, 30)); + $this->assertColor(255, 0, 0, 1, $image->getColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->getColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->getColor(70, 52)); + $this->assertTransparency($image->getColor(90, 30)); } } diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php index 0305f6a82..c4a341369 100644 --- a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -19,16 +19,16 @@ class FlipFlopModifierTest extends TestCase public function testFlipImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); } public function testFlopImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php index dec4de93c..96fbb1935 100644 --- a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php @@ -17,8 +17,8 @@ class GammaModifierTest extends TestCase public function testModifier(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); $image->modify(new GammaModifier(2.1)); - $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00d5f8', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php index 6a9b6380d..c11f22254 100644 --- a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -17,8 +17,8 @@ class GreyscaleModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); + $this->assertFalse($image->getColor(0, 0)->isGreyscale()); $image->modify(new GreyscaleModifier()); - $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); + $this->assertTrue($image->getColor(0, 0)->isGreyscale()); } } diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php index 82a81b590..35ab7ae12 100644 --- a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -17,10 +17,10 @@ class InvertModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->getColor(25, 25)->toHex()); $image->modify(new InvertModifier()); - $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); + $this->assertEquals('ff510f', $image->getColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->getColor(25, 25)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php index 427a80802..04dba07b4 100644 --- a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -17,10 +17,10 @@ class PixelateModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('6aaa8b', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('6aaa8b', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php index 3f3db1e08..c3ddd2d21 100644 --- a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -17,8 +17,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->getColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('32250d', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('32250d', $image->getColor(300, 25)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index bf7ef2340..ed2ebf2bf 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -22,10 +22,10 @@ public function testModify(): void $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $transparent = $image->pickColor(170, 30); + $this->assertColor(255, 0, 0, 1, $image->getColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->getColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->getColor(130, 54)); + $transparent = $image->getColor(170, 30); $this->assertEquals(2130706432, $transparent->toInt()); $this->assertTransparency($transparent); } diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php index 5646bae19..5a7c9180a 100644 --- a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -17,8 +17,8 @@ class SharpenModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); + $this->assertEquals('60ab96', $image->getColor(15, 14)->toHex()); $image->modify(new SharpenModifier(10)); - $this->assertEquals('4daba7', $image->pickColor(15, 14)->toHex()); + $this->assertEquals('4daba7', $image->getColor(15, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php index 485b8e513..1d9123309 100644 --- a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -17,8 +17,8 @@ class BlurModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new BlurModifier(30)); - $this->assertEquals('42acb2', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('42acb2', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php index 8cb0b3db1..b1da26773 100644 --- a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -17,8 +17,8 @@ class BrightnessModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new BrightnessModifier(30)); - $this->assertEquals('39c9ff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('39c9ff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php index c5232a4c8..e22a36c03 100644 --- a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -17,8 +17,8 @@ class ContrastModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new ContrastModifier(30)); - $this->assertEquals('00fcff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00fcff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php index a2bed0d8a..b12be356c 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php @@ -15,10 +15,10 @@ class DrawEllipseModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $drawable = new Ellipse(10, 10); $drawable->background('b53717'); $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); - $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php index 423e27dae..9602ab546 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php @@ -15,10 +15,10 @@ class DrawLineModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $line = new Line(new Point(0, 0), new Point(10, 0), 4); $line->background('b53517'); $image->modify(new DrawLineModifier(new Point(0, 0), $line)); - $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b53517', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php index 90c076d72..ac5d03dfc 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php @@ -18,8 +18,8 @@ class DrawPixelModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); - $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php index 02ee6b2be..99796e58c 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php @@ -15,10 +15,10 @@ class DrawPolygonModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); $drawable->background('b53717'); $image->modify(new DrawPolygonModifier($drawable)); - $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php index 876397f81..99928e81a 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php @@ -15,10 +15,10 @@ class DrawRectangleModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $rectangle = new Rectangle(300, 200); $rectangle->background('ffffff'); $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); - $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index 37d9b9d7f..bbb53eade 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -20,20 +20,20 @@ class FillModifierTest extends TestCase public function testFloodFillColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')), new Point(540, 400))); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); } public function testFillAllColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')))); - $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); + $this->assertEquals('cccccc', $image->getColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index 783e8e495..76acee6d9 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); - $this->assertTransparency($image->pickColor(90, 30)); + $this->assertColor(255, 0, 0, 1, $image->getColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->getColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->getColor(70, 52)); + $this->assertTransparency($image->getColor(90, 30)); } } diff --git a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php index 10ecab694..b6957cca8 100644 --- a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php @@ -19,16 +19,16 @@ class FlipFlopModifierTest extends TestCase public function testFlipImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); } public function testFlopImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php index c3af78c02..3e77e9f68 100644 --- a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php @@ -17,8 +17,8 @@ class GammaModifierTest extends TestCase public function testModifier(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); $image->modify(new GammaModifier(2.1)); - $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00d5f8', $image->getColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php index 42257fad0..bd31349bb 100644 --- a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -17,8 +17,8 @@ class GreyscaleModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); + $this->assertFalse($image->getColor(0, 0)->isGreyscale()); $image->modify(new GreyscaleModifier()); - $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); + $this->assertTrue($image->getColor(0, 0)->isGreyscale()); } } diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php index 2c3779cdb..0768d24bb 100644 --- a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -17,10 +17,10 @@ class InvertModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->getColor(25, 25)->toHex()); $image->modify(new InvertModifier()); - $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); + $this->assertEquals('ff510f', $image->getColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->getColor(25, 25)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index 9329b8307..cbaa12392 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -17,10 +17,10 @@ class PixelateModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('6bab8c', $image->pickColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('6bab8c', $image->getColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index 15a3af838..92a22ab2f 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -17,8 +17,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->getColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('33260e', $image->pickColor(300, 25)->toHex()); + $this->assertEquals('33260e', $image->getColor(300, 25)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 519e69f1e..b112cedb8 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $this->assertTransparency($image->pickColor(150, 45)); + $this->assertColor(255, 0, 0, 1, $image->getColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->getColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->getColor(130, 54)); + $this->assertTransparency($image->getColor(150, 45)); } } diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php index 2b80fd4e3..920630937 100644 --- a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -17,8 +17,8 @@ class SharpenModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); + $this->assertEquals('60ab96', $image->getColor(15, 14)->toHex()); $image->modify(new SharpenModifier(10)); - $this->assertEquals('4faca6', $image->pickColor(15, 14)->toHex()); + $this->assertEquals('4faca6', $image->getColor(15, 14)->toHex()); } } From 4ff9abd3a0f7cd66ba91853bebd43ae48f1a8a94 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 7 Oct 2023 09:38:48 +0200 Subject: [PATCH 0433/1667] Add missing method to ColorInterface --- src/Interfaces/ColorInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 07ee8e910..2662258d4 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -11,4 +11,5 @@ public function alpha(): float; public function toArray(): array; public function toHex(string $prefix = ''): string; public function toInt(): int; + public function isGreyscale(): bool; } From d9f4c2440a5a4c302b144ea24534c4602cfd1a99 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 7 Oct 2023 10:25:41 +0200 Subject: [PATCH 0434/1667] Revert "Rename methods of ImageInterface" This reverts commit ba01e87883b18a8da728db8bcab8f50429315ede. --- src/Drivers/Abstract/AbstractImage.php | 4 ++-- src/Drivers/Gd/Image.php | 2 +- src/Drivers/Imagick/Image.php | 2 +- src/Interfaces/ImageInterface.php | 23 +++---------------- tests/Drivers/Abstract/AbstractImageTest.php | 6 ++--- tests/Drivers/Gd/ImageTest.php | 14 +++++------ .../Drivers/Gd/Modifiers/BlurModifierTest.php | 4 ++-- .../Gd/Modifiers/BrightnessModifierTest.php | 4 ++-- .../Gd/Modifiers/ContrastModifierTest.php | 4 ++-- .../Gd/Modifiers/DrawEllipseModiferTest.php | 4 ++-- .../Gd/Modifiers/DrawLineModiferTest.php | 4 ++-- .../Gd/Modifiers/DrawPixelModifierTest.php | 4 ++-- .../Gd/Modifiers/DrawPolygonModifierTest.php | 4 ++-- .../Modifiers/DrawRectangleModifierTest.php | 4 ++-- .../Drivers/Gd/Modifiers/FillModifierTest.php | 16 ++++++------- .../Drivers/Gd/Modifiers/FitModifierTest.php | 8 +++---- .../Gd/Modifiers/FlipFlopModifierTest.php | 8 +++---- .../Gd/Modifiers/GammaModifierTest.php | 4 ++-- .../Gd/Modifiers/GreyscaleModifierTest.php | 4 ++-- .../Gd/Modifiers/InvertModifierTest.php | 8 +++---- .../Gd/Modifiers/PixelateModifierTest.php | 8 +++---- .../Gd/Modifiers/PlaceModifierTest.php | 4 ++-- .../Gd/Modifiers/ResizeModifierTest.php | 8 +++---- .../Gd/Modifiers/SharpenModifierTest.php | 4 ++-- .../Imagick/Modifiers/BlurModifierTest.php | 4 ++-- .../Modifiers/BrightnessModifierTest.php | 4 ++-- .../Modifiers/ContrastModifierTest.php | 4 ++-- .../Modifiers/DrawEllipseModifierTest.php | 4 ++-- .../Modifiers/DrawLineModifierTest.php | 4 ++-- .../Modifiers/DrawPixelModifierTest.php | 4 ++-- .../Modifiers/DrawPolygonModifierTest.php | 4 ++-- .../Modifiers/DrawRectangleModifierTest.php | 4 ++-- .../Imagick/Modifiers/FillModifierTest.php | 16 ++++++------- .../Imagick/Modifiers/FitModifierTest.php | 8 +++---- .../Modifiers/FlipFlopModifierTest.php | 8 +++---- .../Imagick/Modifiers/GammaModifierTest.php | 4 ++-- .../Modifiers/GreyscaleModifierTest.php | 4 ++-- .../Imagick/Modifiers/InvertModifierTest.php | 8 +++---- .../Modifiers/PixelateModifierTest.php | 8 +++---- .../Imagick/Modifiers/PlaceModifierTest.php | 4 ++-- .../Imagick/Modifiers/ResizeModifierTest.php | 8 +++---- .../Imagick/Modifiers/SharpenModifierTest.php | 4 ++-- 42 files changed, 121 insertions(+), 138 deletions(-) diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index cc3ac4883..3b46865fa 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -217,11 +217,11 @@ public function sharpen(int $amount = 10): ImageInterface ); } - public function getColors(int $x, int $y): CollectionInterface + public function pickColors(int $x, int $y): CollectionInterface { $colors = new Collection(); foreach ($this as $key => $frame) { - $colors->push($this->getColor($x, $y, $key)); + $colors->push($this->pickColor($x, $y, $key)); } return $colors; diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 52a29f1ca..ad9b3754d 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -66,7 +66,7 @@ public function getHeight(): int return imagesy($this->getFrame()->getCore()); } - public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + 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)); diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 0ef864e63..4dc8ca6a5 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -121,7 +121,7 @@ public function getHeight(): int return $this->getFrame()->getCore()->getImageHeight(); } - public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + 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)); diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 6020f963a..45b257db5 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Interfaces; use Countable; +use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Traversable; @@ -137,26 +138,8 @@ public function toAvif(): EncodedImage; */ public function toPng(): EncodedImage; - /** - * Return color of pixel at the given position of the image - * For animated image pass the key of the animation frame. - * - * @param int $x - * @param int $y - * @param int $frame_key - * @return null|ColorInterface - */ - public function getColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; - - /** - * Return a collection with the color of the pixel at the given position - * For animated images all pixel colors for all frames are returned. - * - * @param int $x - * @param int $y - * @return CollectionInterface - */ - public function getColors(int $x, int $y): CollectionInterface; + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + public function pickColors(int $x, int $y): CollectionInterface; /** * Draw text on image diff --git a/tests/Drivers/Abstract/AbstractImageTest.php b/tests/Drivers/Abstract/AbstractImageTest.php index 54092d3fc..e4f64cd29 100644 --- a/tests/Drivers/Abstract/AbstractImageTest.php +++ b/tests/Drivers/Abstract/AbstractImageTest.php @@ -297,12 +297,12 @@ public function testSharpen(): void $this->assertInstanceOf(ImageInterface::class, $result); } - public function testGetColors(): void + public function testPickColors(): void { $color = Mockery::mock(ColorInterface::class); $img = $this->abstractImageMock(); - $img->shouldReceive('getColor')->times(3)->andReturn($color); - $result = $img->getColors(1, 2); + $img->shouldReceive('pickColor')->times(3)->andReturn($color); + $result = $img->pickColors(1, 2); $this->assertInstanceOf(Collection::class, $result); } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 07cc42335..927450fd2 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -92,33 +92,33 @@ public function testGetSize(): void $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } - public function testGetColor(): void + public function testPickColor(): void { - $color = $this->image->getColor(0, 0); + $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->getColor(0, 0, 1); + $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->getColor(0, 0, 2); + $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->getColor(0, 0, 3); + $color = $this->image->pickColor(0, 0, 3); $this->assertNull($color); } - public function testGetColors(): void + public function testPickColors(): void { - $colors = $this->image->getColors(0, 0); + $colors = $this->image->pickColors(0, 0); $this->assertInstanceOf(Collection::class, $colors); $this->assertCount(3, $colors); diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php index 237868c6b..5fa2328a1 100644 --- a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -17,8 +17,8 @@ class BlurModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new BlurModifier(30)); - $this->assertEquals('4fa68d', $image->getColor(14, 14)->toHex()); + $this->assertEquals('4fa68d', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php index 3879a0054..243129789 100644 --- a/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/BrightnessModifierTest.php @@ -17,8 +17,8 @@ class BrightnessModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new BrightnessModifier(30)); - $this->assertEquals('4cfaff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('4cfaff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php index 74059c1b6..66afbf577 100644 --- a/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContrastModifierTest.php @@ -17,8 +17,8 @@ class ContrastModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new ContrastModifier(30)); - $this->assertEquals('00ceff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00ceff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php index a9dfa0bd3..1c95cd3c6 100644 --- a/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawEllipseModiferTest.php @@ -19,10 +19,10 @@ class DrawEllipseModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $drawable = new Ellipse(10, 10); $drawable->background('b53717'); $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); - $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php index 392db16f6..e726f3117 100644 --- a/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawLineModiferTest.php @@ -19,10 +19,10 @@ class DrawLineModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $line = new Line(new Point(0, 0), new Point(10, 0), 4); $line->background('b53517'); $image->modify(new DrawLineModifier(new Point(0, 0), $line)); - $this->assertEquals('b53517', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php index 04c44096d..e6ef8f5d0 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPixelModifierTest.php @@ -18,8 +18,8 @@ class DrawPixelModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); - $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php index eaf7663af..1b62a24ae 100644 --- a/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawPolygonModifierTest.php @@ -19,10 +19,10 @@ class DrawPolygonModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); $drawable->background('b53717'); $image->modify(new DrawPolygonModifier($drawable)); - $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php index f5b2d1b6d..2368c6ee5 100644 --- a/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/DrawRectangleModifierTest.php @@ -19,10 +19,10 @@ class DrawRectangleModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $rectangle = new Rectangle(300, 200); $rectangle->background('ffffff'); $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); - $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index f2263ad98..380b746a5 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -19,20 +19,20 @@ class FillModifierTest extends TestCase public function testFloodFillColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(13421772), new Point(540, 400))); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } public function testFillAllColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(13421772))); - $this->assertEquals('cccccc', $image->getColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index b6e02fba1..58bd8094d 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->getColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->getColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->getColor(70, 52)); - $this->assertTransparency($image->getColor(90, 30)); + $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertTransparency($image->pickColor(90, 30)); } } diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php index c4a341369..0305f6a82 100644 --- a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -19,16 +19,16 @@ class FlipFlopModifierTest extends TestCase public function testFlipImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); } public function testFlopImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php index 96fbb1935..dec4de93c 100644 --- a/tests/Drivers/Gd/Modifiers/GammaModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GammaModifierTest.php @@ -17,8 +17,8 @@ class GammaModifierTest extends TestCase public function testModifier(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); $image->modify(new GammaModifier(2.1)); - $this->assertEquals('00d5f8', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php index c11f22254..6a9b6380d 100644 --- a/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/GreyscaleModifierTest.php @@ -17,8 +17,8 @@ class GreyscaleModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertFalse($image->getColor(0, 0)->isGreyscale()); + $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); $image->modify(new GreyscaleModifier()); - $this->assertTrue($image->getColor(0, 0)->isGreyscale()); + $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); } } diff --git a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php index 35ab7ae12..82a81b590 100644 --- a/tests/Drivers/Gd/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/InvertModifierTest.php @@ -17,10 +17,10 @@ class InvertModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('ffa601', $image->getColor(25, 25)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); $image->modify(new InvertModifier()); - $this->assertEquals('ff510f', $image->getColor(0, 0)->toHex()); - $this->assertEquals('0059fe', $image->getColor(25, 25)->toHex()); + $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php index 04dba07b4..427a80802 100644 --- a/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PixelateModifierTest.php @@ -17,10 +17,10 @@ class PixelateModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('6aaa8b', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('6aaa8b', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php index c3ddd2d21..3f3db1e08 100644 --- a/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/PlaceModifierTest.php @@ -17,8 +17,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('febc44', $image->getColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('32250d', $image->getColor(300, 25)->toHex()); + $this->assertEquals('32250d', $image->pickColor(300, 25)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index ed2ebf2bf..bf7ef2340 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -22,10 +22,10 @@ public function testModify(): void $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->getColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->getColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->getColor(130, 54)); - $transparent = $image->getColor(170, 30); + $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); + $transparent = $image->pickColor(170, 30); $this->assertEquals(2130706432, $transparent->toInt()); $this->assertTransparency($transparent); } diff --git a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php index 5a7c9180a..5646bae19 100644 --- a/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/SharpenModifierTest.php @@ -17,8 +17,8 @@ class SharpenModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('60ab96', $image->getColor(15, 14)->toHex()); + $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); $image->modify(new SharpenModifier(10)); - $this->assertEquals('4daba7', $image->getColor(15, 14)->toHex()); + $this->assertEquals('4daba7', $image->pickColor(15, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php index 1d9123309..485b8e513 100644 --- a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -17,8 +17,8 @@ class BlurModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new BlurModifier(30)); - $this->assertEquals('42acb2', $image->getColor(14, 14)->toHex()); + $this->assertEquals('42acb2', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php index b1da26773..8cb0b3db1 100644 --- a/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/BrightnessModifierTest.php @@ -17,8 +17,8 @@ class BrightnessModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new BrightnessModifier(30)); - $this->assertEquals('39c9ff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('39c9ff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php index e22a36c03..c5232a4c8 100644 --- a/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ContrastModifierTest.php @@ -17,8 +17,8 @@ class ContrastModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new ContrastModifier(30)); - $this->assertEquals('00fcff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00fcff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php index b12be356c..a2bed0d8a 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawEllipseModifierTest.php @@ -15,10 +15,10 @@ class DrawEllipseModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $drawable = new Ellipse(10, 10); $drawable->background('b53717'); $image->modify(new DrawEllipseModifier(new Point(14, 14), $drawable)); - $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php index 9602ab546..423e27dae 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawLineModifierTest.php @@ -15,10 +15,10 @@ class DrawLineModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $line = new Line(new Point(0, 0), new Point(10, 0), 4); $line->background('b53517'); $image->modify(new DrawLineModifier(new Point(0, 0), $line)); - $this->assertEquals('b53517', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php index ac5d03dfc..90c076d72 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPixelModifierTest.php @@ -18,8 +18,8 @@ class DrawPixelModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new DrawPixelModifier(new Point(14, 14), 'ffffff')); - $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php index 99796e58c..02ee6b2be 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawPolygonModifierTest.php @@ -15,10 +15,10 @@ class DrawPolygonModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $drawable = new Polygon([new Point(0, 0), new Point(15, 15), new Point(20, 20)]); $drawable->background('b53717'); $image->modify(new DrawPolygonModifier($drawable)); - $this->assertEquals('b53717', $image->getColor(14, 14)->toHex()); + $this->assertEquals('b53717', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php index 99928e81a..876397f81 100644 --- a/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/DrawRectangleModifierTest.php @@ -15,10 +15,10 @@ class DrawRectangleModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $rectangle = new Rectangle(300, 200); $rectangle->background('ffffff'); $image->modify(new DrawRectangleModifier(new Point(14, 14), $rectangle)); - $this->assertEquals('ffffff', $image->getColor(14, 14)->toHex()); + $this->assertEquals('ffffff', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index bbb53eade..37d9b9d7f 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -20,20 +20,20 @@ class FillModifierTest extends TestCase public function testFloodFillColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')), new Point(540, 400))); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } public function testFillAllColor(): void { $image = $this->createTestImage('blocks.png'); - $this->assertEquals('0000ff', $image->getColor(420, 270)->toHex()); - $this->assertEquals('ff0000', $image->getColor(540, 400)->toHex()); + $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')))); - $this->assertEquals('cccccc', $image->getColor(420, 270)->toHex()); - $this->assertEquals('cccccc', $image->getColor(540, 400)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); + $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index 76acee6d9..783e8e495 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->getColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->getColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->getColor(70, 52)); - $this->assertTransparency($image->getColor(90, 30)); + $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertTransparency($image->pickColor(90, 30)); } } diff --git a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php index b6957cca8..10ecab694 100644 --- a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php @@ -19,16 +19,16 @@ class FlipFlopModifierTest extends TestCase public function testFlipImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); } public function testFlopImage(): void { $image = $this->createTestImage('tile.png'); - $this->assertEquals('b4e000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->getColor(0, 0)->toHex()); + $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php index 3e77e9f68..c3af78c02 100644 --- a/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GammaModifierTest.php @@ -17,8 +17,8 @@ class GammaModifierTest extends TestCase public function testModifier(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); $image->modify(new GammaModifier(2.1)); - $this->assertEquals('00d5f8', $image->getColor(0, 0)->toHex()); + $this->assertEquals('00d5f8', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php index bd31349bb..42257fad0 100644 --- a/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/GreyscaleModifierTest.php @@ -17,8 +17,8 @@ class GreyscaleModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('trim.png'); - $this->assertFalse($image->getColor(0, 0)->isGreyscale()); + $this->assertFalse($image->pickColor(0, 0)->isGreyscale()); $image->modify(new GreyscaleModifier()); - $this->assertTrue($image->getColor(0, 0)->isGreyscale()); + $this->assertTrue($image->pickColor(0, 0)->isGreyscale()); } } diff --git a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php index 0768d24bb..2c3779cdb 100644 --- a/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/InvertModifierTest.php @@ -17,10 +17,10 @@ class InvertModifierTest extends TestCase public function testApply(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('ffa601', $image->getColor(25, 25)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('ffa601', $image->pickColor(25, 25)->toHex()); $image->modify(new InvertModifier()); - $this->assertEquals('ff510f', $image->getColor(0, 0)->toHex()); - $this->assertEquals('0059fe', $image->getColor(25, 25)->toHex()); + $this->assertEquals('ff510f', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('0059fe', $image->pickColor(25, 25)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index cbaa12392..9329b8307 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -17,10 +17,10 @@ class PixelateModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('00aef0', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->getColor(0, 0)->toHex()); - $this->assertEquals('6bab8c', $image->getColor(14, 14)->toHex()); + $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('6bab8c', $image->pickColor(14, 14)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php index 92a22ab2f..15a3af838 100644 --- a/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PlaceModifierTest.php @@ -17,8 +17,8 @@ class PlaceModifierTest extends TestCase public function testColorChange(): void { $image = $this->createTestImage('test.jpg'); - $this->assertEquals('febc44', $image->getColor(300, 25)->toHex()); + $this->assertEquals('febc44', $image->pickColor(300, 25)->toHex()); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); - $this->assertEquals('33260e', $image->getColor(300, 25)->toHex()); + $this->assertEquals('33260e', $image->pickColor(300, 25)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index b112cedb8..519e69f1e 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->getColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->getColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->getColor(130, 54)); - $this->assertTransparency($image->getColor(150, 45)); + $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); + $this->assertTransparency($image->pickColor(150, 45)); } } diff --git a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php index 920630937..2b80fd4e3 100644 --- a/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/SharpenModifierTest.php @@ -17,8 +17,8 @@ class SharpenModifierTest extends TestCase public function testModify(): void { $image = $this->createTestImage('trim.png'); - $this->assertEquals('60ab96', $image->getColor(15, 14)->toHex()); + $this->assertEquals('60ab96', $image->pickColor(15, 14)->toHex()); $image->modify(new SharpenModifier(10)); - $this->assertEquals('4faca6', $image->getColor(15, 14)->toHex()); + $this->assertEquals('4faca6', $image->pickColor(15, 14)->toHex()); } } From d71448e6bf1f5c391a8b1c6026a9f38cd857a3d7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 7 Oct 2023 16:08:02 +0200 Subject: [PATCH 0435/1667] Rename class --- src/Drivers/Gd/Decoders/HexColorDecoder.php | 2 +- .../{ArrayColorDecoder.php => RgbArrayColorDecoder.php} | 2 +- src/Drivers/Gd/Decoders/RgbStringColorDecoder.php | 2 +- src/Drivers/Gd/Decoders/TransparentColorDecoder.php | 2 +- src/Drivers/Gd/InputHandler.php | 2 +- src/Drivers/Imagick/Decoders/HexColorDecoder.php | 2 +- .../{ArrayColorDecoder.php => RgbArrayColorDecoder.php} | 2 +- src/Drivers/Imagick/Decoders/TransparentColorDecoder.php | 2 +- src/Drivers/Imagick/InputHandler.php | 2 +- ...yColorDecoderTest.php => RgbArrayColorDecoderTest.php} | 8 ++++---- ...yColorDecoderTest.php => RgbArrayColorDecoderTest.php} | 8 ++++---- 11 files changed, 17 insertions(+), 17 deletions(-) rename src/Drivers/Gd/Decoders/{ArrayColorDecoder.php => RgbArrayColorDecoder.php} (92%) rename src/Drivers/Imagick/Decoders/{ArrayColorDecoder.php => RgbArrayColorDecoder.php} (92%) rename tests/Drivers/Gd/Decoders/{ArrayColorDecoderTest.php => RgbArrayColorDecoderTest.php} (69%) rename tests/Drivers/Imagick/Decoders/{ArrayColorDecoderTest.php => RgbArrayColorDecoderTest.php} (69%) diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php index 17134634d..8cb6b27b5 100644 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface +class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php b/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php similarity index 92% rename from src/Drivers/Gd/Decoders/ArrayColorDecoder.php rename to src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php index 3d694e49c..8b25f691f 100644 --- a/src/Drivers/Gd/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php @@ -10,7 +10,7 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanValidateColors; -class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface +class RgbArrayColorDecoder extends AbstractDecoder implements DecoderInterface { use CanValidateColors; diff --git a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php index 300dd4a1b..03c46b960 100644 --- a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class RgbStringColorDecoder extends ArrayColorDecoder implements DecoderInterface +class RgbStringColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php index 64f4adfcd..5cdd3d029 100644 --- a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Gd/Decoders/TransparentColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class TransparentColorDecoder extends ArrayColorDecoder implements DecoderInterface +class TransparentColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index c100808c7..9fec013ba 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -9,7 +9,7 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ Decoders\ImageObjectDecoder::class, Decoders\FilePointerImageDecoder::class, - Decoders\ArrayColorDecoder::class, + Decoders\RgbArrayColorDecoder::class, Decoders\HtmlColorNameDecoder::class, Decoders\RgbStringColorDecoder::class, Decoders\HexColorDecoder::class, diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php index 84526d162..37498320b 100644 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class HexColorDecoder extends ArrayColorDecoder implements DecoderInterface +class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php similarity index 92% rename from src/Drivers/Imagick/Decoders/ArrayColorDecoder.php rename to src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php index 12f8aaa22..2e924c87e 100644 --- a/src/Drivers/Imagick/Decoders/ArrayColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php @@ -11,7 +11,7 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanValidateColors; -class ArrayColorDecoder extends AbstractDecoder implements DecoderInterface +class RgbArrayColorDecoder extends AbstractDecoder implements DecoderInterface { use CanValidateColors; diff --git a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php index b93ef7063..097a35ff4 100644 --- a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php @@ -7,7 +7,7 @@ use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class TransparentColorDecoder extends ArrayColorDecoder implements DecoderInterface +class TransparentColorDecoder extends RgbArrayColorDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 3c751a297..8b00c35e8 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -9,7 +9,7 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ Decoders\ImageObjectDecoder::class, Decoders\FilePointerImageDecoder::class, - Decoders\ArrayColorDecoder::class, + Decoders\RgbArrayColorDecoder::class, Decoders\HexColorDecoder::class, Decoders\HtmlColorNameDecoder::class, Decoders\RgbStringColorDecoder::class, diff --git a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php similarity index 69% rename from tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php rename to tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php index d13920ea4..524b2f630 100644 --- a/tests/Drivers/Gd/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php @@ -3,18 +3,18 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; use Intervention\Image\Drivers\Gd\Color; -use Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder; +use Intervention\Image\Drivers\Gd\Decoders\RgbArrayColorDecoder; use Intervention\Image\Tests\TestCase; /** * @requires extension gd - * @covers \Intervention\Image\Drivers\Gd\Decoders\ArrayColorDecoder + * @covers \Intervention\Image\Drivers\Gd\Decoders\RgbArrayColorDecoder */ -class ArrayColorDecoderTest extends TestCase +class RgbArrayColorDecoderTest extends TestCase { public function testDecode(): void { - $decoder = new ArrayColorDecoder(); + $decoder = new RgbArrayColorDecoder(); $color = $decoder->decode([181, 55, 23, .5]); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(181, $color->red()); diff --git a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php similarity index 69% rename from tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php rename to tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php index 41301b0af..fbb5b4e0b 100644 --- a/tests/Drivers/Imagick/Decoders/ArrayColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php @@ -3,18 +3,18 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Decoders; use Intervention\Image\Drivers\Imagick\Color; -use Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\RgbArrayColorDecoder; use Intervention\Image\Tests\TestCase; /** * @requires extension imagick - * @covers \Intervention\Image\Drivers\Imagick\Decoders\ArrayColorDecoder + * @covers \Intervention\Image\Drivers\Imagick\Decoders\RgbArrayColorDecoder */ -class ArrayColorDecoderTest extends TestCase +class RgbArrayColorDecoderTest extends TestCase { public function testDecode(): void { - $decoder = new ArrayColorDecoder(); + $decoder = new RgbArrayColorDecoder(); $color = $decoder->decode([181, 55, 23, .5]); $this->assertInstanceOf(Color::class, $color); $this->assertEquals(181, $color->red()); From e72b0ff6df6e596c41eb3f71f31a85f13c904082 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 9 Oct 2023 16:30:03 +0200 Subject: [PATCH 0436/1667] Refactor color management --- src/Colors/Cmyk/Channels/Cyan.php | 45 +++++++++ src/Colors/Cmyk/Channels/Key.php | 8 ++ src/Colors/Cmyk/Channels/Magenta.php | 8 ++ src/Colors/Cmyk/Channels/Yellow.php | 8 ++ src/Colors/Cmyk/Color.php | 97 +++++++++++++++++++ src/Colors/Cmyk/Colorspace.php | 33 +++++++ src/Colors/Rgb/Channels/Blue.php | 8 ++ src/Colors/Rgb/Channels/Green.php | 8 ++ src/Colors/Rgb/Channels/Red.php | 45 +++++++++ src/Colors/Rgb/Color.php | 115 +++++++++++++++++++++++ src/Colors/Rgb/Colorspace.php | 27 ++++++ src/Exceptions/ColorException.php | 8 ++ src/Interfaces/ColorChannelInterface.php | 12 +++ src/Interfaces/ColorInterface.php | 20 ++-- src/Interfaces/ColorspaceInterface.php | 8 ++ tests/Colors/Rgb/ColorTest.php | 106 +++++++++++++++++++++ 16 files changed, 548 insertions(+), 8 deletions(-) create mode 100644 src/Colors/Cmyk/Channels/Cyan.php create mode 100644 src/Colors/Cmyk/Channels/Key.php create mode 100644 src/Colors/Cmyk/Channels/Magenta.php create mode 100644 src/Colors/Cmyk/Channels/Yellow.php create mode 100644 src/Colors/Cmyk/Color.php create mode 100644 src/Colors/Cmyk/Colorspace.php create mode 100644 src/Colors/Rgb/Channels/Blue.php create mode 100644 src/Colors/Rgb/Channels/Green.php create mode 100644 src/Colors/Rgb/Channels/Red.php create mode 100644 src/Colors/Rgb/Color.php create mode 100644 src/Colors/Rgb/Colorspace.php create mode 100644 src/Exceptions/ColorException.php create mode 100644 src/Interfaces/ColorChannelInterface.php create mode 100644 src/Interfaces/ColorspaceInterface.php create mode 100644 tests/Colors/Rgb/ColorTest.php diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php new file mode 100644 index 000000000..cb40daac0 --- /dev/null +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -0,0 +1,45 @@ +value = $this->validate($value); + } + + public function value(): int + { + return $this->value; + } + + public function normalize($precision = 32): float + { + return round($this->value() / $this->max(), $precision); + } + + public function min(): int + { + return 0; + } + + public function max(): int + { + return 100; + } + + public function validate(mixed $value): mixed + { + if ($value < $this->min() || $value > $this->max()) { + throw new ColorException('CMYK color values must be in range 0-100.'); + } + + return $value; + } +} diff --git a/src/Colors/Cmyk/Channels/Key.php b/src/Colors/Cmyk/Channels/Key.php new file mode 100644 index 000000000..2893d2afb --- /dev/null +++ b/src/Colors/Cmyk/Channels/Key.php @@ -0,0 +1,8 @@ +cyan = new Cyan($c); + $this->magenta = new Magenta($m); + $this->yellow = new Yellow($y); + $this->key = new Key($k); + } + + public function channels(): array + { + return $this->channels; + } + + public function cyan(): Cyan + { + return $this->cyan; + } + + public function magenta(): Magenta + { + return $this->magenta; + } + + public function yellow(): Yellow + { + return $this->yellow; + } + + public function key(): Key + { + return $this->key; + } + + public function toArray(): array + { + return [ + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + $this->key()->value(), + ]; + } + + public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface + { + $colorspace = match (true) { + is_object($colorspace) => $colorspace, + default => new $colorspace(), + }; + + return $colorspace->transformColor($this); + } + + public function toRgb(): RgbColor + { + return $this->transformTo(RgbColorspace::class); + } + + public function toCmyk(): self + { + return $this->transformTo(CmykColorspace::class); + } + + public function __toString(): string + { + return sprintf( + 'cmyk(%d, %d, %d, %d)', + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + $this->key()->value() + ); + } +} diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php new file mode 100644 index 000000000..968bd023d --- /dev/null +++ b/src/Colors/Cmyk/Colorspace.php @@ -0,0 +1,33 @@ + $this->convertRgbColor($color), + default => $color, + }; + } + + protected function convertRgbColor(RgbColor $color): CmykColor + { + $c = (255 - $color->red()->value()) / 255.0 * 100; + $m = (255 - $color->green()->value()) / 255.0 * 100; + $y = (255 - $color->blue()->value()) / 255.0 * 100; + $k = intval(round(min([$c, $m, $y]))); + + $c = intval(round($c - $k)); + $m = intval(round($m - $k)); + $y = intval(round($y - $k)); + + return new CmykColor($c, $m, $y, $k); + } +} diff --git a/src/Colors/Rgb/Channels/Blue.php b/src/Colors/Rgb/Channels/Blue.php new file mode 100644 index 000000000..b83be821e --- /dev/null +++ b/src/Colors/Rgb/Channels/Blue.php @@ -0,0 +1,8 @@ +value = $this->validate($value); + } + + public function value(): int + { + return $this->value; + } + + public function normalize($precision = 32): float + { + return round($this->value() / $this->max(), $precision); + } + + public function min(): int + { + return 0; + } + + public function max(): int + { + return 255; + } + + public function validate(mixed $value): mixed + { + if ($value < $this->min() || $value > $this->max()) { + throw new ColorException('RGB color values must be in range 0-255.'); + } + + return $value; + } +} diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php new file mode 100644 index 000000000..18be0fc2c --- /dev/null +++ b/src/Colors/Rgb/Color.php @@ -0,0 +1,115 @@ +channels = [ + new Red($r), + new Green($g), + new Blue($b), + ]; + } + + public function channels(): array + { + return $this->channels; + } + + public function channel(string $classname): ColorChannelInterface + { + $channels = array_filter($this->channels(), function (ColorChannelInterface $channel) use ($classname) { + return is_a($channel, $classname); + }); + + return reset($channels); + } + + public function red(): Red + { + return $this->channel(Red::class); + } + + public function green(): Green + { + return $this->channel(Green::class); + } + + public function blue(): Blue + { + return $this->channel(Blue::class); + } + + public function toArray(): array + { + return array_map(function (ColorChannelInterface $channel) { + return $channel->value(); + }, $this->channels()); + } + + public function normalize(): array + { + return array_map(function (ColorChannelInterface $channel) { + return $channel->normalize(); + }, $this->channels()); + } + + public function toHex(string $prefix = ''): string + { + return sprintf( + '%s%02x%02x%02x', + $prefix, + $this->red()->value(), + $this->green()->value(), + $this->blue()->value() + ); + } + + public function toRgb(): Color + { + return $this; + } + + public function toInt(): int + { + return $this->red()->value() * 256 * 256 + $this->green()->value() * 256 + $this->blue()->value(); + } + + public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface + { + $colorspace = match (true) { + is_object($colorspace) => $colorspace, + default => new $colorspace(), + }; + + return $colorspace->transformColor($this); + } + + public function toCmyk(): CmykColor + { + return $this->transformTo(CmykColorspace::class); + } + + public function __toString(): string + { + return sprintf( + 'rgb(%d, %d, %d)', + $this->red()->value(), + $this->green()->value(), + $this->blue()->value() + ); + } +} diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php new file mode 100644 index 000000000..fd28b1e92 --- /dev/null +++ b/src/Colors/Rgb/Colorspace.php @@ -0,0 +1,27 @@ + $this->convertCmykColor($color), + default => $color, + }; + } + + protected function convertCmykColor(CmykColor $color): Color + { + return new Color( + (int) (255 * (1 - $color->cyan()->value()) * (1 - $color->key()->value())), + (int) (255 * (1 - $color->magenta()->value()) * (1 - $color->key()->value())), + (int) (255 * (1 - $color->yellow()->value()) * (1 - $color->key()->value())), + ); + } +} diff --git a/src/Exceptions/ColorException.php b/src/Exceptions/ColorException.php new file mode 100644 index 000000000..4eda93229 --- /dev/null +++ b/src/Exceptions/ColorException.php @@ -0,0 +1,8 @@ +assertInstanceOf(Color::class, $color); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30); + $this->assertIsArray($color->channels()); + $this->assertCount(3, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30); + $channel = $color->channel(Red::class); + $this->assertInstanceOf(Red::class, $channel); + $this->assertEquals(10, $channel->value()); + } + + public function testRedGreenBlue(): void + { + $color = new Color(10, 20, 30); + $this->assertInstanceOf(Red::class, $color->red()); + $this->assertInstanceOf(Green::class, $color->green()); + $this->assertInstanceOf(Blue::class, $color->blue()); + $this->assertEquals(10, $color->red()->value()); + $this->assertEquals(20, $color->green()->value()); + $this->assertEquals(30, $color->blue()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30); + $this->assertEquals([10, 20, 30], $color->toArray()); + } + + public function testToHex(): void + { + $color = new Color(181, 55, 23); + $this->assertEquals('b53717', $color->toHex()); + $this->assertEquals('#b53717', $color->toHex('#')); + } + + public function testNormalize(): void + { + $color = new Color(255, 0, 51); + $this->assertEquals([1.0, 0.0, 0.2], $color->normalize()); + } + + public function testToInt(): void + { + $color = new Color(0, 0, 0); + $this->assertEquals(0, $color->toInt()); + + $color = new Color(255, 255, 255); + $this->assertEquals(16777215, $color->toInt()); + + $color = new Color(181, 55, 23); + $this->assertEquals(11876119, $color->toInt()); + } + + public function testToString(): void + { + $color = new Color(181, 55, 23); + $this->assertEquals('rgb(181, 55, 23)', (string) $color); + } + + public function testToCmyk(): void + { + $color = new Color(0, 0, 0); + $this->assertEquals([0, 0, 0, 100], $color->toCmyk()->toArray()); + + $color = new Color(255, 255, 255); + $this->assertEquals([0, 0, 0, 0], $color->toCmyk()->toArray()); + + $color = new Color(255, 0, 0); + $this->assertEquals([0, 100, 100, 0], $color->toCmyk()->toArray()); + + $color = new Color(255, 0, 255); + $this->assertEquals([0, 100, 0, 0], $color->toCmyk()->toArray()); + + $color = new Color(255, 255, 0); + $this->assertEquals([0, 0, 100, 0], $color->toCmyk()->toArray()); + + $color = new Color(255, 204, 204); + $this->assertEquals([0, 20, 20, 0], $color->toCmyk()->toArray()); + } +} From 929c7545bb1e143534ba79b372985bca49f99db2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 14 Oct 2023 14:33:12 +0200 Subject: [PATCH 0437/1667] Add color tests --- src/Colors/Cmyk/Color.php | 41 ++++---- src/Colors/Cmyk/Colorspace.php | 10 +- src/Colors/Rgb/Color.php | 44 +++------ src/Colors/Rgb/Colorspace.php | 21 ++++- src/Colors/Rgba/Channels/Alpha.php | 8 ++ src/Colors/Rgba/Channels/Blue.php | 10 ++ src/Colors/Rgba/Channels/Green.php | 10 ++ src/Colors/Rgba/Channels/Red.php | 10 ++ src/Colors/Rgba/Color.php | 94 +++++++++++++++++++ src/Colors/Rgba/Colorspace.php | 55 +++++++++++ src/Colors/Traits/CanHandleChannels.php | 34 +++++++ src/Interfaces/ColorInterface.php | 14 +-- src/Interfaces/ColorspaceInterface.php | 8 +- tests/Colors/Cmyk/ColorTest.php | 94 +++++++++++++++++++ tests/Colors/Cmyk/ColorspaceTest.php | 26 ++++++ tests/Colors/Rgb/ColorTest.php | 52 +++++++---- tests/Colors/Rgb/ColorspaceTest.php | 26 ++++++ tests/Colors/Rgba/ColorTest.php | 119 ++++++++++++++++++++++++ tests/Colors/Rgba/ColorspaceTest.php | 26 ++++++ 19 files changed, 623 insertions(+), 79 deletions(-) create mode 100644 src/Colors/Rgba/Channels/Alpha.php create mode 100644 src/Colors/Rgba/Channels/Blue.php create mode 100644 src/Colors/Rgba/Channels/Green.php create mode 100644 src/Colors/Rgba/Channels/Red.php create mode 100644 src/Colors/Rgba/Color.php create mode 100644 src/Colors/Rgba/Colorspace.php create mode 100644 src/Colors/Traits/CanHandleChannels.php create mode 100644 tests/Colors/Cmyk/ColorTest.php create mode 100644 tests/Colors/Cmyk/ColorspaceTest.php create mode 100644 tests/Colors/Rgb/ColorspaceTest.php create mode 100644 tests/Colors/Rgba/ColorTest.php create mode 100644 tests/Colors/Rgba/ColorspaceTest.php diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index 63b90b6d8..26b423ea1 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -9,24 +9,26 @@ use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; +use Intervention\Image\Colors\Rgba\Colorspace as RgbaColorspace; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; +use Intervention\Image\Colors\Traits\CanHandleChannels; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; class Color implements ColorInterface { - protected array $channels; + use CanHandleChannels; - protected Cyan $cyan; - protected Magenta $magenta; - protected Yellow $yellow; - protected Key $key; + protected array $channels; public function __construct(int $c, int $m, int $y, int $k) { - $this->cyan = new Cyan($c); - $this->magenta = new Magenta($m); - $this->yellow = new Yellow($y); - $this->key = new Key($k); + $this->channels = [ + new Cyan($c), + new Magenta($m), + new Yellow($y), + new Key($k), + ]; } public function channels(): array @@ -36,22 +38,22 @@ public function channels(): array public function cyan(): Cyan { - return $this->cyan; + return $this->channel(Cyan::class); } public function magenta(): Magenta { - return $this->magenta; + return $this->channel(Magenta::class); } public function yellow(): Yellow { - return $this->yellow; + return $this->channel(Yellow::class); } public function key(): Key { - return $this->key; + return $this->channel(Key::class); } public function toArray(): array @@ -64,24 +66,29 @@ public function toArray(): array ]; } - public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface { $colorspace = match (true) { is_object($colorspace) => $colorspace, default => new $colorspace(), }; - return $colorspace->transformColor($this); + return $colorspace->convertColor($this); } public function toRgb(): RgbColor { - return $this->transformTo(RgbColorspace::class); + return $this->convertTo(RgbColorspace::class); + } + + public function toRgba(): RgbaColor + { + return $this->convertTo(RgbaColorspace::class); } public function toCmyk(): self { - return $this->transformTo(CmykColorspace::class); + return $this->convertTo(CmykColorspace::class); } public function __toString(): string diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php index 968bd023d..69c6c82ad 100644 --- a/src/Colors/Cmyk/Colorspace.php +++ b/src/Colors/Cmyk/Colorspace.php @@ -3,16 +3,22 @@ namespace Intervention\Image\Colors\Cmyk; use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; class Colorspace implements ColorspaceInterface { - public function transformColor(ColorInterface $color): ColorInterface + /** + * {@inheritdoc} + * + * @see ColorspaceInterface::convertColor() + */ + public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { - RgbColor::class => $this->convertRgbColor($color), + RgbColor::class, RgbaColor::class => $this->convertRgbColor($color), default => $color, }; } diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 18be0fc2c..644b3936c 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -4,15 +4,20 @@ use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; +use Intervention\Image\Colors\Rgba\Colorspace as RgbaColorspace; use Intervention\Image\Colors\Rgb\Channels\Blue; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; +use Intervention\Image\Colors\Traits\CanHandleChannels; use Intervention\Image\Interfaces\ColorChannelInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; class Color implements ColorInterface { + use CanHandleChannels; + protected array $channels; public function __construct(int $r, int $g, int $b) @@ -24,20 +29,6 @@ public function __construct(int $r, int $g, int $b) ]; } - public function channels(): array - { - return $this->channels; - } - - public function channel(string $classname): ColorChannelInterface - { - $channels = array_filter($this->channels(), function (ColorChannelInterface $channel) use ($classname) { - return is_a($channel, $classname); - }); - - return reset($channels); - } - public function red(): Red { return $this->channel(Red::class); @@ -60,13 +51,6 @@ public function toArray(): array }, $this->channels()); } - public function normalize(): array - { - return array_map(function (ColorChannelInterface $channel) { - return $channel->normalize(); - }, $this->channels()); - } - public function toHex(string $prefix = ''): string { return sprintf( @@ -78,29 +62,29 @@ public function toHex(string $prefix = ''): string ); } - public function toRgb(): Color + public function toRgb(): self { return $this; } - public function toInt(): int - { - return $this->red()->value() * 256 * 256 + $this->green()->value() * 256 + $this->blue()->value(); - } - - public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface { $colorspace = match (true) { is_object($colorspace) => $colorspace, default => new $colorspace(), }; - return $colorspace->transformColor($this); + return $colorspace->convertColor($this); } public function toCmyk(): CmykColor { - return $this->transformTo(CmykColorspace::class); + return $this->convertTo(CmykColorspace::class); + } + + public function toRgba(): RgbaColor + { + return $this->convertTo(RgbaColorspace::class); } public function __toString(): string diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index fd28b1e92..489df51d8 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -3,15 +3,17 @@ namespace Intervention\Image\Colors\Rgb; use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; class Colorspace implements ColorspaceInterface { - public function transformColor(ColorInterface $color): ColorInterface + public function convertColor(ColorInterface $color): ColorInterface { - return match ($color) { + return match (get_class($color)) { CmykColor::class => $this->convertCmykColor($color), + RgbaColor::class => $this->convertRgbaColor($color), default => $color, }; } @@ -19,9 +21,18 @@ public function transformColor(ColorInterface $color): ColorInterface protected function convertCmykColor(CmykColor $color): Color { return new Color( - (int) (255 * (1 - $color->cyan()->value()) * (1 - $color->key()->value())), - (int) (255 * (1 - $color->magenta()->value()) * (1 - $color->key()->value())), - (int) (255 * (1 - $color->yellow()->value()) * (1 - $color->key()->value())), + (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), + ); + } + + protected function convertRgbaColor(RgbaColor $color): Color + { + return new Color( + $color->red()->value(), + $color->green()->value(), + $color->blue()->value() ); } } diff --git a/src/Colors/Rgba/Channels/Alpha.php b/src/Colors/Rgba/Channels/Alpha.php new file mode 100644 index 000000000..46d3a1522 --- /dev/null +++ b/src/Colors/Rgba/Channels/Alpha.php @@ -0,0 +1,8 @@ +channels = [ + new Red($r), + new Green($g), + new Blue($b), + new Alpha($a), + ]; + } + + public function red(): Red + { + return $this->channel(Red::class); + } + + public function green(): Green + { + return $this->channel(Green::class); + } + + public function blue(): Blue + { + return $this->channel(Blue::class); + } + + public function alpha(): Alpha + { + return $this->channel(Alpha::class); + } + + public function isFullyOpaque(): bool + { + return $this->alpha()->value() === 255; + } + + public function toHex(string $prefix = ''): string + { + if ($this->isFullyOpaque()) { + return parent::toHex($prefix); + } + + return sprintf( + '%s%02x%02x%02x%02x', + $prefix, + $this->red()->value(), + $this->green()->value(), + $this->blue()->value(), + $this->alpha()->value() + ); + } + + public function toRgb(): RgbColor + { + return $this->convertTo(RgbColorspace::class); + } + + public function toRgba(): self + { + return $this; + } + + public function toCmyk(): CmykColor + { + return $this->convertTo(CmykColorspace::class); + } + + public function __toString(): string + { + return sprintf( + 'rgba(%d, %d, %d, %.1F)', + $this->red()->value(), + $this->green()->value(), + $this->blue()->value(), + $this->alpha()->normalize(), + ); + } +} diff --git a/src/Colors/Rgba/Colorspace.php b/src/Colors/Rgba/Colorspace.php new file mode 100644 index 000000000..28b53f83f --- /dev/null +++ b/src/Colors/Rgba/Colorspace.php @@ -0,0 +1,55 @@ + $this->convertCmykColor($color), + RgbColor::class => $this->convertRgbColor($color), + default => $color, + }; + } + + /** + * Convert given color to the RGBA colorspace + * + * @param RgbColor $color + * @return Color + */ + protected function convertRgbColor(RgbColor $color): Color + { + return new Color( + $color->red()->value(), + $color->green()->value(), + $color->blue()->value(), + 255 + ); + } + + /** + * Convert given color to the RGBA colorspace + * + * @param CmykColor $color + * @return Color + */ + protected function convertCmykColor(CmykColor $color): Color + { + return new Color( + (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), + 255 + ); + } +} diff --git a/src/Colors/Traits/CanHandleChannels.php b/src/Colors/Traits/CanHandleChannels.php new file mode 100644 index 000000000..bc2311393 --- /dev/null +++ b/src/Colors/Traits/CanHandleChannels.php @@ -0,0 +1,34 @@ +channels; + } + + public function channel(string $classname): ColorChannelInterface + { + $channels = array_filter($this->channels(), function (ColorChannelInterface $channel) use ($classname) { + return get_class($channel) == $classname; + }); + + if (count($channels) == 0) { + throw new ColorException('Channel ' . $classname . ' could not be found.'); + } + + return reset($channels); + } + + public function normalize(): array + { + return array_map(function (ColorChannelInterface $channel) { + return $channel->normalize(); + }, $this->channels()); + } +} diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 0953402d7..1333e94be 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -2,18 +2,20 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Colors\CmykColor; -use Intervention\Image\Colors\RgbColor; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; interface ColorInterface { + // public function toRgb(): RgbColor; + // public function toRgba(): RgbaColor; + // public function toCmyk(): CmykColor; + // public function channels(): array; // public function channel(string $classname): ColorChannelInterface; // public function colorspace(): ColorspaceInterface; - // public function toRgb(): RgbColor; - // public function toRgba(): RgbColor; - // public function toCmyk(): CmykColor; // public function toArray(): array; - // public function transformTo(string|ColorspaceInterface $colorspace): ColorInterface; + // public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; // public function __toString(): string; } diff --git a/src/Interfaces/ColorspaceInterface.php b/src/Interfaces/ColorspaceInterface.php index b1dc3bd1f..a2bb37db3 100644 --- a/src/Interfaces/ColorspaceInterface.php +++ b/src/Interfaces/ColorspaceInterface.php @@ -4,5 +4,11 @@ interface ColorspaceInterface { - public function transformColor(ColorInterface $color): ColorInterface; + /** + * Convert given color to the format of the current colorspace + * + * @param ColorInterface $color + * @return ColorInterface + */ + public function convertColor(ColorInterface $color): ColorInterface; } diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php new file mode 100644 index 000000000..2052480f8 --- /dev/null +++ b/tests/Colors/Cmyk/ColorTest.php @@ -0,0 +1,94 @@ +assertInstanceOf(Color::class, $color); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertIsArray($color->channels()); + $this->assertCount(4, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30, 40); + $channel = $color->channel(Cyan::class); + $this->assertInstanceOf(Cyan::class, $channel); + $this->assertEquals(10, $channel->value()); + } + + public function testCyanMagentaYellowKey(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertInstanceOf(Cyan::class, $color->cyan()); + $this->assertInstanceOf(Magenta::class, $color->magenta()); + $this->assertInstanceOf(Yellow::class, $color->yellow()); + $this->assertInstanceOf(Key::class, $color->key()); + $this->assertEquals(10, $color->cyan()->value()); + $this->assertEquals(20, $color->magenta()->value()); + $this->assertEquals(30, $color->yellow()->value()); + $this->assertEquals(40, $color->key()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertEquals([10, 20, 30, 40], $color->toArray()); + } + + public function testNormalize(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals([1.0, 0.5, 0.2, 0.0], $color->normalize()); + } + + public function testToString(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals('cmyk(100, 50, 20, 0)', (string) $color); + } + + public function testToCmyk(): void + { + $color = new Color(0, 0, 0, 0); + $converted = $color->toCmyk(); + $this->assertInstanceOf(Color::class, $converted); + } + + public function testToRgb(): void + { + $color = new Color(0, 20, 20, 0); + $converted = $color->toRgb(); + $this->assertInstanceOf(RgbColor::class, $converted); + $this->assertEquals([255, 204, 204], $converted->toArray()); + } + + public function testToRgba(): void + { + $color = new Color(0, 20, 20, 0); + $converted = $color->toRgba(); + $this->assertInstanceOf(RgbaColor::class, $converted); + $this->assertEquals([255, 204, 204, 255], $converted->toArray()); + } +} diff --git a/tests/Colors/Cmyk/ColorspaceTest.php b/tests/Colors/Cmyk/ColorspaceTest.php new file mode 100644 index 000000000..818269fcf --- /dev/null +++ b/tests/Colors/Cmyk/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + CmykColor::class, + $colorspace->convertColor( + new RgbColor(0, 0, 0) + ) + ); + } +} diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index 31e4436bd..5c542fa77 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Tests\Colors\Rgb; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Blue; @@ -65,18 +67,6 @@ public function testNormalize(): void $this->assertEquals([1.0, 0.0, 0.2], $color->normalize()); } - public function testToInt(): void - { - $color = new Color(0, 0, 0); - $this->assertEquals(0, $color->toInt()); - - $color = new Color(255, 255, 255); - $this->assertEquals(16777215, $color->toInt()); - - $color = new Color(181, 55, 23); - $this->assertEquals(11876119, $color->toInt()); - } - public function testToString(): void { $color = new Color(181, 55, 23); @@ -86,21 +76,47 @@ public function testToString(): void public function testToCmyk(): void { $color = new Color(0, 0, 0); - $this->assertEquals([0, 0, 0, 100], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 100], $converted->toArray()); $color = new Color(255, 255, 255); - $this->assertEquals([0, 0, 0, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 0], $converted->toArray()); $color = new Color(255, 0, 0); - $this->assertEquals([0, 100, 100, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 100, 100, 0], $converted->toArray()); $color = new Color(255, 0, 255); - $this->assertEquals([0, 100, 0, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 100, 0, 0], $converted->toArray()); $color = new Color(255, 255, 0); - $this->assertEquals([0, 0, 100, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 100, 0], $converted->toArray()); $color = new Color(255, 204, 204); - $this->assertEquals([0, 20, 20, 0], $color->toCmyk()->toArray()); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 20, 20, 0], $converted->toArray()); + } + + public function testToRgb(): void + { + $color = new Color(181, 55, 23); + $this->assertInstanceOf(Color::class, $color->toRgb()); + } + + public function testToRgba(): void + { + $color = new Color(181, 55, 23); + $converted = $color->toRgba(); + $this->assertInstanceOf(RgbaColor::class, $converted); + $this->assertEquals([181, 55, 23, 255], $converted->toArray()); } } diff --git a/tests/Colors/Rgb/ColorspaceTest.php b/tests/Colors/Rgb/ColorspaceTest.php new file mode 100644 index 000000000..be7808c5f --- /dev/null +++ b/tests/Colors/Rgb/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + RgbColor::class, + $colorspace->convertColor( + new CmykColor(0, 0, 0, 0) + ) + ); + } +} diff --git a/tests/Colors/Rgba/ColorTest.php b/tests/Colors/Rgba/ColorTest.php new file mode 100644 index 000000000..cf9b9e612 --- /dev/null +++ b/tests/Colors/Rgba/ColorTest.php @@ -0,0 +1,119 @@ +assertInstanceOf(Color::class, $color); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30, 255); + $this->assertIsArray($color->channels()); + $this->assertCount(4, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30, 255); + $channel = $color->channel(Red::class); + $this->assertInstanceOf(Red::class, $channel); + $this->assertEquals(10, $channel->value()); + $channel = $color->channel(Alpha::class); + $this->assertInstanceOf(Alpha::class, $channel); + $this->assertEquals(255, $channel->value()); + } + + public function testRedGreenBlueAlpha(): void + { + $color = new Color(10, 20, 30, 255); + $this->assertInstanceOf(Red::class, $color->red()); + $this->assertInstanceOf(Green::class, $color->green()); + $this->assertInstanceOf(Blue::class, $color->blue()); + $this->assertInstanceOf(Alpha::class, $color->alpha()); + $this->assertEquals(10, $color->red()->value()); + $this->assertEquals(20, $color->green()->value()); + $this->assertEquals(30, $color->blue()->value()); + $this->assertEquals(255, $color->alpha()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30, 255); + $this->assertEquals([10, 20, 30, 255], $color->toArray()); + } + + public function testToHex(): void + { + $color = new Color(181, 55, 23, 0); + $this->assertEquals('b5371700', $color->toHex()); + $this->assertEquals('#b5371700', $color->toHex('#')); + + $color = new Color(181, 55, 23, 255); + $this->assertEquals('b53717', $color->toHex()); + + $color = new Color(181, 55, 23, 204); + $this->assertEquals('b53717cc', $color->toHex()); + } + + public function testNormalize(): void + { + $color = new Color(255, 0, 51, 255); + $this->assertEquals([1.0, 0.0, 0.2, 1.0], $color->normalize()); + $color = new Color(255, 0, 51, 51); + $this->assertEquals([1.0, 0.0, 0.2, 0.2], $color->normalize()); + } + + public function testToString(): void + { + $color = new Color(255, 255, 255, 255); + $this->assertEquals('rgba(255, 255, 255, 1.0)', (string) $color); + + $color = new Color(10, 20, 30, 85); + $this->assertEquals('rgba(10, 20, 30, 0.3)', (string) $color); + } + + public function testToRgba(): void + { + $color = new Color(181, 55, 23, 120); + $converted = $color->toRgba(); + $this->assertInstanceOf(Color::class, $converted); + } + + public function testToRgb(): void + { + $color = new Color(181, 55, 23, 120); + $converted = $color->toRgb(); + $this->assertInstanceOf(RgbColor::class, $converted); + $this->assertEquals([181, 55, 23], $converted->toArray()); + } + + public function testToCmyk(): void + { + $color = new Color(0, 0, 0, 255); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 100], $converted->toArray()); + + $color = new Color(0, 0, 0, 0); + $converted = $color->toCmyk(); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 100], $converted->toArray()); + } +} diff --git a/tests/Colors/Rgba/ColorspaceTest.php b/tests/Colors/Rgba/ColorspaceTest.php new file mode 100644 index 000000000..2ff88b0f6 --- /dev/null +++ b/tests/Colors/Rgba/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + RgbaColor::class, + $colorspace->convertColor( + new CmykColor(0, 0, 0, 0) + ) + ); + } +} From a5cc7e2d41a8a01b823d2461bab619f971d070aa Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 14 Oct 2023 14:42:27 +0200 Subject: [PATCH 0438/1667] Add channel tests --- tests/Colors/Cmyk/ChannelTest.php | 48 +++++++++++++++++++++++++++++++ tests/Colors/Rgb/ChannelTest.php | 47 ++++++++++++++++++++++++++++++ tests/Colors/Rgba/ChannelTest.php | 48 +++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 tests/Colors/Cmyk/ChannelTest.php create mode 100644 tests/Colors/Rgb/ChannelTest.php create mode 100644 tests/Colors/Rgba/ChannelTest.php diff --git a/tests/Colors/Cmyk/ChannelTest.php b/tests/Colors/Cmyk/ChannelTest.php new file mode 100644 index 000000000..44928abf6 --- /dev/null +++ b/tests/Colors/Cmyk/ChannelTest.php @@ -0,0 +1,48 @@ +assertInstanceOf(Channel::class, $channel); + } + + public function testValue(): void + { + $channel = new Channel(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Channel(100); + $this->assertEquals(1, $channel->normalize()); + $channel = new Channel(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Channel(20); + $this->assertEquals(.2, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Channel(101); + + $this->expectException(ColorException::class); + new Channel(-1); + } +} diff --git a/tests/Colors/Rgb/ChannelTest.php b/tests/Colors/Rgb/ChannelTest.php new file mode 100644 index 000000000..4cc2fadbb --- /dev/null +++ b/tests/Colors/Rgb/ChannelTest.php @@ -0,0 +1,47 @@ +assertInstanceOf(Channel::class, $channel); + } + + public function testValue(): void + { + $channel = new Channel(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Channel(255); + $this->assertEquals(1, $channel->normalize()); + $channel = new Channel(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Channel(51); + $this->assertEquals(.2, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Channel(256); + + $this->expectException(ColorException::class); + new Channel(-1); + } +} diff --git a/tests/Colors/Rgba/ChannelTest.php b/tests/Colors/Rgba/ChannelTest.php new file mode 100644 index 000000000..a1144e40e --- /dev/null +++ b/tests/Colors/Rgba/ChannelTest.php @@ -0,0 +1,48 @@ +assertInstanceOf(Channel::class, $channel); + } + + public function testValue(): void + { + $channel = new Channel(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Channel(255); + $this->assertEquals(1, $channel->normalize()); + $channel = new Channel(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Channel(51); + $this->assertEquals(.2, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Channel(256); + + $this->expectException(ColorException::class); + new Channel(-1); + } +} From dbbcded7a46049233e68831ecf4a6c469f143b5b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 15 Oct 2023 10:39:15 +0200 Subject: [PATCH 0439/1667] Replace driver color classes --- docker-compose.yml | 2 +- src/Colors/Rgb/Parser.php | 193 ++++++++++++++++++ src/Colors/Rgba/Parser.php | 55 +++++ src/Drivers/Gd/Decoders/HexColorDecoder.php | 25 ++- .../Gd/Decoders/HtmlColorNameDecoder.php | 18 +- .../Gd/Decoders/RgbArrayColorDecoder.php | 38 ---- .../Gd/Decoders/RgbStringColorDecoder.php | 26 +-- src/Drivers/Gd/InputHandler.php | 5 +- .../Imagick/Decoders/HexColorDecoder.php | 25 ++- .../Imagick/Decoders/HtmlColorNameDecoder.php | 15 +- .../Imagick/Decoders/RgbArrayColorDecoder.php | 36 ---- .../Decoders/RgbStringColorDecoder.php | 20 +- src/Drivers/Imagick/InputHandler.php | 7 +- src/Traits/CanReadHtmlColorNames.php | 165 --------------- tests/Colors/Rgb/ParserTest.php | 60 ++++++ tests/Colors/Rgba/ParserTest.php | 68 ++++++ tests/Drivers/Abstract/AbstractColorTest.php | 39 ---- tests/Drivers/Gd/ColorTest.php | 88 -------- .../Gd/Decoders/RgbArrayColorDecoderTest.php | 25 --- tests/Drivers/Gd/InputHandlerTest.php | 75 +++---- tests/Drivers/Imagick/ColorTest.php | 112 ---------- .../Decoders/RgbArrayColorDecoderTest.php | 25 --- tests/Drivers/Imagick/InputHandlerTest.php | 55 ++++- 23 files changed, 542 insertions(+), 635 deletions(-) create mode 100644 src/Colors/Rgb/Parser.php create mode 100644 src/Colors/Rgba/Parser.php delete mode 100644 src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php delete mode 100644 src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php delete mode 100644 src/Traits/CanReadHtmlColorNames.php create mode 100644 tests/Colors/Rgb/ParserTest.php create mode 100644 tests/Colors/Rgba/ParserTest.php delete mode 100644 tests/Drivers/Abstract/AbstractColorTest.php delete mode 100644 tests/Drivers/Gd/ColorTest.php delete mode 100644 tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php delete mode 100644 tests/Drivers/Imagick/ColorTest.php delete mode 100644 tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php diff --git a/docker-compose.yml b/docker-compose.yml index 8d90fbff8..e32993d79 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: tests: build: ./ working_dir: /project - command: bash -c "composer install && ./vendor/bin/phpunit -vvv" + command: bash -c "composer install && ./vendor/bin/phpunit -vvv --filter=InputHandlerTest" volumes: - ./:/project analysis: diff --git a/src/Colors/Rgb/Parser.php b/src/Colors/Rgb/Parser.php new file mode 100644 index 000000000..be787daba --- /dev/null +++ b/src/Colors/Rgb/Parser.php @@ -0,0 +1,193 @@ +[0-9a-f]{3}|[0-9a-f]{6})$/i'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + $matches = match (strlen($matches['hex'])) { + 3 => str_split($matches['hex']), + 6 => str_split($matches['hex'], 2), + default => throw new ColorException('Unable to parse color'), + }; + + return new Color( + strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + ); + } + + public static function fromString(string $input): Color + { + $pattern = '/^rgb\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + return new Color($matches['r'], $matches['g'], $matches['b']); + } + + public static function fromName(string $input): Color + { + $names = [ + 'lightsalmon' => '#FFA07A', + 'salmon' => '#FA8072', + 'darksalmon' => '#E9967A', + 'lightcoral' => '#F08080', + 'indianred' => '#CD5C5C', + 'crimson' => '#DC143C', + 'firebrick' => '#B22222', + 'red' => '#FF0000', + 'darkred' => '#8B0000', + 'coral' => '#FF7F50', + 'tomato' => '#FF6347', + 'orangered' => '#FF4500', + 'gold' => '#FFD700', + 'orange' => '#FFA500', + 'darkorange' => '#FF8C00', + 'lightyellow' => '#FFFFE0', + 'lemonchiffon' => '#FFFACD', + 'lightgoldenrodyellow' => '#FAFAD2', + 'papayawhip' => '#FFEFD5', + 'moccasin' => '#FFE4B5', + 'peachpuff' => '#FFDAB9', + 'palegoldenrod' => '#EEE8AA', + 'khaki' => '#F0E68C', + 'darkkhaki' => '#BDB76B', + 'yellow' => '#FFFF00', + 'lawngreen' => '#7CFC00', + 'chartreuse' => '#7FFF00', + 'limegreen' => '#32CD32', + 'lime' => '#00FF00', + 'forestgreen' => '#228B22', + 'green' => '#008000', + 'darkgreen' => '#006400', + 'greenyellow' => '#ADFF2F', + 'yellowgreen' => '#9ACD32', + 'springgreen' => '#00FF7F', + 'mediumspringgreen' => '#00FA9A', + 'lightgreen' => '#90EE90', + 'palegreen' => '#98FB98', + 'darkseagreen' => '#8FBC8F', + 'mediumseagre' => 'en #3CB371', + 'seagreen' => '#2E8B57', + 'olive' => '#808000', + 'darkolivegreen' => '#556B2F', + 'olivedrab' => '#6B8E23', + 'lightcyan' => '#E0FFFF', + 'cyan' => '#00FFFF', + 'aqua' => '#00FFFF', + 'aquamarine' => '#7FFFD4', + 'mediumaquamarine' => '#66CDAA', + 'paleturquoise' => '#AFEEEE', + 'turquoise' => '#40E0D0', + 'mediumturquoise' => '#48D1CC', + 'darkturquoise' => '#00CED1', + 'lightseagreen' => '#20B2AA', + 'cadetblue' => '#5F9EA0', + 'darkcyan' => '#008B8B', + 'teal' => '#008080', + 'powderblue' => '#B0E0E6', + 'lightblue' => '#ADD8E6', + 'lightskyblue' => '#87CEFA', + 'skyblue' => '#87CEEB', + 'deepskyblue' => '#00BFFF', + 'lightsteelblue' => '#B0C4DE', + 'dodgerblue' => '#1E90FF', + 'cornflowerblue' => '#6495ED', + 'steelblue' => '#4682B4', + 'royalblue' => '#4169E1', + 'blue' => '#0000FF', + 'mediumblue' => '#0000CD', + 'darkblue' => '#00008B', + 'navy' => '#000080', + 'midnightblue' => '#191970', + 'mediumslateblue' => '#7B68EE', + 'slateblue' => '#6A5ACD', + 'darkslateblue' => '#483D8B', + 'lavender' => '#E6E6FA', + 'thistle' => '#D8BFD8', + 'plum' => '#DDA0DD', + 'violet' => '#EE82EE', + 'orchid' => '#DA70D6', + 'fuchsia' => '#FF00FF', + 'magenta' => '#FF00FF', + 'mediumorchid' => '#BA55D3', + 'mediumpurple' => '#9370DB', + 'blueviolet' => '#8A2BE2', + 'darkviolet' => '#9400D3', + 'darkorchid' => '#9932CC', + 'darkmagenta' => '#8B008B', + 'purple' => '#800080', + 'indigo' => '#4B0082', + 'pink' => '#FFC0CB', + 'lightpink' => '#FFB6C1', + 'hotpink' => '#FF69B4', + 'deeppink' => '#FF1493', + 'palevioletred' => '#DB7093', + 'mediumvioletred' => '#C71585', + 'white' => '#FFFFFF', + 'snow' => '#FFFAFA', + 'honeydew' => '#F0FFF0', + 'mintcream' => '#F5FFFA', + 'azure' => '#F0FFFF', + 'aliceblue' => '#F0F8FF', + 'ghostwhite' => '#F8F8FF', + 'whitesmoke' => '#F5F5F5', + 'seashell' => '#FFF5EE', + 'beige' => '#F5F5DC', + 'oldlace' => '#FDF5E6', + 'floralwhite' => '#FFFAF0', + 'ivory' => '#FFFFF0', + 'antiquewhite' => '#FAEBD7', + 'linen' => '#FAF0E6', + 'lavenderblush' => '#FFF0F5', + 'mistyrose' => '#FFE4E1', + 'gainsboro' => '#DCDCDC', + 'lightgray' => '#D3D3D3', + 'silver' => '#C0C0C0', + 'darkgray' => '#A9A9A9', + 'gray' => '#808080', + 'dimgray' => '#696969', + 'lightslategray' => '#778899', + 'slategray' => '#708090', + 'darkslategray' => '#2F4F4F', + 'black' => '#000000', + 'cornsilk' => '#FFF8DC', + 'blanchedalmond' => '#FFEBCD', + 'bisque' => '#FFE4C4', + 'navajowhite' => '#FFDEAD', + 'wheat' => '#F5DEB3', + 'burlywood' => '#DEB887', + 'tan' => '#D2B48C', + 'rosybrown' => '#BC8F8F', + 'sandybrown' => '#F4A460', + 'goldenrod' => '#DAA520', + 'peru' => '#CD853F', + 'chocolate' => '#D2691E', + 'saddlebrown' => '#8B4513', + 'sienna' => '#A0522D', + 'brown' => '#A52A2A', + 'maroon' => '#800000', + ]; + + if (!array_key_exists(strtolower($input), $names)) { + throw new ColorException('Unable to parse color'); + } + + return static::fromHex($names[strtolower($input)]); + } +} diff --git a/src/Colors/Rgba/Parser.php b/src/Colors/Rgba/Parser.php new file mode 100644 index 000000000..a567f70e9 --- /dev/null +++ b/src/Colors/Rgba/Parser.php @@ -0,0 +1,55 @@ +toRgba(); + } catch (ColorException $e) { + // move on + } + + $pattern = '/^#?(?P[0-9a-f]{4}|[0-9a-f]{8})$/i'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + $matches = match (strlen($matches['hex'])) { + 4 => str_split($matches['hex']), + 8 => str_split($matches['hex'], 2), + default => throw new ColorException('Unable to parse color'), + }; + + return new Color( + strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), + ); + } + + public static function fromString(string $input): Color + { + $pattern = '/^rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + return new Color( + $matches['r'], + $matches['g'], + $matches['b'], + intval(round(floatval($matches['a']) * 255)) + ); + } +} diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php index 8cb6b27b5..3371659a8 100644 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ b/src/Drivers/Gd/Decoders/HexColorDecoder.php @@ -2,12 +2,16 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Colors\Rgb\Parser as RgbColorParser; +use Intervention\Image\Colors\Rgba\Parser as RgbaColorParser; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class HexColorDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { @@ -15,17 +19,18 @@ public function decode($input): ImageInterface|ColorInterface throw new DecoderException('Unable to decode input'); } - $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; - $result = preg_match($pattern, $input, $matches); + try { + return RgbColorParser::fromHex($input); + } catch (ColorException $e) { + # code ... + } - if ($result !== 1) { - throw new DecoderException('Unable to decode input'); + try { + return RgbaColorParser::fromHex($input); + } catch (ColorException $e) { + # code ... } - return parent::decode([ - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), - strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), - ]); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php b/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php index 59ed6fd28..fd1d9dce2 100644 --- a/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php +++ b/src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php @@ -2,27 +2,27 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Colors\Rgb\Parser; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanReadHtmlColorNames; -class HtmlColorNameDecoder extends HexColorDecoder +class HtmlColorNameDecoder extends AbstractDecoder { - use CanReadHtmlColorNames; - public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { throw new DecoderException('Unable to decode input'); } - $hexcolor = $this->hexColorFromColorName($input); - - if (empty($hexcolor)) { - throw new DecoderException('Unable to decode input'); + try { + return Parser::fromName($input); + } catch (ColorException $e) { + # code ... } - return parent::decode($hexcolor); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php b/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php deleted file mode 100644 index 8b25f691f..000000000 --- a/src/Drivers/Gd/Decoders/RgbArrayColorDecoder.php +++ /dev/null @@ -1,38 +0,0 @@ -isValidColorArray($input)) { - throw new DecoderException('Unable to decode input'); - } - - if (count($input) === 3) { - $input[] = 1; - } - - list($r, $g, $b, $a) = $input; - - return new Color( - ($this->opacityToGdAlpha($a) << 24) + ($r << 16) + ($g << 8) + $b - ); - } - - protected function opacityToGdAlpha(float $opacity): int - { - return intval(round($opacity * 127 * -1 + 127)); - } -} diff --git a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php index 03c46b960..8707375e2 100644 --- a/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Gd/Decoders/RgbStringColorDecoder.php @@ -2,12 +2,16 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Colors\Rgb\Parser as RgbColorParser; +use Intervention\Image\Colors\Rgba\Parser as RgbaColorParser; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class RgbStringColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class RgbStringColorDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { @@ -15,20 +19,16 @@ public function decode($input): ImageInterface|ColorInterface throw new DecoderException('Unable to decode input'); } - if (substr($input, 0, 3) !== 'rgb') { - throw new DecoderException('Unable to decode input'); - } - - // rgb string like rgb(102, 200, 0) - $pattern = "/^rgb ?\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/i"; - if ((bool) preg_match($pattern, $input, $matches)) { - return parent::decode([$matches['r'], $matches['g'], $matches['b']]); + try { + return RgbColorParser::fromString($input); + } catch (ColorException $e) { + # code ... } - // rgba string like "rgba(200, 10, 30, 0.5)" - $pattern = "/^rgba ?\(((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?(?P[0-9.]{1,4})\)$/i"; - if ((bool) preg_match($pattern, $input, $matches)) { - return parent::decode([$matches['r'], $matches['g'], $matches['b'], $matches['a']]); + try { + return RgbaColorParser::fromString($input); + } catch (ColorException $e) { + # code ... } throw new DecoderException('Unable to decode input'); diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 9fec013ba..42785e597 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -9,11 +9,10 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ Decoders\ImageObjectDecoder::class, Decoders\FilePointerImageDecoder::class, - Decoders\RgbArrayColorDecoder::class, Decoders\HtmlColorNameDecoder::class, - Decoders\RgbStringColorDecoder::class, Decoders\HexColorDecoder::class, - Decoders\TransparentColorDecoder::class, + Decoders\RgbStringColorDecoder::class, + // Decoders\TransparentColorDecoder::class, Decoders\FilePathImageDecoder::class, Decoders\BinaryImageDecoder::class, Decoders\DataUriImageDecoder::class, diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php index 37498320b..e1e687b38 100644 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/HexColorDecoder.php @@ -2,12 +2,16 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Colors\Rgb\Parser as RgbColorParser; +use Intervention\Image\Colors\Rgba\Parser as RgbaColorParser; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class HexColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class HexColorDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { @@ -15,17 +19,18 @@ public function decode($input): ImageInterface|ColorInterface throw new DecoderException('Unable to decode input'); } - $pattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i'; - $result = preg_match($pattern, $input, $matches); + try { + return RgbColorParser::fromHex($input); + } catch (ColorException $e) { + # code ... + } - if ($result !== 1) { - throw new DecoderException('Unable to decode input'); + try { + return RgbaColorParser::fromHex($input); + } catch (ColorException $e) { + # code ... } - return parent::decode([ - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), - strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), - ]); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php b/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php index fba912ea5..e8cb70a6c 100644 --- a/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php +++ b/src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php @@ -2,27 +2,26 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Colors\Rgb\Parser; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Traits\CanReadHtmlColorNames; class HtmlColorNameDecoder extends HexColorDecoder { - use CanReadHtmlColorNames; - public function decode($input): ImageInterface|ColorInterface { if (!is_string($input)) { throw new DecoderException('Unable to decode input'); } - $hexcolor = $this->hexColorFromColorName($input); - - if (empty($hexcolor)) { - throw new DecoderException('Unable to decode input'); + try { + return Parser::fromName($input); + } catch (ColorException $e) { + # code ... } - return parent::decode($hexcolor); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php deleted file mode 100644 index 2e924c87e..000000000 --- a/src/Drivers/Imagick/Decoders/RgbArrayColorDecoder.php +++ /dev/null @@ -1,36 +0,0 @@ -isValidColorArray($input)) { - throw new DecoderException('Unable to decode input'); - } - - if (count($input) === 3) { - $input[] = 1; - } - - list($r, $g, $b, $a) = $input; - - $pixel = new ImagickPixel( - sprintf('rgba(%d, %d, %d, %.2F)', $r, $g, $b, $a) - ); - - return new Color($pixel); - } -} diff --git a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php index 7b036033f..cd2549602 100644 --- a/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php @@ -2,10 +2,10 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; -use ImagickPixel; -use ImagickPixelException; +use Intervention\Image\Colors\Rgb\Parser as RgbColorParser; +use Intervention\Image\Colors\Rgba\Parser as RgbaColorParser; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; @@ -19,16 +19,18 @@ public function decode($input): ImageInterface|ColorInterface throw new DecoderException('Unable to decode input'); } - if (substr($input, 0, 3) !== 'rgb') { - throw new DecoderException('Unable to decode input'); + try { + return RgbColorParser::fromString($input); + } catch (ColorException $e) { + # code ... } try { - $pixel = new ImagickPixel($input); - } catch (ImagickPixelException $e) { - throw new DecoderException('Unable to decode input'); + return RgbaColorParser::fromString($input); + } catch (ColorException $e) { + # code ... } - return new Color($pixel); + throw new DecoderException('Unable to decode input'); } } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 8b00c35e8..0fc798ffb 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -9,14 +9,13 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ Decoders\ImageObjectDecoder::class, Decoders\FilePointerImageDecoder::class, - Decoders\RgbArrayColorDecoder::class, - Decoders\HexColorDecoder::class, Decoders\HtmlColorNameDecoder::class, + Decoders\HexColorDecoder::class, Decoders\RgbStringColorDecoder::class, - Decoders\TransparentColorDecoder::class, + // Decoders\TransparentColorDecoder::class, Decoders\FilePathImageDecoder::class, Decoders\BinaryImageDecoder::class, Decoders\DataUriImageDecoder::class, - Decoders\Base64ImageDecoder::class + Decoders\Base64ImageDecoder::class, ]; } diff --git a/src/Traits/CanReadHtmlColorNames.php b/src/Traits/CanReadHtmlColorNames.php deleted file mode 100644 index c02b43676..000000000 --- a/src/Traits/CanReadHtmlColorNames.php +++ /dev/null @@ -1,165 +0,0 @@ - '#FFA07A', - 'salmon' => '#FA8072', - 'darksalmon' => '#E9967A', - 'lightcoral' => '#F08080', - 'indianred' => '#CD5C5C', - 'crimson' => '#DC143C', - 'firebrick' => '#B22222', - 'red' => '#FF0000', - 'darkred' => '#8B0000', - 'coral' => '#FF7F50', - 'tomato' => '#FF6347', - 'orangered' => '#FF4500', - 'gold' => '#FFD700', - 'orange' => '#FFA500', - 'darkorange' => '#FF8C00', - 'lightyellow' => '#FFFFE0', - 'lemonchiffon' => '#FFFACD', - 'lightgoldenrodyellow' => '#FAFAD2', - 'papayawhip' => '#FFEFD5', - 'moccasin' => '#FFE4B5', - 'peachpuff' => '#FFDAB9', - 'palegoldenrod' => '#EEE8AA', - 'khaki' => '#F0E68C', - 'darkkhaki' => '#BDB76B', - 'yellow' => '#FFFF00', - 'lawngreen' => '#7CFC00', - 'chartreuse' => '#7FFF00', - 'limegreen' => '#32CD32', - 'lime' => '#00FF00', - 'forestgreen' => '#228B22', - 'green' => '#008000', - 'darkgreen' => '#006400', - 'greenyellow' => '#ADFF2F', - 'yellowgreen' => '#9ACD32', - 'springgreen' => '#00FF7F', - 'mediumspringgreen' => '#00FA9A', - 'lightgreen' => '#90EE90', - 'palegreen' => '#98FB98', - 'darkseagreen' => '#8FBC8F', - 'mediumseagre' => 'en #3CB371', - 'seagreen' => '#2E8B57', - 'olive' => '#808000', - 'darkolivegreen' => '#556B2F', - 'olivedrab' => '#6B8E23', - 'lightcyan' => '#E0FFFF', - 'cyan' => '#00FFFF', - 'aqua' => '#00FFFF', - 'aquamarine' => '#7FFFD4', - 'mediumaquamarine' => '#66CDAA', - 'paleturquoise' => '#AFEEEE', - 'turquoise' => '#40E0D0', - 'mediumturquoise' => '#48D1CC', - 'darkturquoise' => '#00CED1', - 'lightseagreen' => '#20B2AA', - 'cadetblue' => '#5F9EA0', - 'darkcyan' => '#008B8B', - 'teal' => '#008080', - 'powderblue' => '#B0E0E6', - 'lightblue' => '#ADD8E6', - 'lightskyblue' => '#87CEFA', - 'skyblue' => '#87CEEB', - 'deepskyblue' => '#00BFFF', - 'lightsteelblue' => '#B0C4DE', - 'dodgerblue' => '#1E90FF', - 'cornflowerblue' => '#6495ED', - 'steelblue' => '#4682B4', - 'royalblue' => '#4169E1', - 'blue' => '#0000FF', - 'mediumblue' => '#0000CD', - 'darkblue' => '#00008B', - 'navy' => '#000080', - 'midnightblue' => '#191970', - 'mediumslateblue' => '#7B68EE', - 'slateblue' => '#6A5ACD', - 'darkslateblue' => '#483D8B', - 'lavender' => '#E6E6FA', - 'thistle' => '#D8BFD8', - 'plum' => '#DDA0DD', - 'violet' => '#EE82EE', - 'orchid' => '#DA70D6', - 'fuchsia' => '#FF00FF', - 'magenta' => '#FF00FF', - 'mediumorchid' => '#BA55D3', - 'mediumpurple' => '#9370DB', - 'blueviolet' => '#8A2BE2', - 'darkviolet' => '#9400D3', - 'darkorchid' => '#9932CC', - 'darkmagenta' => '#8B008B', - 'purple' => '#800080', - 'indigo' => '#4B0082', - 'pink' => '#FFC0CB', - 'lightpink' => '#FFB6C1', - 'hotpink' => '#FF69B4', - 'deeppink' => '#FF1493', - 'palevioletred' => '#DB7093', - 'mediumvioletred' => '#C71585', - 'white' => '#FFFFFF', - 'snow' => '#FFFAFA', - 'honeydew' => '#F0FFF0', - 'mintcream' => '#F5FFFA', - 'azure' => '#F0FFFF', - 'aliceblue' => '#F0F8FF', - 'ghostwhite' => '#F8F8FF', - 'whitesmoke' => '#F5F5F5', - 'seashell' => '#FFF5EE', - 'beige' => '#F5F5DC', - 'oldlace' => '#FDF5E6', - 'floralwhite' => '#FFFAF0', - 'ivory' => '#FFFFF0', - 'antiquewhite' => '#FAEBD7', - 'linen' => '#FAF0E6', - 'lavenderblush' => '#FFF0F5', - 'mistyrose' => '#FFE4E1', - 'gainsboro' => '#DCDCDC', - 'lightgray' => '#D3D3D3', - 'silver' => '#C0C0C0', - 'darkgray' => '#A9A9A9', - 'gray' => '#808080', - 'dimgray' => '#696969', - 'lightslategray' => '#778899', - 'slategray' => '#708090', - 'darkslategray' => '#2F4F4F', - 'black' => '#000000', - 'cornsilk' => '#FFF8DC', - 'blanchedalmond' => '#FFEBCD', - 'bisque' => '#FFE4C4', - 'navajowhite' => '#FFDEAD', - 'wheat' => '#F5DEB3', - 'burlywood' => '#DEB887', - 'tan' => '#D2B48C', - 'rosybrown' => '#BC8F8F', - 'sandybrown' => '#F4A460', - 'goldenrod' => '#DAA520', - 'peru' => '#CD853F', - 'chocolate' => '#D2691E', - 'saddlebrown' => '#8B4513', - 'sienna' => '#A0522D', - 'brown' => '#A52A2A', - 'maroon' => '#800000', - ]; - - /** - * Transform given html color name to hex color - * or return null, if color name doesn't exist. - * - * @param string $name - * @return null|string - */ - public function hexColorFromColorName(string $name): ?string - { - $name = strtolower($name); - if (!array_key_exists($name, $this->color_names)) { - return null; - } - - return $this->color_names[$name]; - } -} diff --git a/tests/Colors/Rgb/ParserTest.php b/tests/Colors/Rgb/ParserTest.php new file mode 100644 index 000000000..08389cbf6 --- /dev/null +++ b/tests/Colors/Rgb/ParserTest.php @@ -0,0 +1,60 @@ +assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('cccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('#cccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $this->expectException(ColorException::class); + (new Parser())->fromHex('cccccccc'); + } + + public function testFromString(): void + { + $color = Parser::fromString('rgb(204, 204, 204)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::fromString('rgb(204,204,204)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $this->expectException(ColorException::class); + (new Parser())->fromString('rgb(204,204,204,1)'); + + $this->expectException(ColorException::class); + (new Parser())->fromString('rgb(120)'); + + $this->expectException(ColorException::class); + (new Parser())->fromString('rgba(204,204,204,1)'); + } + + public function testFromName(): void + { + $color = Parser::fromName('salmon'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('fa8072', $color->toHex()); + } +} diff --git a/tests/Colors/Rgba/ParserTest.php b/tests/Colors/Rgba/ParserTest.php new file mode 100644 index 000000000..0986c797d --- /dev/null +++ b/tests/Colors/Rgba/ParserTest.php @@ -0,0 +1,68 @@ +assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromHex('cccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromHex('#cccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromHex('#cccccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('#cccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('cccccccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::fromHex('cccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + } + + public function testFromString(): void + { + $color = Parser::fromString('rgba(204, 204, 204, 1)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromString('rgba(204,204,204,1.0)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::fromString('rgba(204,204,204,0.2)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 51], $color->toArray()); + + $color = Parser::fromString('rgba(204,204, 204, .2)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 51], $color->toArray()); + + $this->expectException(ColorException::class); + $color = Parser::fromString('rgba(204, 204, 204, 1.2)'); + } +} diff --git a/tests/Drivers/Abstract/AbstractColorTest.php b/tests/Drivers/Abstract/AbstractColorTest.php deleted file mode 100644 index d419c1576..000000000 --- a/tests/Drivers/Abstract/AbstractColorTest.php +++ /dev/null @@ -1,39 +0,0 @@ -makePartial(); - $color->shouldReceive('red')->andReturn(255); - $color->shouldReceive('green')->andReturn(0); - $color->shouldReceive('blue')->andReturn(0); - - $this->assertEquals('ff0000', $color->toHex()); - $this->assertEquals('#ff0000', $color->toHex('#')); - } - - public function testIsGreyscale(): void - { - $color = Mockery::mock(AbstractColor::class)->makePartial(); - $color->shouldReceive('red')->andReturn(255); - $color->shouldReceive('green')->andReturn(0); - $color->shouldReceive('blue')->andReturn(0); - $this->assertFalse($color->isGreyscale()); - - $color = Mockery::mock(AbstractColor::class)->makePartial(); - $color->shouldReceive('red')->andReturn(100); - $color->shouldReceive('green')->andReturn(100); - $color->shouldReceive('blue')->andReturn(100); - $this->assertTrue($color->isGreyscale()); - } -} diff --git a/tests/Drivers/Gd/ColorTest.php b/tests/Drivers/Gd/ColorTest.php deleted file mode 100644 index 3c9aa9dc1..000000000 --- a/tests/Drivers/Gd/ColorTest.php +++ /dev/null @@ -1,88 +0,0 @@ -assertInstanceOf(Color::class, $this->getTestColor()); - } - - public function testRed(): void - { - $color = $this->getTestColor(255, 0, 0); - $this->assertEquals(255, $color->red()); - } - - public function testGreen(): void - { - $color = $this->getTestColor(0, 150, 0); - $this->assertEquals(150, $color->green()); - } - - public function testBlue(): void - { - $color = $this->getTestColor(0, 0, 120); - $this->assertEquals(120, $color->blue()); - } - - public function testAlpha(): void - { - $color = $this->getTestColor(0, 0, 120, 0); - $this->assertEquals(1, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, 127); - $this->assertEquals(0, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, 64); - $this->assertEquals(.5, $color->alpha()); - } - - public function testToArray(): void - { - $color = $this->getTestColor(0, 0, 120, 0); - $this->assertEquals([0, 0, 120, 1], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, 127); - $this->assertEquals([0, 0, 120, 0], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, 64); - $this->assertEquals([0, 0, 120, .5], $color->toArray()); - } - - public function testToInt(): void - { - $color = $this->getTestColor(0, 0, 0, 0); - $this->assertEquals(0, $color->toInt()); - - $color = $this->getTestColor(255, 255, 255, 0); - $this->assertEquals(16777215, $color->toInt()); - } - - public function testToHex(): void - { - $color = $this->getTestColor(181, 55, 23); - $this->assertEquals('b53717', $color->toHex()); - $this->assertEquals('#b53717', $color->toHex('#')); - - $color = $this->getTestColor(181, 55, 23, 127); - $this->assertEquals('b53717', $color->toHex()); - $this->assertEquals('#b53717', $color->toHex('#')); - } -} diff --git a/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php deleted file mode 100644 index 524b2f630..000000000 --- a/tests/Drivers/Gd/Decoders/RgbArrayColorDecoderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -decode([181, 55, 23, .5]); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(.5, $color->alpha()); - } -} diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 829fdca1a..8157f01ad 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,7 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -12,13 +13,13 @@ * @requires extension gd * @covers \Intervention\Image\Drivers\Gd\InputHandler */ -class InputHandlerTest extends TestCase +class GdInputHandlerTest extends TestCase { public function testHandleEmptyString(): void { $handler = new InputHandler(); $this->expectException(DecoderException::class); - $result = $handler->handle(''); + $handler->handle(''); } public function testHandleBinaryImage(): void @@ -53,59 +54,63 @@ public function testHandleDataUriImage(): void $this->assertInstanceOf(Image::class, $result); } - public function testHandleArrayColor(): void + public function testHandleHexColor(): void { $handler = new InputHandler(); - $input = [181, 55, 23, .5]; + $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([204, 255, 51], $result->toArray()); $handler = new InputHandler(); - $input = [181, 55, 23]; + $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - } + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([204, 255, 51], $result->toArray()); - public function testHandleHexColor(): void - { $handler = new InputHandler(); - $input = 'ccff33'; + $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals(204, $result->red()); - $this->assertEquals(255, $result->green()); - $this->assertEquals(51, $result->blue()); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([18, 52, 86], $result->toArray()); $handler = new InputHandler(); - $input = 'cf3'; + $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals(204, $result->red()); - $this->assertEquals(255, $result->green()); - $this->assertEquals(51, $result->blue()); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([51, 51, 51], $result->toArray()); $handler = new InputHandler(); - $input = '#123456'; + $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals(18, $result->red()); - $this->assertEquals(52, $result->green()); - $this->assertEquals(86, $result->blue()); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); - $input = '#333'; + $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals(51, $result->red()); - $this->assertEquals(51, $result->green()); - $this->assertEquals(51, $result->blue()); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([51, 51, 51, 51], $result->toArray()); } - public function testHandleTransparent(): void + public function testHandleRgbString(): void { $handler = new InputHandler(); - $input = 'transparent'; - $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $result = $handler->handle('rgb(10, 20, 30)'); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([10, 20, 30], $result->toArray()); + + $handler = new InputHandler(); + $result = $handler->handle('rgba(10, 20, 30, 1.0)'); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([10, 20, 30, 255], $result->toArray()); } + + // public function testHandleTransparent(): void + // { + // $handler = new InputHandler(); + // $input = 'transparent'; + // $result = $handler->handle($input); + // $this->assertInstanceOf(Color::class, $result); + // } } diff --git a/tests/Drivers/Imagick/ColorTest.php b/tests/Drivers/Imagick/ColorTest.php deleted file mode 100644 index 2840e98f2..000000000 --- a/tests/Drivers/Imagick/ColorTest.php +++ /dev/null @@ -1,112 +0,0 @@ -assertInstanceOf(Color::class, $this->getTestColor()); - } - - public function testRed(): void - { - $color = $this->getTestColor(255, 0, 0); - $this->assertEquals(255, $color->red()); - } - - public function testGreen(): void - { - $color = $this->getTestColor(0, 150, 0); - $this->assertEquals(150, $color->green()); - } - - public function testBlue(): void - { - $color = $this->getTestColor(0, 0, 120); - $this->assertEquals(120, $color->blue()); - } - - public function testAlpha(): void - { - $color = $this->getTestColor(0, 0, 120, 1); - $this->assertEquals(1, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, 0); - $this->assertEquals(0, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, .5); - $this->assertEquals(.5, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, .57); - $this->assertEquals(.57, $color->alpha()); - - $color = $this->getTestColor(0, 0, 120, .578); - $this->assertEquals(.58, $color->alpha()); - } - - public function testToArray(): void - { - $color = $this->getTestColor(0, 0, 120, 1); - $this->assertEquals([0, 0, 120, 1], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, 0); - $this->assertEquals([0, 0, 120, 0], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, .5); - $this->assertEquals([0, 0, 120, .5], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, .57); - $this->assertEquals([0, 0, 120, .57], $color->toArray()); - - $color = $this->getTestColor(0, 0, 120, .578); - $this->assertEquals([0, 0, 120, .58], $color->toArray()); - } - - public function testToHex(): void - { - $color = $this->getTestColor(181, 55, 23); - $this->assertEquals('b53717', $color->toHex()); - $this->assertEquals('#b53717', $color->toHex('#')); - - $color = $this->getTestColor(181, 55, 23, 127); - $this->assertEquals('b53717', $color->toHex()); - $this->assertEquals('#b53717', $color->toHex('#')); - } - - public function testToInt(): void - { - $color = $this->getTestColor(255, 255, 255); - $this->assertEquals($color->toInt(), 4294967295); - - $color = $this->getTestColor(255, 255, 255, 1); - $this->assertEquals($color->toInt(), 4294967295); - - $color = $this->getTestColor(181, 55, 23, 0.2); - $this->assertEquals($color->toInt(), 867514135); - - $color = $this->getTestColor(255, 255, 255, 0.5); - $this->assertEquals($color->toInt(), 2164260863); - - $color = $this->getTestColor(181, 55, 23, 1); - $this->assertEquals($color->toInt(), 4290066199); - - $color = $this->getTestColor(0, 0, 0, 0); - $this->assertEquals($color->toInt(), 0); - } -} diff --git a/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php deleted file mode 100644 index fbb5b4e0b..000000000 --- a/tests/Drivers/Imagick/Decoders/RgbArrayColorDecoderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -decode([181, 55, 23, .5]); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(.5, $color->alpha()); - } -} diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index 627529efa..f125f686e 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,7 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -18,7 +19,7 @@ public function testHandleEmptyString(): void { $handler = new InputHandler(); $this->expectException(DecoderException::class); - $result = $handler->handle(''); + $handler->handle(''); } public function testHandleBinaryImage(): void @@ -53,11 +54,55 @@ public function testHandleDataUriImage(): void $this->assertInstanceOf(Image::class, $result); } - public function testHandleArrayColor(): void + public function testHandleHexColor(): void { $handler = new InputHandler(); - $input = [181, 55, 23, .5]; + $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([204, 255, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = 'cf3'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([204, 255, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = '#123456'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([18, 52, 86], $result->toArray()); + + $handler = new InputHandler(); + $input = '#333'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([51, 51, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = '#3333'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([51, 51, 51, 51], $result->toArray()); + + $handler = new InputHandler(); + $input = '#33333333'; + $result = $handler->handle($input); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([51, 51, 51, 51], $result->toArray()); + } + + public function testHandleRgbString(): void + { + $handler = new InputHandler(); + $result = $handler->handle('rgb(10, 20, 30)'); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals([10, 20, 30], $result->toArray()); + + $handler = new InputHandler(); + $result = $handler->handle('rgba(10, 20, 30, 1.0)'); + $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertEquals([10, 20, 30, 255], $result->toArray()); } } From 56ab8b1ea7f2e8c578026e44cf88215584654ca0 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 15 Oct 2023 11:29:33 +0200 Subject: [PATCH 0440/1667] Replace color implementation in FillModifiers --- docker-compose.yml | 2 +- src/Colors/Cmyk/Channels/Cyan.php | 10 +++ src/Colors/Cmyk/Color.php | 7 ++- src/Colors/Cmyk/Parser.php | 34 +++++++++++ src/Colors/Parser.php | 28 +++++++++ src/Colors/Rgb/Channels/Red.php | 10 +++ src/Colors/Rgb/Color.php | 7 ++- src/Colors/Rgb/Parser.php | 50 +++++++++++++-- src/Colors/Rgba/Channels/Alpha.php | 10 ++- src/Colors/Rgba/Color.php | 7 ++- src/Colors/Rgba/Parser.php | 31 +++++++--- src/Drivers/Gd/Color.php | 50 --------------- src/Drivers/Gd/ColorTransformer.php | 61 +++++++++++++++++++ ...olorDecoder.php => ColorObjectDecoder.php} | 7 ++- src/Drivers/Gd/Image.php | 2 +- src/Drivers/Gd/InputHandler.php | 1 + src/Drivers/Gd/Modifiers/FillModifier.php | 9 ++- src/Drivers/Imagick/Color.php | 61 ------------------- src/Drivers/Imagick/ColorTransformer.php | 32 ++++++++++ ...olorDecoder.php => ColorObjectDecoder.php} | 7 ++- src/Drivers/Imagick/Image.php | 4 +- src/Drivers/Imagick/InputHandler.php | 1 + .../Imagick/Modifiers/FillModifier.php | 14 +++-- src/Drivers/Imagick/Traits/CanReadColors.php | 14 +++++ src/Interfaces/ColorChannelInterface.php | 2 + src/Interfaces/ColorInterface.php | 11 ++-- tests/Colors/Cmyk/ParserTest.php | 32 ++++++++++ tests/Colors/ParserTest.php | 47 ++++++++++++++ tests/Colors/Rgb/ParserTest.php | 23 +++++++ tests/Colors/Rgba/ChannelTest.php | 19 ++++++ tests/Colors/Rgba/ParserTest.php | 23 +++++++ tests/Drivers/Gd/ColorTransformerTest.php | 30 +++++++++ .../Gd/Decoders/HexColorDecoderTest.php | 2 +- .../Gd/Decoders/HtmlColorNameDecoderTest.php | 2 +- .../Gd/Decoders/RgbStringColorDecoderTest.php | 17 ++---- .../Decoders/TransparentColorDecoderTest.php | 25 -------- .../Drivers/Imagick/ColorTransformerTest.php | 37 +++++++++++ .../Decoders/HtmlColorNameDecoderTest.php | 2 +- .../Decoders/RgbStringColorDecoderTest.php | 19 +++--- .../Decoders/TransparentColorDecoderTest.php | 25 -------- 40 files changed, 549 insertions(+), 226 deletions(-) create mode 100644 src/Colors/Cmyk/Parser.php create mode 100644 src/Colors/Parser.php delete mode 100644 src/Drivers/Gd/Color.php create mode 100644 src/Drivers/Gd/ColorTransformer.php rename src/Drivers/Gd/Decoders/{TransparentColorDecoder.php => ColorObjectDecoder.php} (65%) delete mode 100644 src/Drivers/Imagick/Color.php create mode 100644 src/Drivers/Imagick/ColorTransformer.php rename src/Drivers/Imagick/Decoders/{TransparentColorDecoder.php => ColorObjectDecoder.php} (66%) create mode 100644 src/Drivers/Imagick/Traits/CanReadColors.php create mode 100644 tests/Colors/Cmyk/ParserTest.php create mode 100644 tests/Colors/ParserTest.php create mode 100644 tests/Drivers/Gd/ColorTransformerTest.php delete mode 100644 tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php create mode 100644 tests/Drivers/Imagick/ColorTransformerTest.php delete mode 100644 tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php diff --git a/docker-compose.yml b/docker-compose.yml index e32993d79..8d90fbff8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: tests: build: ./ working_dir: /project - command: bash -c "composer install && ./vendor/bin/phpunit -vvv --filter=InputHandlerTest" + command: bash -c "composer install && ./vendor/bin/phpunit -vvv" volumes: - ./:/project analysis: diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php index cb40daac0..213fea38b 100644 --- a/src/Colors/Cmyk/Channels/Cyan.php +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -42,4 +42,14 @@ public function validate(mixed $value): mixed return $value; } + + public function toString(): string + { + return (string) $this->value(); + } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index 26b423ea1..74ee05400 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -91,7 +91,7 @@ public function toCmyk(): self return $this->convertTo(CmykColorspace::class); } - public function __toString(): string + public function toString(): string { return sprintf( 'cmyk(%d, %d, %d, %d)', @@ -101,4 +101,9 @@ public function __toString(): string $this->key()->value() ); } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Cmyk/Parser.php b/src/Colors/Cmyk/Parser.php new file mode 100644 index 000000000..15fb3a700 --- /dev/null +++ b/src/Colors/Cmyk/Parser.php @@ -0,0 +1,34 @@ +[0-9\.]+)%?, ?(?P[0-9\.]+)%?, ?(?P[0-9\.]+)%?, ?(?P[0-9\.]+)%?\)$/'; + $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + intval(round(floatval($matches['c']))), + intval(round(floatval($matches['m']))), + intval(round(floatval($matches['y']))), + intval(round(floatval($matches['k']))) + ); + } + + throw new ColorException('Unable to parse color'); + } +} diff --git a/src/Colors/Parser.php b/src/Colors/Parser.php new file mode 100644 index 000000000..edd2b2b43 --- /dev/null +++ b/src/Colors/Parser.php @@ -0,0 +1,28 @@ +value(); + } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 644b3936c..b630829ee 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -87,7 +87,7 @@ public function toRgba(): RgbaColor return $this->convertTo(RgbaColorspace::class); } - public function __toString(): string + public function toString(): string { return sprintf( 'rgb(%d, %d, %d)', @@ -96,4 +96,9 @@ public function __toString(): string $this->blue()->value() ); } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Rgb/Parser.php b/src/Colors/Rgb/Parser.php index be787daba..82aef1e61 100644 --- a/src/Colors/Rgb/Parser.php +++ b/src/Colors/Rgb/Parser.php @@ -6,6 +6,33 @@ class Parser { + public static function parse(mixed $value): Color + { + if (!is_string($value)) { + throw new ColorException('Unable to parse color'); + } + + try { + return static::fromHex($value); + } catch (ColorException $e) { + # move on + } + + try { + return static::fromString($value); + } catch (ColorException $e) { + # move on + } + + try { + return static::fromName($value); + } catch (ColorException $e) { + # move on + } + + throw new ColorException('Unable to parse color'); + } + public static function fromHex(string $input): Color { $pattern = '/^#?(?P[0-9a-f]{3}|[0-9a-f]{6})$/i'; @@ -30,14 +57,29 @@ public static function fromHex(string $input): Color public static function fromString(string $input): Color { - $pattern = '/^rgb\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/'; + // rgb(255, 255, 255) + $pattern = '/^s?rgb\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/'; $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + $matches['r'], + $matches['g'], + $matches['b'] + ); + } - if ($result !== 1) { - throw new ColorException('Unable to parse color'); + // rgb(100%, 100%, 100%) + $pattern = '/^s?rgb\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)$/'; + $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + intval(round(floatval($matches['r']) / 100 * 255)), + intval(round(floatval($matches['g']) / 100 * 255)), + intval(round(floatval($matches['b']) / 100 * 255)) + ); } - return new Color($matches['r'], $matches['g'], $matches['b']); + throw new ColorException('Unable to parse color'); } public static function fromName(string $input): Color diff --git a/src/Colors/Rgba/Channels/Alpha.php b/src/Colors/Rgba/Channels/Alpha.php index 46d3a1522..e69a437ed 100644 --- a/src/Colors/Rgba/Channels/Alpha.php +++ b/src/Colors/Rgba/Channels/Alpha.php @@ -4,5 +4,13 @@ class Alpha extends Red { - // + public function toString(): string + { + return strval(round($this->normalize(), 6)); + } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Rgba/Color.php b/src/Colors/Rgba/Color.php index 90e65827c..8bfdf2270 100644 --- a/src/Colors/Rgba/Color.php +++ b/src/Colors/Rgba/Color.php @@ -81,7 +81,7 @@ public function toCmyk(): CmykColor return $this->convertTo(CmykColorspace::class); } - public function __toString(): string + public function toString(): string { return sprintf( 'rgba(%d, %d, %d, %.1F)', @@ -91,4 +91,9 @@ public function __toString(): string $this->alpha()->normalize(), ); } + + public function __toString(): string + { + return $this->toString(); + } } diff --git a/src/Colors/Rgba/Parser.php b/src/Colors/Rgba/Parser.php index a567f70e9..efaa9f704 100644 --- a/src/Colors/Rgba/Parser.php +++ b/src/Colors/Rgba/Parser.php @@ -38,18 +38,31 @@ public static function fromHex(string $input): Color public static function fromString(string $input): Color { - $pattern = '/^rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; + // rgba(255, 255, 255, 1.0) + $pattern = '/^s?rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; $result = preg_match($pattern, $input, $matches); - if ($result !== 1) { - throw new ColorException('Unable to parse color'); + if ($result === 1) { + return new Color( + $matches['r'], + $matches['g'], + $matches['b'], + intval(round(floatval($matches['a']) * 255)) + ); } - return new Color( - $matches['r'], - $matches['g'], - $matches['b'], - intval(round(floatval($matches['a']) * 255)) - ); + // rgba(100%, 100%, 100%, 100%) + $pattern = '/s?rgba\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)/'; + $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + intval(round(floatval($matches['r']) / 100 * 255)), + intval(round(floatval($matches['g']) / 100 * 255)), + intval(round(floatval($matches['b']) / 100 * 255)), + intval(round(floatval($matches['a']) / 100 * 255)) + ); + } + + throw new ColorException('Unable to parse color'); } } diff --git a/src/Drivers/Gd/Color.php b/src/Drivers/Gd/Color.php deleted file mode 100644 index d4cde7209..000000000 --- a/src/Drivers/Gd/Color.php +++ /dev/null @@ -1,50 +0,0 @@ -toArray()[0]; - } - - public function green(): int - { - return $this->toArray()[1]; - } - - public function blue(): int - { - return $this->toArray()[2]; - } - - public function alpha(): float - { - return $this->toArray()[3]; - } - - public function toArray(): array - { - $a = ($this->value >> 24) & 0x7F; - $r = ($this->value >> 16) & 0xFF; - $g = ($this->value >> 8) & 0xFF; - $b = $this->value & 0xFF; - $a = (float) round(1 - $a / 127, 2); - - return [$r, $g, $b, $a]; - } - - public function toInt(): int - { - return $this->value; - } -} diff --git a/src/Drivers/Gd/ColorTransformer.php b/src/Drivers/Gd/ColorTransformer.php new file mode 100644 index 000000000..aac312e7c --- /dev/null +++ b/src/Drivers/Gd/ColorTransformer.php @@ -0,0 +1,61 @@ +> 24) & 0xFF; + $r = ($value >> 16) & 0xFF; + $g = ($value >> 8) & 0xFF; + $b = $value & 0xFF; + + // convert gd apha integer to intervention alpha integer + // ([opaque]0-127[transparent]) to ([opaque]255-0[transparent]) + $a = (int) static::convertRange($a, 127, 0, 0, 255); + + return new Color($r, $g, $b, $a); + } + + /** + * Transforms given color to the corresponding GD Library integer value + * + * @param ColorInterface $color + * @return int + */ + public static function colorToInteger(ColorInterface $color): int + { + $color = $color->toRgba(); + + $r = $color->red()->value(); + $g = $color->green()->value(); + $b = $color->blue()->value(); + $a = $color->alpha()->value(); + + // convert alpha value to gd alpha + // ([opaque]255-0[transparent]) to ([opaque]0-127[transparent]) + $a = (int) static::convertRange($a, 0, 255, 127, 0); + + return ($a << 24) + ($r << 16) + ($g << 8) + $b; + } + + private static function convertRange( + float|int $input, + float|int $min, + float|int $max, + float|int $targetMin, + float|int $targetMax + ): float|int { + return ceil(((($input - $min) * ($targetMax - $targetMin)) / ($max - $min)) + $targetMin); + } +} diff --git a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php b/src/Drivers/Gd/Decoders/ColorObjectDecoder.php similarity index 65% rename from src/Drivers/Gd/Decoders/TransparentColorDecoder.php rename to src/Drivers/Gd/Decoders/ColorObjectDecoder.php index 5cdd3d029..7c2e83a49 100644 --- a/src/Drivers/Gd/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Gd/Decoders/ColorObjectDecoder.php @@ -2,19 +2,20 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class TransparentColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class ColorObjectDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { - if (! is_string($input) || strtolower($input) !== 'transparent') { + if (! is_a($input, ColorInterface::class)) { throw new DecoderException('Unable to decode input'); } - return parent::decode([0, 0, 0, 0]); + return $input; } } diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index ad9b3754d..b572354bc 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -69,7 +69,7 @@ public function getHeight(): int 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 ColorTransformer::colorFromInteger(imagecolorat($frame->getCore(), $x, $y)); } return null; diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 42785e597..076269c8d 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -8,6 +8,7 @@ class InputHandler extends AbstractInputHandler { protected $decoders = [ Decoders\ImageObjectDecoder::class, + Decoders\ColorObjectDecoder::class, Decoders\FilePointerImageDecoder::class, Decoders\HtmlColorNameDecoder::class, Decoders\HexColorDecoder::class, diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 0f7473428..0d0ea3f3f 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\ColorTransformer; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ColorInterface; @@ -10,9 +11,11 @@ class FillModifier implements ModifierInterface { + protected $gd_color; + public function __construct(protected ColorInterface $color, protected ?Point $position = null) { - // + $this->gd_color = ColorTransformer::colorToInteger($color); } public function apply(ImageInterface $image): ImageInterface @@ -34,7 +37,7 @@ protected function floodFillWithColor(Frame $frame): void $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->color->toInt() + $this->gd_color ); } @@ -47,7 +50,7 @@ protected function fillAllWithColor(Frame $frame): void 0, $frame->getSize()->getWidth() - 1, $frame->getSize()->getHeight() - 1, - $this->color->toInt() + $this->gd_color ); } diff --git a/src/Drivers/Imagick/Color.php b/src/Drivers/Imagick/Color.php deleted file mode 100644 index 7503c7b1a..000000000 --- a/src/Drivers/Imagick/Color.php +++ /dev/null @@ -1,61 +0,0 @@ -pixel; - } - - public function red(): int - { - return intval(round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255)); - } - - public function green(): int - { - return intval(round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255)); - } - - public function blue(): int - { - return intval(round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255)); - } - - public function alpha(): float - { - return round($this->pixel->getColorValue(Imagick::COLOR_ALPHA), 2); - } - - public function toArray(): array - { - return [ - $this->red(), - $this->green(), - $this->blue(), - $this->alpha() - ]; - } - - public function toInt(): int - { - $r = $this->red(); - $g = $this->green(); - $b = $this->blue(); - $a = intval(round($this->alpha() * 255)); - - return intval(($a << 24) + ($r << 16) + ($g << 8) + $b); - } -} diff --git a/src/Drivers/Imagick/ColorTransformer.php b/src/Drivers/Imagick/ColorTransformer.php new file mode 100644 index 000000000..1ddf8a32d --- /dev/null +++ b/src/Drivers/Imagick/ColorTransformer.php @@ -0,0 +1,32 @@ +getColorAsString()); + } + + /** + * Transforms given color to the corresponding ImagickPixel + * + * @param ColorInterface $color + * @return ImagickPixel + */ + public static function colorToPixel(ColorInterface $color): ImagickPixel + { + return new ImagickPixel($color->toString()); + } +} diff --git a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php b/src/Drivers/Imagick/Decoders/ColorObjectDecoder.php similarity index 66% rename from src/Drivers/Imagick/Decoders/TransparentColorDecoder.php rename to src/Drivers/Imagick/Decoders/ColorObjectDecoder.php index 097a35ff4..426d2e52e 100644 --- a/src/Drivers/Imagick/Decoders/TransparentColorDecoder.php +++ b/src/Drivers/Imagick/Decoders/ColorObjectDecoder.php @@ -2,19 +2,20 @@ namespace Intervention\Image\Drivers\Imagick\Decoders; +use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -class TransparentColorDecoder extends RgbArrayColorDecoder implements DecoderInterface +class ColorObjectDecoder extends AbstractDecoder implements DecoderInterface { public function decode($input): ImageInterface|ColorInterface { - if (! is_string($input) || strtolower($input) !== 'transparent') { + if (! is_a($input, ColorInterface::class)) { throw new DecoderException('Unable to decode input'); } - return parent::decode([0, 0, 0, 0]); + return $input; } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 4dc8ca6a5..a6ac13e5a 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -124,7 +124,9 @@ public function getHeight(): int 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 ColorTransformer::colorFromPixel( + $frame->getCore()->getImagePixelColor($x, $y) + ); } return null; diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 0fc798ffb..d6ec81ef5 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -8,6 +8,7 @@ class InputHandler extends AbstractInputHandler { protected $decoders = [ Decoders\ImageObjectDecoder::class, + Decoders\ColorObjectDecoder::class, Decoders\FilePointerImageDecoder::class, Decoders\HtmlColorNameDecoder::class, Decoders\HexColorDecoder::class, diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index a6cc51a4d..36bef1af9 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -4,19 +4,23 @@ use Imagick; use ImagickDraw; -use Intervention\Image\Drivers\Imagick\Color; +use ImagickPixel; +use Intervention\Image\Drivers\Imagick\ColorTransformer; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Geometry\Point; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class FillModifier implements ModifierInterface { + protected ImagickPixel $pixel; + public function __construct( - protected Color $color, + protected ColorInterface $color, protected ?Point $position = null ) { - // + $this->pixel = ColorTransformer::colorToPixel($color); } public function apply(ImageInterface $image): ImageInterface @@ -40,7 +44,7 @@ protected function floodFillWithColor(Frame $frame): void ); $frame->getCore()->floodfillPaintImage( - $this->color->getPixel(), + $this->pixel, 100, $target, $this->position->getX(), @@ -53,7 +57,7 @@ protected function floodFillWithColor(Frame $frame): void protected function fillAllWithColor(Frame $frame): void { $draw = new ImagickDraw(); - $draw->setFillColor($this->color->getPixel()); + $draw->setFillColor($this->pixel); $draw->rectangle( 0, 0, diff --git a/src/Drivers/Imagick/Traits/CanReadColors.php b/src/Drivers/Imagick/Traits/CanReadColors.php new file mode 100644 index 000000000..aba65d761 --- /dev/null +++ b/src/Drivers/Imagick/Traits/CanReadColors.php @@ -0,0 +1,14 @@ +toString()); + } +} diff --git a/src/Interfaces/ColorChannelInterface.php b/src/Interfaces/ColorChannelInterface.php index 82de01da0..6c3c7ce86 100644 --- a/src/Interfaces/ColorChannelInterface.php +++ b/src/Interfaces/ColorChannelInterface.php @@ -9,4 +9,6 @@ public function normalize(int $precision = 32): float; public function validate(mixed $value): mixed; public function min(): int; public function max(): int; + public function toString(): string; + public function __toString(): string; } diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 1333e94be..46a4dab8f 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -8,14 +8,15 @@ interface ColorInterface { - // public function toRgb(): RgbColor; - // public function toRgba(): RgbaColor; - // public function toCmyk(): CmykColor; + public function toRgb(): RgbColor; + public function toRgba(): RgbaColor; + public function toCmyk(): CmykColor; + public function toArray(): array; + public function toString(): string; + public function __toString(): string; // public function channels(): array; // public function channel(string $classname): ColorChannelInterface; // public function colorspace(): ColorspaceInterface; - // public function toArray(): array; // public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; - // public function __toString(): string; } diff --git a/tests/Colors/Cmyk/ParserTest.php b/tests/Colors/Cmyk/ParserTest.php new file mode 100644 index 000000000..b94ec4f2f --- /dev/null +++ b/tests/Colors/Cmyk/ParserTest.php @@ -0,0 +1,32 @@ +assertInstanceOf(Color::class, $color); + $this->assertEquals([100, 0, 0, 0], $color->toArray()); + } + + public function testFromString(): void + { + $color = Parser::fromString('cmyk(100, 0, 0, 0)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([100, 0, 0, 0], $color->toArray()); + + $color = Parser::fromString('cmyk(100%, 0%, 0%, 0%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([100, 0, 0, 0], $color->toArray()); + } +} diff --git a/tests/Colors/ParserTest.php b/tests/Colors/ParserTest.php new file mode 100644 index 000000000..a783f62a1 --- /dev/null +++ b/tests/Colors/ParserTest.php @@ -0,0 +1,47 @@ +assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::parse('rgb(204, 204, 204)'); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::parse('rgba(204, 204, 204, 1)'); + $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::parse('cccc'); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::parse('cccccccc'); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([204, 204, 204, 204], $color->toArray()); + + $color = Parser::parse('salmon'); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals('fa8072', $color->toHex()); + + $color = Parser::parse('cmyk(100, 100, 0,0)'); + $this->assertInstanceOf(CmykColor::class, $color); + $this->assertEquals([100, 100, 0, 0], $color->toArray()); + } +} diff --git a/tests/Colors/Rgb/ParserTest.php b/tests/Colors/Rgb/ParserTest.php index 08389cbf6..b6ccce7d9 100644 --- a/tests/Colors/Rgb/ParserTest.php +++ b/tests/Colors/Rgb/ParserTest.php @@ -13,6 +13,21 @@ */ class ParserTest extends TestCase { + public function testParse(): void + { + $color = Parser::parse('ccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::parse('rgb(204, 204, 204)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204], $color->toArray()); + + $color = Parser::parse('salmon'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('fa8072', $color->toHex()); + } + public function testFromHex(): void { $color = Parser::fromHex('ccc'); @@ -41,6 +56,14 @@ public function testFromString(): void $this->assertInstanceOf(Color::class, $color); $this->assertEquals([204, 204, 204], $color->toArray()); + $color = Parser::fromString('rgb(100%,20%,25%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([255, 51, 64], $color->toArray()); + + $color = Parser::fromString('rgb(100%,74.8064%,25.2497%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([255, 191, 64], $color->toArray()); + $this->expectException(ColorException::class); (new Parser())->fromString('rgb(204,204,204,1)'); diff --git a/tests/Colors/Rgba/ChannelTest.php b/tests/Colors/Rgba/ChannelTest.php index a1144e40e..3edcca170 100644 --- a/tests/Colors/Rgba/ChannelTest.php +++ b/tests/Colors/Rgba/ChannelTest.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Tests\Colors\Rgba; use Intervention\Image\Colors\Rgba\Channels\Red as Channel; +use Intervention\Image\Colors\Rgba\Channels\Alpha as Alpha; use Intervention\Image\Exceptions\ColorException; use Intervention\Image\Tests\TestCase; @@ -45,4 +46,22 @@ public function testValidate(): void $this->expectException(ColorException::class); new Channel(-1); } + + public function testToString(): void + { + $channel = new Channel(255); + $this->assertEquals("255", $channel->toString()); + + $channel = new Alpha(0); + $this->assertEquals("0", $channel->toString()); + + $channel = new Alpha(51); + $this->assertEquals("0.2", $channel->toString()); + + $channel = new Alpha(255); + $this->assertEquals("1", $channel->toString()); + + $channel = new Alpha(170); + $this->assertEquals("0.666667", $channel->toString()); + } } diff --git a/tests/Colors/Rgba/ParserTest.php b/tests/Colors/Rgba/ParserTest.php index 0986c797d..89441646b 100644 --- a/tests/Colors/Rgba/ParserTest.php +++ b/tests/Colors/Rgba/ParserTest.php @@ -13,6 +13,21 @@ */ class ParserTest extends TestCase { + public function testParse(): void + { + $color = Parser::parse('ccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::parse('rgba(204, 204, 204, 1)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Parser::parse('salmon'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals('fa8072', $color->toHex()); + } + public function testFromHex(): void { $color = Parser::fromHex('ccc'); @@ -62,6 +77,14 @@ public function testFromString(): void $this->assertInstanceOf(Color::class, $color); $this->assertEquals([204, 204, 204, 51], $color->toArray()); + $color = Parser::fromString('rgba(100%,20%,25%,100%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([255, 51, 64, 255], $color->toArray()); + + $color = Parser::fromString('rgba(100%,74.8064%,25.2497%,100%)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([255, 191, 64, 255], $color->toArray()); + $this->expectException(ColorException::class); $color = Parser::fromString('rgba(204, 204, 204, 1.2)'); } diff --git a/tests/Drivers/Gd/ColorTransformerTest.php b/tests/Drivers/Gd/ColorTransformerTest.php new file mode 100644 index 000000000..24a3fa7ca --- /dev/null +++ b/tests/Drivers/Gd/ColorTransformerTest.php @@ -0,0 +1,30 @@ +assertInstanceOf(Color::class, $result); + $this->assertEquals([181, 55, 23, 155], $result->toArray()); + + $result = ColorTransformer::colorFromInteger(16777215); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 255, 255, 255], $result->toArray()); + } + + public function testToInteger(): void + { + $result = ColorTransformer::colorToInteger(new Color(181, 55, 23, 155)); + $this->assertEquals(850736919, $result); + + $result = ColorTransformer::colorToInteger(new Color(255, 255, 255, 255)); + $this->assertEquals(16777215, $result); + } +} diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php index 179f46208..f78468abd 100644 --- a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Decoders\HexColorDecoder; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php index 8b7135997..1113c7844 100644 --- a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Decoders\HtmlColorNameDecoder; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php index 45537a27a..fa73370c7 100644 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -2,7 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; @@ -16,21 +17,15 @@ public function testDecodeRgb(): void { $decoder = new RgbStringColorDecoder(); $color = $decoder->decode('rgb(181, 55, 23)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(1, $color->alpha()); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([181, 55, 23], $color->toArray()); } public function testDecodeRgba(): void { $decoder = new RgbStringColorDecoder(); $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(.5, $color->alpha()); + $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertEquals([181, 55, 23, 51], $color->toArray()); } } diff --git a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php deleted file mode 100644 index f680a367e..000000000 --- a/tests/Drivers/Gd/Decoders/TransparentColorDecoderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -decode('transparent'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->red()); - $this->assertEquals(0, $color->green()); - $this->assertEquals(0, $color->blue()); - $this->assertEquals(0, $color->alpha()); - } -} diff --git a/tests/Drivers/Imagick/ColorTransformerTest.php b/tests/Drivers/Imagick/ColorTransformerTest.php new file mode 100644 index 000000000..fa71db901 --- /dev/null +++ b/tests/Drivers/Imagick/ColorTransformerTest.php @@ -0,0 +1,37 @@ +assertInstanceOf(Color::class, $result); + $this->assertEquals([181, 55, 23, 153], $result->toArray()); + + $result = ColorTransformer::colorFromPixel(new ImagickPixel('rgba(255, 255, 255, 1)')); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 255, 255, 255], $result->toArray()); + } + + public function testToPixel(): void + { + $result = ColorTransformer::colorToPixel(new Color(181, 55, 23, 153)); + $this->assertInstanceOf(ImagickPixel::class, $result); + $this->assertEquals('srgba(181,55,23,0.6)', $result->getColorAsString()); + + $result = ColorTransformer::colorToPixel(new Color(255, 255, 255, 255)); + $this->assertInstanceOf(ImagickPixel::class, $result); + $this->assertEquals('srgba(255,255,255,1)', $result->getColorAsString()); + + $result = ColorTransformer::colorToPixel(new Color(255, 255, 255, 170)); + $this->assertInstanceOf(ImagickPixel::class, $result); + $this->assertEquals('srgba(255,255,255,0.699992)', $result->getColorAsString()); + } +} diff --git a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php index 4328f4b12..ba9790f6f 100644 --- a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Decoders; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\Decoders\HtmlColorNameDecoder; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php index c3b6a52a2..3029bc027 100644 --- a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php @@ -2,7 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Decoders; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Imagick\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; @@ -16,21 +17,15 @@ public function testDecodeRgb(): void { $decoder = new RgbStringColorDecoder(); $color = $decoder->decode('rgb(181, 55, 23)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(1, $color->alpha()); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals([181, 55, 23], $color->toArray()); } public function testDecodeRgba(): void { $decoder = new RgbStringColorDecoder(); - $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(181, $color->red()); - $this->assertEquals(55, $color->green()); - $this->assertEquals(23, $color->blue()); - $this->assertEquals(.5, $color->alpha()); + $color = $decoder->decode('rgba(181, 55, 23, 0.2)'); + $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertEquals([181, 55, 23, 51], $color->toArray()); } } diff --git a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php deleted file mode 100644 index 011947ad7..000000000 --- a/tests/Drivers/Imagick/Decoders/TransparentColorDecoderTest.php +++ /dev/null @@ -1,25 +0,0 @@ -decode('transparent'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->red()); - $this->assertEquals(0, $color->green()); - $this->assertEquals(0, $color->blue()); - $this->assertEquals(0, $color->alpha()); - } -} From d95120153f9866b05de8e1dffbbb4e7b72ecd176 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 16 Oct 2023 17:28:04 +0200 Subject: [PATCH 0441/1667] Remove Rgba color space RGB colors with alpha channel are now handles by RGB. --- src/Colors/Cmyk/Color.php | 7 -- src/Colors/Cmyk/Colorspace.php | 3 +- src/Colors/Parser.php | 1 - src/Colors/{Rgba => Rgb}/Channels/Alpha.php | 2 +- src/Colors/Rgb/Color.php | 44 +++++-- src/Colors/Rgb/Colorspace.php | 11 -- src/Colors/Rgb/Parser.php | 51 ++++++++ src/Colors/Rgba/Channels/Blue.php | 10 -- src/Colors/Rgba/Channels/Green.php | 10 -- src/Colors/Rgba/Channels/Red.php | 10 -- src/Colors/Rgba/Color.php | 99 --------------- src/Colors/Rgba/Colorspace.php | 55 -------- src/Colors/Rgba/Parser.php | 68 ---------- src/Drivers/Gd/ColorTransformer.php | 6 +- src/Interfaces/ColorInterface.php | 2 - tests/Colors/Cmyk/ColorTest.php | 9 -- tests/Colors/ParserTest.php | 3 +- tests/Colors/Rgb/ColorTest.php | 9 -- tests/Colors/Rgb/ParserTest.php | 3 - tests/Colors/Rgba/ChannelTest.php | 67 ---------- tests/Colors/Rgba/ColorTest.php | 119 ------------------ tests/Colors/Rgba/ColorspaceTest.php | 26 ---- tests/Colors/Rgba/ParserTest.php | 91 -------------- tests/Drivers/Gd/ColorTransformerTest.php | 2 +- .../Gd/Decoders/RgbStringColorDecoderTest.php | 3 +- tests/Drivers/Gd/InputHandlerTest.php | 7 +- .../Drivers/Imagick/ColorTransformerTest.php | 2 +- tests/Drivers/Imagick/InputHandlerTest.php | 7 +- 28 files changed, 101 insertions(+), 626 deletions(-) rename src/Colors/{Rgba => Rgb}/Channels/Alpha.php (81%) delete mode 100644 src/Colors/Rgba/Channels/Blue.php delete mode 100644 src/Colors/Rgba/Channels/Green.php delete mode 100644 src/Colors/Rgba/Channels/Red.php delete mode 100644 src/Colors/Rgba/Color.php delete mode 100644 src/Colors/Rgba/Colorspace.php delete mode 100644 src/Colors/Rgba/Parser.php delete mode 100644 tests/Colors/Rgba/ChannelTest.php delete mode 100644 tests/Colors/Rgba/ColorTest.php delete mode 100644 tests/Colors/Rgba/ColorspaceTest.php delete mode 100644 tests/Colors/Rgba/ParserTest.php diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index 74ee05400..bf6cb401e 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -9,8 +9,6 @@ use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; -use Intervention\Image\Colors\Rgba\Colorspace as RgbaColorspace; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Traits\CanHandleChannels; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -81,11 +79,6 @@ public function toRgb(): RgbColor return $this->convertTo(RgbColorspace::class); } - public function toRgba(): RgbaColor - { - return $this->convertTo(RgbaColorspace::class); - } - public function toCmyk(): self { return $this->convertTo(CmykColorspace::class); diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php index 69c6c82ad..76ce8ea33 100644 --- a/src/Colors/Cmyk/Colorspace.php +++ b/src/Colors/Cmyk/Colorspace.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Colors\Cmyk; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -18,7 +17,7 @@ class Colorspace implements ColorspaceInterface public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { - RgbColor::class, RgbaColor::class => $this->convertRgbColor($color), + RgbColor::class => $this->convertRgbColor($color), default => $color, }; } diff --git a/src/Colors/Parser.php b/src/Colors/Parser.php index edd2b2b43..dcf91c591 100644 --- a/src/Colors/Parser.php +++ b/src/Colors/Parser.php @@ -9,7 +9,6 @@ class Parser { protected static $parsers = [ Rgb\Parser::class, - Rgba\Parser::class, Cmyk\Parser::class, ]; diff --git a/src/Colors/Rgba/Channels/Alpha.php b/src/Colors/Rgb/Channels/Alpha.php similarity index 81% rename from src/Colors/Rgba/Channels/Alpha.php rename to src/Colors/Rgb/Channels/Alpha.php index e69a437ed..7172791fc 100644 --- a/src/Colors/Rgba/Channels/Alpha.php +++ b/src/Colors/Rgb/Channels/Alpha.php @@ -1,6 +1,6 @@ channels = [ new Red($r), new Green($g), new Blue($b), + new Alpha($a), ]; } @@ -44,6 +44,11 @@ public function blue(): Blue return $this->channel(Blue::class); } + public function alpha(): Alpha + { + return $this->channel(Alpha::class); + } + public function toArray(): array { return array_map(function (ColorChannelInterface $channel) { @@ -53,12 +58,23 @@ public function toArray(): array public function toHex(string $prefix = ''): string { + if ($this->isFullyOpaque()) { + return sprintf( + '%s%02x%02x%02x', + $prefix, + $this->red()->value(), + $this->green()->value(), + $this->blue()->value() + ); + } + return sprintf( - '%s%02x%02x%02x', + '%s%02x%02x%02x%02x', $prefix, $this->red()->value(), $this->green()->value(), - $this->blue()->value() + $this->blue()->value(), + $this->alpha()->value() ); } @@ -82,18 +98,28 @@ public function toCmyk(): CmykColor return $this->convertTo(CmykColorspace::class); } - public function toRgba(): RgbaColor + public function isFullyOpaque(): bool { - return $this->convertTo(RgbaColorspace::class); + return $this->alpha()->value() === 255; } public function toString(): string { + if ($this->isFullyOpaque()) { + return sprintf( + 'rgb(%d, %d, %d)', + $this->red()->value(), + $this->green()->value(), + $this->blue()->value() + ); + } + return sprintf( - 'rgb(%d, %d, %d)', + 'rgba(%d, %d, %d, %.1F)', $this->red()->value(), $this->green()->value(), - $this->blue()->value() + $this->blue()->value(), + $this->alpha()->normalize(), ); } diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index 489df51d8..4c0bcb0f2 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Colors\Rgb; use Intervention\Image\Colors\Cmyk\Color as CmykColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -13,7 +12,6 @@ public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { CmykColor::class => $this->convertCmykColor($color), - RgbaColor::class => $this->convertRgbaColor($color), default => $color, }; } @@ -26,13 +24,4 @@ protected function convertCmykColor(CmykColor $color): Color (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), ); } - - protected function convertRgbaColor(RgbaColor $color): Color - { - return new Color( - $color->red()->value(), - $color->green()->value(), - $color->blue()->value() - ); - } } diff --git a/src/Colors/Rgb/Parser.php b/src/Colors/Rgb/Parser.php index 82aef1e61..746a40d61 100644 --- a/src/Colors/Rgb/Parser.php +++ b/src/Colors/Rgb/Parser.php @@ -35,6 +35,7 @@ public static function parse(mixed $value): Color public static function fromHex(string $input): Color { + // Hexadecimal colors $pattern = '/^#?(?P[0-9a-f]{3}|[0-9a-f]{6})$/i'; $result = preg_match($pattern, $input, $matches); @@ -48,10 +49,35 @@ public static function fromHex(string $input): Color default => throw new ColorException('Unable to parse color'), }; + try { + return new Color( + strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), + strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), + strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]) + ); + } catch (ColorException $e) { + # move on + } + + // Hexadecimal colors with transparency + $pattern = '/^#?(?P[0-9a-f]{4}|[0-9a-f]{8})$/i'; + $result = preg_match($pattern, $input, $matches); + + if ($result !== 1) { + throw new ColorException('Unable to parse color'); + } + + $matches = match (strlen($matches['hex'])) { + 4 => str_split($matches['hex']), + 8 => str_split($matches['hex'], 2), + default => throw new ColorException('Unable to parse color'), + }; + return new Color( strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), + strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), ); } @@ -79,6 +105,31 @@ public static function fromString(string $input): Color ); } + // rgba(255, 255, 255, 1.0) + $pattern = '/^s?rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; + $result = preg_match($pattern, $input, $matches); + + if ($result === 1) { + return new Color( + $matches['r'], + $matches['g'], + $matches['b'], + intval(round(floatval($matches['a']) * 255)) + ); + } + + // rgba(100%, 100%, 100%, 100%) + $pattern = '/s?rgba\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)/'; + $result = preg_match($pattern, $input, $matches); + if ($result === 1) { + return new Color( + intval(round(floatval($matches['r']) / 100 * 255)), + intval(round(floatval($matches['g']) / 100 * 255)), + intval(round(floatval($matches['b']) / 100 * 255)), + intval(round(floatval($matches['a']) / 100 * 255)) + ); + } + throw new ColorException('Unable to parse color'); } diff --git a/src/Colors/Rgba/Channels/Blue.php b/src/Colors/Rgba/Channels/Blue.php deleted file mode 100644 index 54b9d5bc8..000000000 --- a/src/Colors/Rgba/Channels/Blue.php +++ /dev/null @@ -1,10 +0,0 @@ -channels = [ - new Red($r), - new Green($g), - new Blue($b), - new Alpha($a), - ]; - } - - public function red(): Red - { - return $this->channel(Red::class); - } - - public function green(): Green - { - return $this->channel(Green::class); - } - - public function blue(): Blue - { - return $this->channel(Blue::class); - } - - public function alpha(): Alpha - { - return $this->channel(Alpha::class); - } - - public function isFullyOpaque(): bool - { - return $this->alpha()->value() === 255; - } - - public function toHex(string $prefix = ''): string - { - if ($this->isFullyOpaque()) { - return parent::toHex($prefix); - } - - return sprintf( - '%s%02x%02x%02x%02x', - $prefix, - $this->red()->value(), - $this->green()->value(), - $this->blue()->value(), - $this->alpha()->value() - ); - } - - public function toRgb(): RgbColor - { - return $this->convertTo(RgbColorspace::class); - } - - public function toRgba(): self - { - return $this; - } - - public function toCmyk(): CmykColor - { - return $this->convertTo(CmykColorspace::class); - } - - public function toString(): string - { - return sprintf( - 'rgba(%d, %d, %d, %.1F)', - $this->red()->value(), - $this->green()->value(), - $this->blue()->value(), - $this->alpha()->normalize(), - ); - } - - public function __toString(): string - { - return $this->toString(); - } -} diff --git a/src/Colors/Rgba/Colorspace.php b/src/Colors/Rgba/Colorspace.php deleted file mode 100644 index 28b53f83f..000000000 --- a/src/Colors/Rgba/Colorspace.php +++ /dev/null @@ -1,55 +0,0 @@ - $this->convertCmykColor($color), - RgbColor::class => $this->convertRgbColor($color), - default => $color, - }; - } - - /** - * Convert given color to the RGBA colorspace - * - * @param RgbColor $color - * @return Color - */ - protected function convertRgbColor(RgbColor $color): Color - { - return new Color( - $color->red()->value(), - $color->green()->value(), - $color->blue()->value(), - 255 - ); - } - - /** - * Convert given color to the RGBA colorspace - * - * @param CmykColor $color - * @return Color - */ - protected function convertCmykColor(CmykColor $color): Color - { - return new Color( - (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), - (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), - (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), - 255 - ); - } -} diff --git a/src/Colors/Rgba/Parser.php b/src/Colors/Rgba/Parser.php deleted file mode 100644 index efaa9f704..000000000 --- a/src/Colors/Rgba/Parser.php +++ /dev/null @@ -1,68 +0,0 @@ -toRgba(); - } catch (ColorException $e) { - // move on - } - - $pattern = '/^#?(?P[0-9a-f]{4}|[0-9a-f]{8})$/i'; - $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new ColorException('Unable to parse color'); - } - - $matches = match (strlen($matches['hex'])) { - 4 => str_split($matches['hex']), - 8 => str_split($matches['hex'], 2), - default => throw new ColorException('Unable to parse color'), - }; - - return new Color( - strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), - strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), - ); - } - - public static function fromString(string $input): Color - { - // rgba(255, 255, 255, 1.0) - $pattern = '/^s?rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; - $result = preg_match($pattern, $input, $matches); - - if ($result === 1) { - return new Color( - $matches['r'], - $matches['g'], - $matches['b'], - intval(round(floatval($matches['a']) * 255)) - ); - } - - // rgba(100%, 100%, 100%, 100%) - $pattern = '/s?rgba\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - intval(round(floatval($matches['r']) / 100 * 255)), - intval(round(floatval($matches['g']) / 100 * 255)), - intval(round(floatval($matches['b']) / 100 * 255)), - intval(round(floatval($matches['a']) / 100 * 255)) - ); - } - - throw new ColorException('Unable to parse color'); - } -} diff --git a/src/Drivers/Gd/ColorTransformer.php b/src/Drivers/Gd/ColorTransformer.php index aac312e7c..62d9c8bde 100644 --- a/src/Drivers/Gd/ColorTransformer.php +++ b/src/Drivers/Gd/ColorTransformer.php @@ -3,12 +3,12 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Colors\Rgba\Color; +use Intervention\Image\Colors\Rgb\Color; class ColorTransformer { /** - * Transforms GD Library integer color value to RGBA color object + * Transforms GD Library integer color value to RGB color object * * @param int $value * @return Color @@ -35,7 +35,7 @@ public static function colorFromInteger(int $value): Color */ public static function colorToInteger(ColorInterface $color): int { - $color = $color->toRgba(); + $color = $color->toRgb(); $r = $color->red()->value(); $g = $color->green()->value(); diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 46a4dab8f..7cf183172 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -4,12 +4,10 @@ use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; interface ColorInterface { public function toRgb(): RgbColor; - public function toRgba(): RgbaColor; public function toCmyk(): CmykColor; public function toArray(): array; public function toString(): string; diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php index 2052480f8..66e872839 100644 --- a/tests/Colors/Cmyk/ColorTest.php +++ b/tests/Colors/Cmyk/ColorTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Colors\Cmyk; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Cmyk\Channels\Cyan; use Intervention\Image\Colors\Cmyk\Channels\Key; use Intervention\Image\Colors\Cmyk\Channels\Magenta; @@ -83,12 +82,4 @@ public function testToRgb(): void $this->assertInstanceOf(RgbColor::class, $converted); $this->assertEquals([255, 204, 204], $converted->toArray()); } - - public function testToRgba(): void - { - $color = new Color(0, 20, 20, 0); - $converted = $color->toRgba(); - $this->assertInstanceOf(RgbaColor::class, $converted); - $this->assertEquals([255, 204, 204, 255], $converted->toArray()); - } } diff --git a/tests/Colors/ParserTest.php b/tests/Colors/ParserTest.php index a783f62a1..4e158140c 100644 --- a/tests/Colors/ParserTest.php +++ b/tests/Colors/ParserTest.php @@ -4,7 +4,6 @@ use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Parser; use Intervention\Image\Tests\TestCase; @@ -25,7 +24,7 @@ public function testParse(): void $this->assertEquals([204, 204, 204], $color->toArray()); $color = Parser::parse('rgba(204, 204, 204, 1)'); - $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertInstanceOf(RgbColor::class, $color); $this->assertEquals([204, 204, 204, 255], $color->toArray()); $color = Parser::parse('cccc'); diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index 5c542fa77..8121a47f0 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Colors\Rgb; use Intervention\Image\Colors\Cmyk\Color as CmykColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Blue; @@ -111,12 +110,4 @@ public function testToRgb(): void $color = new Color(181, 55, 23); $this->assertInstanceOf(Color::class, $color->toRgb()); } - - public function testToRgba(): void - { - $color = new Color(181, 55, 23); - $converted = $color->toRgba(); - $this->assertInstanceOf(RgbaColor::class, $converted); - $this->assertEquals([181, 55, 23, 255], $converted->toArray()); - } } diff --git a/tests/Colors/Rgb/ParserTest.php b/tests/Colors/Rgb/ParserTest.php index b6ccce7d9..d98b8601a 100644 --- a/tests/Colors/Rgb/ParserTest.php +++ b/tests/Colors/Rgb/ParserTest.php @@ -69,9 +69,6 @@ public function testFromString(): void $this->expectException(ColorException::class); (new Parser())->fromString('rgb(120)'); - - $this->expectException(ColorException::class); - (new Parser())->fromString('rgba(204,204,204,1)'); } public function testFromName(): void diff --git a/tests/Colors/Rgba/ChannelTest.php b/tests/Colors/Rgba/ChannelTest.php deleted file mode 100644 index 3edcca170..000000000 --- a/tests/Colors/Rgba/ChannelTest.php +++ /dev/null @@ -1,67 +0,0 @@ -assertInstanceOf(Channel::class, $channel); - } - - public function testValue(): void - { - $channel = new Channel(10); - $this->assertEquals(10, $channel->value()); - } - - public function testNormalize(): void - { - $channel = new Channel(255); - $this->assertEquals(1, $channel->normalize()); - $channel = new Channel(0); - $this->assertEquals(0, $channel->normalize()); - $channel = new Channel(51); - $this->assertEquals(.2, $channel->normalize()); - } - - public function testValidate(): void - { - $this->expectException(ColorException::class); - new Channel(256); - - $this->expectException(ColorException::class); - new Channel(-1); - } - - public function testToString(): void - { - $channel = new Channel(255); - $this->assertEquals("255", $channel->toString()); - - $channel = new Alpha(0); - $this->assertEquals("0", $channel->toString()); - - $channel = new Alpha(51); - $this->assertEquals("0.2", $channel->toString()); - - $channel = new Alpha(255); - $this->assertEquals("1", $channel->toString()); - - $channel = new Alpha(170); - $this->assertEquals("0.666667", $channel->toString()); - } -} diff --git a/tests/Colors/Rgba/ColorTest.php b/tests/Colors/Rgba/ColorTest.php deleted file mode 100644 index cf9b9e612..000000000 --- a/tests/Colors/Rgba/ColorTest.php +++ /dev/null @@ -1,119 +0,0 @@ -assertInstanceOf(Color::class, $color); - } - - public function testChannels(): void - { - $color = new Color(10, 20, 30, 255); - $this->assertIsArray($color->channels()); - $this->assertCount(4, $color->channels()); - } - - public function testChannel(): void - { - $color = new Color(10, 20, 30, 255); - $channel = $color->channel(Red::class); - $this->assertInstanceOf(Red::class, $channel); - $this->assertEquals(10, $channel->value()); - $channel = $color->channel(Alpha::class); - $this->assertInstanceOf(Alpha::class, $channel); - $this->assertEquals(255, $channel->value()); - } - - public function testRedGreenBlueAlpha(): void - { - $color = new Color(10, 20, 30, 255); - $this->assertInstanceOf(Red::class, $color->red()); - $this->assertInstanceOf(Green::class, $color->green()); - $this->assertInstanceOf(Blue::class, $color->blue()); - $this->assertInstanceOf(Alpha::class, $color->alpha()); - $this->assertEquals(10, $color->red()->value()); - $this->assertEquals(20, $color->green()->value()); - $this->assertEquals(30, $color->blue()->value()); - $this->assertEquals(255, $color->alpha()->value()); - } - - public function testToArray(): void - { - $color = new Color(10, 20, 30, 255); - $this->assertEquals([10, 20, 30, 255], $color->toArray()); - } - - public function testToHex(): void - { - $color = new Color(181, 55, 23, 0); - $this->assertEquals('b5371700', $color->toHex()); - $this->assertEquals('#b5371700', $color->toHex('#')); - - $color = new Color(181, 55, 23, 255); - $this->assertEquals('b53717', $color->toHex()); - - $color = new Color(181, 55, 23, 204); - $this->assertEquals('b53717cc', $color->toHex()); - } - - public function testNormalize(): void - { - $color = new Color(255, 0, 51, 255); - $this->assertEquals([1.0, 0.0, 0.2, 1.0], $color->normalize()); - $color = new Color(255, 0, 51, 51); - $this->assertEquals([1.0, 0.0, 0.2, 0.2], $color->normalize()); - } - - public function testToString(): void - { - $color = new Color(255, 255, 255, 255); - $this->assertEquals('rgba(255, 255, 255, 1.0)', (string) $color); - - $color = new Color(10, 20, 30, 85); - $this->assertEquals('rgba(10, 20, 30, 0.3)', (string) $color); - } - - public function testToRgba(): void - { - $color = new Color(181, 55, 23, 120); - $converted = $color->toRgba(); - $this->assertInstanceOf(Color::class, $converted); - } - - public function testToRgb(): void - { - $color = new Color(181, 55, 23, 120); - $converted = $color->toRgb(); - $this->assertInstanceOf(RgbColor::class, $converted); - $this->assertEquals([181, 55, 23], $converted->toArray()); - } - - public function testToCmyk(): void - { - $color = new Color(0, 0, 0, 255); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 0, 100], $converted->toArray()); - - $color = new Color(0, 0, 0, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 0, 100], $converted->toArray()); - } -} diff --git a/tests/Colors/Rgba/ColorspaceTest.php b/tests/Colors/Rgba/ColorspaceTest.php deleted file mode 100644 index 2ff88b0f6..000000000 --- a/tests/Colors/Rgba/ColorspaceTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertInstanceOf( - RgbaColor::class, - $colorspace->convertColor( - new CmykColor(0, 0, 0, 0) - ) - ); - } -} diff --git a/tests/Colors/Rgba/ParserTest.php b/tests/Colors/Rgba/ParserTest.php deleted file mode 100644 index 89441646b..000000000 --- a/tests/Colors/Rgba/ParserTest.php +++ /dev/null @@ -1,91 +0,0 @@ -assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::parse('rgba(204, 204, 204, 1)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::parse('salmon'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('fa8072', $color->toHex()); - } - - public function testFromHex(): void - { - $color = Parser::fromHex('ccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromHex('cccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromHex('#cccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromHex('#cccccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('#cccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('cccccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('cccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - } - - public function testFromString(): void - { - $color = Parser::fromString('rgba(204, 204, 204, 1)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromString('rgba(204,204,204,1.0)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::fromString('rgba(204,204,204,0.2)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 51], $color->toArray()); - - $color = Parser::fromString('rgba(204,204, 204, .2)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204, 51], $color->toArray()); - - $color = Parser::fromString('rgba(100%,20%,25%,100%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([255, 51, 64, 255], $color->toArray()); - - $color = Parser::fromString('rgba(100%,74.8064%,25.2497%,100%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([255, 191, 64, 255], $color->toArray()); - - $this->expectException(ColorException::class); - $color = Parser::fromString('rgba(204, 204, 204, 1.2)'); - } -} diff --git a/tests/Drivers/Gd/ColorTransformerTest.php b/tests/Drivers/Gd/ColorTransformerTest.php index 24a3fa7ca..04260235a 100644 --- a/tests/Drivers/Gd/ColorTransformerTest.php +++ b/tests/Drivers/Gd/ColorTransformerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Colors\Rgba\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\ColorTransformer; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php index fa73370c7..fb19fb98c 100644 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Drivers\Gd\Decoders; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Gd\Decoders\RgbStringColorDecoder; use Intervention\Image\Tests\TestCase; @@ -25,7 +24,7 @@ public function testDecodeRgba(): void { $decoder = new RgbStringColorDecoder(); $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); - $this->assertInstanceOf(RgbaColor::class, $color); + $this->assertInstanceOf(RgbColor::class, $color); $this->assertEquals([181, 55, 23, 51], $color->toArray()); } } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 8157f01ad..3cdaaee29 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -83,13 +82,13 @@ public function testHandleHexColor(): void $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -102,7 +101,7 @@ public function testHandleRgbString(): void $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } diff --git a/tests/Drivers/Imagick/ColorTransformerTest.php b/tests/Drivers/Imagick/ColorTransformerTest.php index fa71db901..49f16ad15 100644 --- a/tests/Drivers/Imagick/ColorTransformerTest.php +++ b/tests/Drivers/Imagick/ColorTransformerTest.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick; use ImagickPixel; -use Intervention\Image\Colors\Rgba\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\ColorTransformer; use Intervention\Image\Tests\TestCase; diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index f125f686e..e7ce923e4 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Tests\Drivers\Imagick; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Colors\Rgba\Color as RgbaColor; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -83,13 +82,13 @@ public function testHandleHexColor(): void $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -102,7 +101,7 @@ public function testHandleRgbString(): void $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbaColor::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } } From 6445957646da8174d3c5f8aec73f4d20a22206f1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 17 Oct 2023 17:08:45 +0200 Subject: [PATCH 0442/1667] Move color parsers to decoder classes --- src/Colors/Cmyk/Color.php | 14 + .../Cmyk/Decoders/StringColorDecoder.php | 37 +++ src/Colors/Cmyk/Parser.php | 34 --- src/Colors/Parser.php | 27 -- src/Colors/Rgb/Color.php | 7 + src/Colors/Rgb/Decoders/HexColorDecoder.php | 48 +++ .../Rgb/Decoders/HtmlColornameDecoder.php | 172 +++++++++++ .../Rgb/Decoders/StringColorDecoder.php | 49 +++ src/Colors/Rgb/Parser.php | 286 ------------------ src/Drivers/Gd/Decoders/HexColorDecoder.php | 36 --- .../Gd/Decoders/HtmlColorNameDecoder.php | 28 -- .../Gd/Decoders/RgbStringColorDecoder.php | 36 --- src/Drivers/Gd/Image.php | 7 +- src/Drivers/Gd/InputHandler.php | 32 +- .../Gd/Modifiers/DrawPixelModifier.php | 4 +- src/Drivers/Gd/Modifiers/FillModifier.php | 20 +- .../CanHandleColors.php} | 21 +- .../Imagick/Decoders/HexColorDecoder.php | 36 --- .../Imagick/Decoders/HtmlColorNameDecoder.php | 27 -- .../Decoders/RgbStringColorDecoder.php | 36 --- src/Drivers/Imagick/Image.php | 5 +- src/Drivers/Imagick/InputHandler.php | 32 +- .../Imagick/Modifiers/DrawPixelModifier.php | 10 +- .../Imagick/Modifiers/FillModifier.php | 19 +- .../CanHandleColors.php} | 14 +- src/Interfaces/ColorInterface.php | 9 +- src/Traits/CanCheckType.php | 9 + tests/Colors/Cmyk/ColorTest.php | 2 +- .../Cmyk/Decoders/StringColorDecoderTest.php | 35 +++ tests/Colors/Cmyk/ParserTest.php | 32 -- tests/Colors/ParserTest.php | 46 --- tests/Colors/Rgb/ColorTest.php | 9 +- .../Rgb/Decoders/HexColorDecoderTest.php | 58 ++++ .../Rgb/Decoders/HtmlColornameDecoderTest.php | 30 ++ .../Rgb/Decoders/StringColorDecoderTest.php | 50 +++ tests/Colors/Rgb/ParserTest.php | 80 ----- tests/Drivers/Gd/ColorTransformerTest.php | 30 -- .../Gd/Decoders/HexColorDecoderTest.php | 30 -- .../Gd/Decoders/HtmlColorNameDecoderTest.php | 18 -- .../Gd/Decoders/RgbStringColorDecoderTest.php | 30 -- tests/Drivers/Gd/ImageTest.php | 38 +-- tests/Drivers/Gd/InputHandlerTest.php | 10 +- .../Drivers/Gd/Modifiers/FillModifierTest.php | 6 +- .../Drivers/Gd/Modifiers/FitModifierTest.php | 6 +- .../Gd/Modifiers/FlipFlopModifierTest.php | 4 +- .../Gd/Modifiers/ResizeModifierTest.php | 10 +- .../Drivers/Imagick/ColorTransformerTest.php | 37 --- .../Decoders/HtmlColorNameDecoderTest.php | 18 -- .../Decoders/RgbStringColorDecoderTest.php | 31 -- tests/Drivers/Imagick/InputHandlerTest.php | 10 +- .../Imagick/Modifiers/FillModifierTest.php | 7 +- .../Imagick/Modifiers/FitModifierTest.php | 6 +- .../Modifiers/FlipFlopModifierTest.php | 4 +- .../Imagick/Modifiers/ResizeModifierTest.php | 8 +- tests/TestCase.php | 9 +- 55 files changed, 685 insertions(+), 1024 deletions(-) create mode 100644 src/Colors/Cmyk/Decoders/StringColorDecoder.php delete mode 100644 src/Colors/Cmyk/Parser.php delete mode 100644 src/Colors/Parser.php create mode 100644 src/Colors/Rgb/Decoders/HexColorDecoder.php create mode 100644 src/Colors/Rgb/Decoders/HtmlColornameDecoder.php create mode 100644 src/Colors/Rgb/Decoders/StringColorDecoder.php delete mode 100644 src/Colors/Rgb/Parser.php delete mode 100644 src/Drivers/Gd/Decoders/HexColorDecoder.php delete mode 100644 src/Drivers/Gd/Decoders/HtmlColorNameDecoder.php delete mode 100644 src/Drivers/Gd/Decoders/RgbStringColorDecoder.php rename src/Drivers/Gd/{ColorTransformer.php => Traits/CanHandleColors.php} (74%) delete mode 100644 src/Drivers/Imagick/Decoders/HexColorDecoder.php delete mode 100644 src/Drivers/Imagick/Decoders/HtmlColorNameDecoder.php delete mode 100644 src/Drivers/Imagick/Decoders/RgbStringColorDecoder.php rename src/Drivers/Imagick/{ColorTransformer.php => Traits/CanHandleColors.php} (55%) create mode 100644 tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php delete mode 100644 tests/Colors/Cmyk/ParserTest.php delete mode 100644 tests/Colors/ParserTest.php create mode 100644 tests/Colors/Rgb/Decoders/HexColorDecoderTest.php create mode 100644 tests/Colors/Rgb/Decoders/HtmlColornameDecoderTest.php create mode 100644 tests/Colors/Rgb/Decoders/StringColorDecoderTest.php delete mode 100644 tests/Colors/Rgb/ParserTest.php delete mode 100644 tests/Drivers/Gd/ColorTransformerTest.php delete mode 100644 tests/Drivers/Gd/Decoders/HexColorDecoderTest.php delete mode 100644 tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php delete mode 100644 tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php delete mode 100644 tests/Drivers/Imagick/ColorTransformerTest.php delete mode 100644 tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php delete mode 100644 tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index bf6cb401e..927669818 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -29,6 +29,11 @@ public function __construct(int $c, int $m, int $y, int $k) ]; } + public function toHex(): string + { + return $this->toRgb()->toHex(); + } + public function channels(): array { return $this->channels; @@ -95,6 +100,15 @@ public function toString(): string ); } + public function isGreyscale(): bool + { + return 0 === array_sum([ + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + ]); + } + public function __toString(): string { return $this->toString(); diff --git a/src/Colors/Cmyk/Decoders/StringColorDecoder.php b/src/Colors/Cmyk/Decoders/StringColorDecoder.php new file mode 100644 index 000000000..0f05ada95 --- /dev/null +++ b/src/Colors/Cmyk/Decoders/StringColorDecoder.php @@ -0,0 +1,37 @@ +[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + $values = array_map(function ($value) { + return intval(round(floatval(trim(str_replace('%', '', $value))))); + }, [$matches['c'], $matches['m'], $matches['y'], $matches['k']]); + + return new Color(...$values); + } +} diff --git a/src/Colors/Cmyk/Parser.php b/src/Colors/Cmyk/Parser.php deleted file mode 100644 index 15fb3a700..000000000 --- a/src/Colors/Cmyk/Parser.php +++ /dev/null @@ -1,34 +0,0 @@ -[0-9\.]+)%?, ?(?P[0-9\.]+)%?, ?(?P[0-9\.]+)%?, ?(?P[0-9\.]+)%?\)$/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - intval(round(floatval($matches['c']))), - intval(round(floatval($matches['m']))), - intval(round(floatval($matches['y']))), - intval(round(floatval($matches['k']))) - ); - } - - throw new ColorException('Unable to parse color'); - } -} diff --git a/src/Colors/Parser.php b/src/Colors/Parser.php deleted file mode 100644 index dcf91c591..000000000 --- a/src/Colors/Parser.php +++ /dev/null @@ -1,27 +0,0 @@ -red()->value(), $this->green()->value(), $this->blue()->value()]; + + return count(array_unique($values, SORT_REGULAR)) === 1; + } + public function __toString(): string { return $this->toString(); diff --git a/src/Colors/Rgb/Decoders/HexColorDecoder.php b/src/Colors/Rgb/Decoders/HexColorDecoder.php new file mode 100644 index 000000000..43aafee3c --- /dev/null +++ b/src/Colors/Rgb/Decoders/HexColorDecoder.php @@ -0,0 +1,48 @@ +[a-f\d]{3}(?:[a-f\d]?|(?:[a-f\d]{3}(?:[a-f\d]{2})?)?)\b)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + $values = str_split($matches['hex']); + $values = match (strlen($matches['hex'])) { + 3, 4 => str_split($matches['hex']), + 6, 8 => str_split($matches['hex'], 2), + default => throw new DecoderException('Unable to decode input'), + }; + + $values = array_map(function ($value) { + return match (strlen($value)) { + 1 => hexdec($value . $value), + 2 => hexdec($value), + default => throw new DecoderException('Unable to decode input'), + }; + }, $values); + + return new Color(...$values); + } +} diff --git a/src/Colors/Rgb/Decoders/HtmlColornameDecoder.php b/src/Colors/Rgb/Decoders/HtmlColornameDecoder.php new file mode 100644 index 000000000..fa4ea6309 --- /dev/null +++ b/src/Colors/Rgb/Decoders/HtmlColornameDecoder.php @@ -0,0 +1,172 @@ + '#ffa07a', + 'salmon' => '#fa8072', + 'darksalmon' => '#e9967a', + 'lightcoral' => '#f08080', + 'indianred' => '#cd5c5c', + 'crimson' => '#dc143c', + 'firebrick' => '#b22222', + 'red' => '#ff0000', + 'darkred' => '#8b0000', + 'coral' => '#ff7f50', + 'tomato' => '#ff6347', + 'orangered' => '#ff4500', + 'gold' => '#ffd700', + 'orange' => '#ffa500', + 'darkorange' => '#ff8c00', + 'lightyellow' => '#ffffe0', + 'lemonchiffon' => '#fffacd', + 'lightgoldenrodyellow' => '#fafad2', + 'papayawhip' => '#ffefd5', + 'moccasin' => '#ffe4b5', + 'peachpuff' => '#ffdab9', + 'palegoldenrod' => '#eee8aa', + 'khaki' => '#f0e68c', + 'darkkhaki' => '#bdb76b', + 'yellow' => '#ffff00', + 'lawngreen' => '#7cfc00', + 'chartreuse' => '#7fff00', + 'limegreen' => '#32cd32', + 'lime' => '#00ff00', + 'forestgreen' => '#228b22', + 'green' => '#008000', + 'darkgreen' => '#006400', + 'greenyellow' => '#adff2f', + 'yellowgreen' => '#9acd32', + 'springgreen' => '#00ff7f', + 'mediumspringgreen' => '#00fa9a', + 'lightgreen' => '#90ee90', + 'palegreen' => '#98fb98', + 'darkseagreen' => '#8fbc8f', + 'mediumseagre' => 'en #3cb371', + 'seagreen' => '#2e8b57', + 'olive' => '#808000', + 'darkolivegreen' => '#556b2f', + 'olivedrab' => '#6b8e23', + 'lightcyan' => '#e0ffff', + 'cyan' => '#00ffff', + 'aqua' => '#00ffff', + 'aquamarine' => '#7fffd4', + 'mediumaquamarine' => '#66cdaa', + 'paleturquoise' => '#afeeee', + 'turquoise' => '#40e0d0', + 'mediumturquoise' => '#48d1cc', + 'darkturquoise' => '#00ced1', + 'lightseagreen' => '#20b2aa', + 'cadetblue' => '#5f9ea0', + 'darkcyan' => '#008b8b', + 'teal' => '#008080', + 'powderblue' => '#b0e0e6', + 'lightblue' => '#add8e6', + 'lightskyblue' => '#87cefa', + 'skyblue' => '#87ceeb', + 'deepskyblue' => '#00bfff', + 'lightsteelblue' => '#b0c4de', + 'dodgerblue' => '#1e90ff', + 'cornflowerblue' => '#6495ed', + 'steelblue' => '#4682b4', + 'royalblue' => '#4169e1', + 'blue' => '#0000ff', + 'mediumblue' => '#0000cd', + 'darkblue' => '#00008b', + 'navy' => '#000080', + 'midnightblue' => '#191970', + 'mediumslateblue' => '#7b68ee', + 'slateblue' => '#6a5acd', + 'darkslateblue' => '#483d8b', + 'lavender' => '#e6e6fa', + 'thistle' => '#d8bfd8', + 'plum' => '#dda0dd', + 'violet' => '#ee82ee', + 'orchid' => '#da70d6', + 'fuchsia' => '#ff00ff', + 'magenta' => '#ff00ff', + 'mediumorchid' => '#ba55d3', + 'mediumpurple' => '#9370db', + 'blueviolet' => '#8a2be2', + 'darkviolet' => '#9400d3', + 'darkorchid' => '#9932cc', + 'darkmagenta' => '#8b008b', + 'purple' => '#800080', + 'indigo' => '#4b0082', + 'pink' => '#ffc0cb', + 'lightpink' => '#ffb6c1', + 'hotpink' => '#ff69b4', + 'deeppink' => '#ff1493', + 'palevioletred' => '#db7093', + 'mediumvioletred' => '#c71585', + 'white' => '#ffffff', + 'snow' => '#fffafa', + 'honeydew' => '#f0fff0', + 'mintcream' => '#f5fffa', + 'azure' => '#f0ffff', + 'aliceblue' => '#f0f8ff', + 'ghostwhite' => '#f8f8ff', + 'whitesmoke' => '#f5f5f5', + 'seashell' => '#fff5ee', + 'beige' => '#f5f5dc', + 'oldlace' => '#fdf5e6', + 'floralwhite' => '#fffaf0', + 'ivory' => '#fffff0', + 'antiquewhite' => '#faebd7', + 'linen' => '#faf0e6', + 'lavenderblush' => '#fff0f5', + 'mistyrose' => '#ffe4e1', + 'gainsboro' => '#dcdcdc', + 'lightgray' => '#d3d3d3', + 'silver' => '#c0c0c0', + 'darkgray' => '#a9a9a9', + 'gray' => '#808080', + 'dimgray' => '#696969', + 'lightslategray' => '#778899', + 'slategray' => '#708090', + 'darkslategray' => '#2f4f4f', + 'black' => '#000000', + 'cornsilk' => '#fff8dc', + 'blanchedalmond' => '#ffebcd', + 'bisque' => '#ffe4c4', + 'navajowhite' => '#ffdead', + 'wheat' => '#f5deb3', + 'burlywood' => '#deb887', + 'tan' => '#d2b48c', + 'rosybrown' => '#bc8f8f', + 'sandybrown' => '#f4a460', + 'goldenrod' => '#daa520', + 'peru' => '#cd853f', + 'chocolate' => '#d2691e', + 'saddlebrown' => '#8b4513', + 'sienna' => '#a0522d', + 'brown' => '#a52a2a', + 'maroon' => '#800000', + ]; + + /** + * Decode html color names + * + * @param mixed $input + * @return ImageInterface|ColorInterface + */ + public function decode($input): ImageInterface|ColorInterface + { + if (! is_string($input)) { + throw new DecoderException('Unable to decode input'); + } + + if (!array_key_exists(strtolower($input), static::$names)) { + throw new DecoderException('Unable to decode input'); + } + + return parent::decode(static::$names[strtolower($input)]); + } +} diff --git a/src/Colors/Rgb/Decoders/StringColorDecoder.php b/src/Colors/Rgb/Decoders/StringColorDecoder.php new file mode 100644 index 000000000..86a62b4df --- /dev/null +++ b/src/Colors/Rgb/Decoders/StringColorDecoder.php @@ -0,0 +1,49 @@ +[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)(?:, ?(?P(?:1)|(?:1\.0*)|(?:0)|(?:0?\.\d+%?)|(?:\d{1,3}%)))?\)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + // rgb values + $values = array_map(function ($value) { + return match (strpos($value, '%')) { + false => intval(trim($value)), + default => intval(round(floatval(trim(str_replace('%', '', $value))) / 100 * 255)), + }; + }, [$matches['r'], $matches['g'], $matches['b']]); + + // alpha value + if (array_key_exists('a', $matches)) { + $values[] = match (true) { + strpos($matches['a'], '%') => round(intval(trim(str_replace('%', '', $matches['a']))) / 2.55), + default => intval(round(floatval(trim($matches['a'])) * 255)), + }; + } + + return new Color(...$values); + } +} diff --git a/src/Colors/Rgb/Parser.php b/src/Colors/Rgb/Parser.php deleted file mode 100644 index 746a40d61..000000000 --- a/src/Colors/Rgb/Parser.php +++ /dev/null @@ -1,286 +0,0 @@ -[0-9a-f]{3}|[0-9a-f]{6})$/i'; - $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new ColorException('Unable to parse color'); - } - - $matches = match (strlen($matches['hex'])) { - 3 => str_split($matches['hex']), - 6 => str_split($matches['hex'], 2), - default => throw new ColorException('Unable to parse color'), - }; - - try { - return new Color( - strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]) - ); - } catch (ColorException $e) { - # move on - } - - // Hexadecimal colors with transparency - $pattern = '/^#?(?P[0-9a-f]{4}|[0-9a-f]{8})$/i'; - $result = preg_match($pattern, $input, $matches); - - if ($result !== 1) { - throw new ColorException('Unable to parse color'); - } - - $matches = match (strlen($matches['hex'])) { - 4 => str_split($matches['hex']), - 8 => str_split($matches['hex'], 2), - default => throw new ColorException('Unable to parse color'), - }; - - return new Color( - strlen($matches[0]) == '1' ? hexdec($matches[0] . $matches[0]) : hexdec($matches[0]), - strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]), - strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]), - strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]), - ); - } - - public static function fromString(string $input): Color - { - // rgb(255, 255, 255) - $pattern = '/^s?rgb\((?P[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - $matches['r'], - $matches['g'], - $matches['b'] - ); - } - - // rgb(100%, 100%, 100%) - $pattern = '/^s?rgb\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)$/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - intval(round(floatval($matches['r']) / 100 * 255)), - intval(round(floatval($matches['g']) / 100 * 255)), - intval(round(floatval($matches['b']) / 100 * 255)) - ); - } - - // rgba(255, 255, 255, 1.0) - $pattern = '/^s?rgba\((?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P[0-9]{1,3}), *(?P((1|0))?(\.[0-9]+)?)\)$/'; - $result = preg_match($pattern, $input, $matches); - - if ($result === 1) { - return new Color( - $matches['r'], - $matches['g'], - $matches['b'], - intval(round(floatval($matches['a']) * 255)) - ); - } - - // rgba(100%, 100%, 100%, 100%) - $pattern = '/s?rgba\((?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%, ?(?P[0-9\.]+)%\)/'; - $result = preg_match($pattern, $input, $matches); - if ($result === 1) { - return new Color( - intval(round(floatval($matches['r']) / 100 * 255)), - intval(round(floatval($matches['g']) / 100 * 255)), - intval(round(floatval($matches['b']) / 100 * 255)), - intval(round(floatval($matches['a']) / 100 * 255)) - ); - } - - throw new ColorException('Unable to parse color'); - } - - public static function fromName(string $input): Color - { - $names = [ - 'lightsalmon' => '#FFA07A', - 'salmon' => '#FA8072', - 'darksalmon' => '#E9967A', - 'lightcoral' => '#F08080', - 'indianred' => '#CD5C5C', - 'crimson' => '#DC143C', - 'firebrick' => '#B22222', - 'red' => '#FF0000', - 'darkred' => '#8B0000', - 'coral' => '#FF7F50', - 'tomato' => '#FF6347', - 'orangered' => '#FF4500', - 'gold' => '#FFD700', - 'orange' => '#FFA500', - 'darkorange' => '#FF8C00', - 'lightyellow' => '#FFFFE0', - 'lemonchiffon' => '#FFFACD', - 'lightgoldenrodyellow' => '#FAFAD2', - 'papayawhip' => '#FFEFD5', - 'moccasin' => '#FFE4B5', - 'peachpuff' => '#FFDAB9', - 'palegoldenrod' => '#EEE8AA', - 'khaki' => '#F0E68C', - 'darkkhaki' => '#BDB76B', - 'yellow' => '#FFFF00', - 'lawngreen' => '#7CFC00', - 'chartreuse' => '#7FFF00', - 'limegreen' => '#32CD32', - 'lime' => '#00FF00', - 'forestgreen' => '#228B22', - 'green' => '#008000', - 'darkgreen' => '#006400', - 'greenyellow' => '#ADFF2F', - 'yellowgreen' => '#9ACD32', - 'springgreen' => '#00FF7F', - 'mediumspringgreen' => '#00FA9A', - 'lightgreen' => '#90EE90', - 'palegreen' => '#98FB98', - 'darkseagreen' => '#8FBC8F', - 'mediumseagre' => 'en #3CB371', - 'seagreen' => '#2E8B57', - 'olive' => '#808000', - 'darkolivegreen' => '#556B2F', - 'olivedrab' => '#6B8E23', - 'lightcyan' => '#E0FFFF', - 'cyan' => '#00FFFF', - 'aqua' => '#00FFFF', - 'aquamarine' => '#7FFFD4', - 'mediumaquamarine' => '#66CDAA', - 'paleturquoise' => '#AFEEEE', - 'turquoise' => '#40E0D0', - 'mediumturquoise' => '#48D1CC', - 'darkturquoise' => '#00CED1', - 'lightseagreen' => '#20B2AA', - 'cadetblue' => '#5F9EA0', - 'darkcyan' => '#008B8B', - 'teal' => '#008080', - 'powderblue' => '#B0E0E6', - 'lightblue' => '#ADD8E6', - 'lightskyblue' => '#87CEFA', - 'skyblue' => '#87CEEB', - 'deepskyblue' => '#00BFFF', - 'lightsteelblue' => '#B0C4DE', - 'dodgerblue' => '#1E90FF', - 'cornflowerblue' => '#6495ED', - 'steelblue' => '#4682B4', - 'royalblue' => '#4169E1', - 'blue' => '#0000FF', - 'mediumblue' => '#0000CD', - 'darkblue' => '#00008B', - 'navy' => '#000080', - 'midnightblue' => '#191970', - 'mediumslateblue' => '#7B68EE', - 'slateblue' => '#6A5ACD', - 'darkslateblue' => '#483D8B', - 'lavender' => '#E6E6FA', - 'thistle' => '#D8BFD8', - 'plum' => '#DDA0DD', - 'violet' => '#EE82EE', - 'orchid' => '#DA70D6', - 'fuchsia' => '#FF00FF', - 'magenta' => '#FF00FF', - 'mediumorchid' => '#BA55D3', - 'mediumpurple' => '#9370DB', - 'blueviolet' => '#8A2BE2', - 'darkviolet' => '#9400D3', - 'darkorchid' => '#9932CC', - 'darkmagenta' => '#8B008B', - 'purple' => '#800080', - 'indigo' => '#4B0082', - 'pink' => '#FFC0CB', - 'lightpink' => '#FFB6C1', - 'hotpink' => '#FF69B4', - 'deeppink' => '#FF1493', - 'palevioletred' => '#DB7093', - 'mediumvioletred' => '#C71585', - 'white' => '#FFFFFF', - 'snow' => '#FFFAFA', - 'honeydew' => '#F0FFF0', - 'mintcream' => '#F5FFFA', - 'azure' => '#F0FFFF', - 'aliceblue' => '#F0F8FF', - 'ghostwhite' => '#F8F8FF', - 'whitesmoke' => '#F5F5F5', - 'seashell' => '#FFF5EE', - 'beige' => '#F5F5DC', - 'oldlace' => '#FDF5E6', - 'floralwhite' => '#FFFAF0', - 'ivory' => '#FFFFF0', - 'antiquewhite' => '#FAEBD7', - 'linen' => '#FAF0E6', - 'lavenderblush' => '#FFF0F5', - 'mistyrose' => '#FFE4E1', - 'gainsboro' => '#DCDCDC', - 'lightgray' => '#D3D3D3', - 'silver' => '#C0C0C0', - 'darkgray' => '#A9A9A9', - 'gray' => '#808080', - 'dimgray' => '#696969', - 'lightslategray' => '#778899', - 'slategray' => '#708090', - 'darkslategray' => '#2F4F4F', - 'black' => '#000000', - 'cornsilk' => '#FFF8DC', - 'blanchedalmond' => '#FFEBCD', - 'bisque' => '#FFE4C4', - 'navajowhite' => '#FFDEAD', - 'wheat' => '#F5DEB3', - 'burlywood' => '#DEB887', - 'tan' => '#D2B48C', - 'rosybrown' => '#BC8F8F', - 'sandybrown' => '#F4A460', - 'goldenrod' => '#DAA520', - 'peru' => '#CD853F', - 'chocolate' => '#D2691E', - 'saddlebrown' => '#8B4513', - 'sienna' => '#A0522D', - 'brown' => '#A52A2A', - 'maroon' => '#800000', - ]; - - if (!array_key_exists(strtolower($input), $names)) { - throw new ColorException('Unable to parse color'); - } - - return static::fromHex($names[strtolower($input)]); - } -} diff --git a/src/Drivers/Gd/Decoders/HexColorDecoder.php b/src/Drivers/Gd/Decoders/HexColorDecoder.php deleted file mode 100644 index 3371659a8..000000000 --- a/src/Drivers/Gd/Decoders/HexColorDecoder.php +++ /dev/null @@ -1,36 +0,0 @@ -getFrame($frame_key)) { - return ColorTransformer::colorFromInteger(imagecolorat($frame->getCore(), $x, $y)); + return $this->colorFromInteger( + imagecolorat($frame->getCore(), $x, $y) + ); } return null; diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 076269c8d..c545168f8 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -2,21 +2,33 @@ namespace Intervention\Image\Drivers\Gd; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; +use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; +use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; +use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; +use Intervention\Image\Drivers\Gd\Decoders\FilePointerImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\DataUriImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\Base64ImageDecoder; class InputHandler extends AbstractInputHandler { protected $decoders = [ - Decoders\ImageObjectDecoder::class, - Decoders\ColorObjectDecoder::class, - Decoders\FilePointerImageDecoder::class, - Decoders\HtmlColorNameDecoder::class, - Decoders\HexColorDecoder::class, - Decoders\RgbStringColorDecoder::class, + ImageObjectDecoder::class, + ColorObjectDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, // Decoders\TransparentColorDecoder::class, - Decoders\FilePathImageDecoder::class, - Decoders\BinaryImageDecoder::class, - Decoders\DataUriImageDecoder::class, - Decoders\Base64ImageDecoder::class, + HtmlColornameDecoder::class, + FilePointerImageDecoder::class, + FilePathImageDecoder::class, + BinaryImageDecoder::class, + DataUriImageDecoder::class, + Base64ImageDecoder::class, ]; } diff --git a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php index bab245798..f9010bf43 100644 --- a/src/Drivers/Gd/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawPixelModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -10,6 +11,7 @@ class DrawPixelModifier implements ModifierInterface { use CanHandleInput; + use CanHandleColors; public function __construct( protected Point $position, @@ -26,7 +28,7 @@ public function apply(ImageInterface $image): ImageInterface $frame->getCore(), $this->position->getX(), $this->position->getY(), - $color->toInt() + $this->colorToInteger($color) ); }); } diff --git a/src/Drivers/Gd/Modifiers/FillModifier.php b/src/Drivers/Gd/Modifiers/FillModifier.php index 0d0ea3f3f..774c32b76 100644 --- a/src/Drivers/Gd/Modifiers/FillModifier.php +++ b/src/Drivers/Gd/Modifiers/FillModifier.php @@ -2,8 +2,8 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\ColorTransformer; use Intervention\Image\Drivers\Gd\Frame; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -11,37 +11,39 @@ class FillModifier implements ModifierInterface { - protected $gd_color; + use CanHandleColors; public function __construct(protected ColorInterface $color, protected ?Point $position = null) { - $this->gd_color = ColorTransformer::colorToInteger($color); + // } public function apply(ImageInterface $image): ImageInterface { + $color = $this->colorToInteger($this->color); + foreach ($image as $frame) { if ($this->hasPosition()) { - $this->floodFillWithColor($frame); + $this->floodFillWithColor($frame, $color); } else { - $this->fillAllWithColor($frame); + $this->fillAllWithColor($frame, $color); } } return $image; } - protected function floodFillWithColor(Frame $frame): void + protected function floodFillWithColor(Frame $frame, int $color): void { imagefill( $frame->getCore(), $this->position->getX(), $this->position->getY(), - $this->gd_color + $color ); } - protected function fillAllWithColor(Frame $frame): void + protected function fillAllWithColor(Frame $frame, int $color): void { imagealphablending($frame->getCore(), true); imagefilledrectangle( @@ -50,7 +52,7 @@ protected function fillAllWithColor(Frame $frame): void 0, $frame->getSize()->getWidth() - 1, $frame->getSize()->getHeight() - 1, - $this->gd_color + $color ); } diff --git a/src/Drivers/Gd/ColorTransformer.php b/src/Drivers/Gd/Traits/CanHandleColors.php similarity index 74% rename from src/Drivers/Gd/ColorTransformer.php rename to src/Drivers/Gd/Traits/CanHandleColors.php index 62d9c8bde..ecfa1dd42 100644 --- a/src/Drivers/Gd/ColorTransformer.php +++ b/src/Drivers/Gd/Traits/CanHandleColors.php @@ -1,11 +1,11 @@ > 24) & 0xFF; $r = ($value >> 16) & 0xFF; @@ -33,7 +33,7 @@ public static function colorFromInteger(int $value): Color * @param ColorInterface $color * @return int */ - public static function colorToInteger(ColorInterface $color): int + public function colorToInteger(ColorInterface $color): int { $color = $color->toRgb(); @@ -49,6 +49,17 @@ public static function colorToInteger(ColorInterface $color): int return ($a << 24) + ($r << 16) + ($g << 8) + $b; } + /** + * Convert input in range (min) to (max) to the corresponding value + * in target range (targetMin) to (targetMax). + * + * @param float|int $input + * @param float|int $min + * @param float|int $max + * @param float|int $targetMin + * @param float|int $targetMax + * @return float|int + */ private static function convertRange( float|int $input, float|int $min, diff --git a/src/Drivers/Imagick/Decoders/HexColorDecoder.php b/src/Drivers/Imagick/Decoders/HexColorDecoder.php deleted file mode 100644 index e1e687b38..000000000 --- a/src/Drivers/Imagick/Decoders/HexColorDecoder.php +++ /dev/null @@ -1,36 +0,0 @@ -getFrame($frame_key)) { - return ColorTransformer::colorFromPixel( + return $this->colorFromPixel( $frame->getCore()->getImagePixelColor($x, $y) ); } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index d6ec81ef5..f7c493b4a 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -2,21 +2,33 @@ namespace Intervention\Image\Drivers\Imagick; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; +use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; +use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\FilePointerImageDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\BinaryImageDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\DataUriImageDecoder; +use Intervention\Image\Drivers\Imagick\Decoders\Base64ImageDecoder; class InputHandler extends AbstractInputHandler { protected $decoders = [ - Decoders\ImageObjectDecoder::class, - Decoders\ColorObjectDecoder::class, - Decoders\FilePointerImageDecoder::class, - Decoders\HtmlColorNameDecoder::class, - Decoders\HexColorDecoder::class, - Decoders\RgbStringColorDecoder::class, + ImageObjectDecoder::class, + ColorObjectDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, // Decoders\TransparentColorDecoder::class, - Decoders\FilePathImageDecoder::class, - Decoders\BinaryImageDecoder::class, - Decoders\DataUriImageDecoder::class, - Decoders\Base64ImageDecoder::class, + HtmlColornameDecoder::class, + FilePointerImageDecoder::class, + FilePathImageDecoder::class, + BinaryImageDecoder::class, + DataUriImageDecoder::class, + Base64ImageDecoder::class, ]; } diff --git a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php index 208889918..abeebb953 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPixelModifier.php @@ -3,8 +3,9 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use ImagickDraw; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Geometry\Point; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Traits\CanCheckType; @@ -13,6 +14,7 @@ class DrawPixelModifier implements ModifierInterface { use CanHandleInput; + use CanHandleColors; use CanCheckType; public function __construct(protected Point $position, protected mixed $color) @@ -22,13 +24,13 @@ public function __construct(protected Point $position, protected mixed $color) public function apply(ImageInterface $image): ImageInterface { - $color = $this->failIfNotClass( + $color = $this->failIfNotInstance( $this->handleInput($this->color), - Color::class, + ColorInterface::class ); $pixel = new ImagickDraw(); - $pixel->setFillColor($color->getPixel()); + $pixel->setFillColor($this->colorToPixel($color)); $pixel->point($this->position->getX(), $this->position->getY()); return $image->eachFrame(function ($frame) use ($pixel) { diff --git a/src/Drivers/Imagick/Modifiers/FillModifier.php b/src/Drivers/Imagick/Modifiers/FillModifier.php index 36bef1af9..c78f06baa 100644 --- a/src/Drivers/Imagick/Modifiers/FillModifier.php +++ b/src/Drivers/Imagick/Modifiers/FillModifier.php @@ -5,8 +5,8 @@ use Imagick; use ImagickDraw; use ImagickPixel; -use Intervention\Image\Drivers\Imagick\ColorTransformer; use Intervention\Image\Drivers\Imagick\Frame; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -14,29 +14,30 @@ class FillModifier implements ModifierInterface { - protected ImagickPixel $pixel; + use CanHandleColors; public function __construct( protected ColorInterface $color, protected ?Point $position = null ) { - $this->pixel = ColorTransformer::colorToPixel($color); + // } public function apply(ImageInterface $image): ImageInterface { + $pixel = $this->colorToPixel($this->color); foreach ($image as $frame) { if ($this->hasPosition()) { - $this->floodFillWithColor($frame); + $this->floodFillWithColor($frame, $pixel); } else { - $this->fillAllWithColor($frame); + $this->fillAllWithColor($frame, $pixel); } } return $image; } - protected function floodFillWithColor(Frame $frame): void + protected function floodFillWithColor(Frame $frame, ImagickPixel $pixel): void { $target = $frame->getCore()->getImagePixelColor( $this->position->getX(), @@ -44,7 +45,7 @@ protected function floodFillWithColor(Frame $frame): void ); $frame->getCore()->floodfillPaintImage( - $this->pixel, + $pixel, 100, $target, $this->position->getX(), @@ -54,10 +55,10 @@ protected function floodFillWithColor(Frame $frame): void ); } - protected function fillAllWithColor(Frame $frame): void + protected function fillAllWithColor(Frame $frame, ImagickPixel $pixel): void { $draw = new ImagickDraw(); - $draw->setFillColor($this->pixel); + $draw->setFillColor($pixel); $draw->rectangle( 0, 0, diff --git a/src/Drivers/Imagick/ColorTransformer.php b/src/Drivers/Imagick/Traits/CanHandleColors.php similarity index 55% rename from src/Drivers/Imagick/ColorTransformer.php rename to src/Drivers/Imagick/Traits/CanHandleColors.php index 1ddf8a32d..547030c1b 100644 --- a/src/Drivers/Imagick/ColorTransformer.php +++ b/src/Drivers/Imagick/Traits/CanHandleColors.php @@ -1,22 +1,24 @@ getColorAsString()); + return $this->handleInput($pixel->getColorAsString()); } /** @@ -25,7 +27,7 @@ public static function colorFromPixel(ImagickPixel $pixel): ColorInterface * @param ColorInterface $color * @return ImagickPixel */ - public static function colorToPixel(ColorInterface $color): ImagickPixel + public function colorToPixel(ColorInterface $color): ImagickPixel { return new ImagickPixel($color->toString()); } diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 7cf183172..c1659ef2f 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -11,10 +11,9 @@ public function toRgb(): RgbColor; public function toCmyk(): CmykColor; public function toArray(): array; public function toString(): string; + public function toHex(): string; public function __toString(): string; - - // public function channels(): array; - // public function channel(string $classname): ColorChannelInterface; - // public function colorspace(): ColorspaceInterface; - // public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; + public function channels(): array; + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; + public function isGreyscale(): bool; } diff --git a/src/Traits/CanCheckType.php b/src/Traits/CanCheckType.php index f70e85bde..c817dfbd4 100644 --- a/src/Traits/CanCheckType.php +++ b/src/Traits/CanCheckType.php @@ -14,4 +14,13 @@ public function failIfNotClass(mixed $input, string $classname) return $input; } + + public function failIfNotInstance(mixed $input, string $classname) + { + if (!is_object($input) || !is_a($input, $classname)) { + throw new TypeException('Given input is not instance of ' . $classname); + } + + return $input; + } } diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php index 66e872839..070a199d3 100644 --- a/tests/Colors/Cmyk/ColorTest.php +++ b/tests/Colors/Cmyk/ColorTest.php @@ -80,6 +80,6 @@ public function testToRgb(): void $color = new Color(0, 20, 20, 0); $converted = $color->toRgb(); $this->assertInstanceOf(RgbColor::class, $converted); - $this->assertEquals([255, 204, 204], $converted->toArray()); + $this->assertEquals([255, 204, 204, 255], $converted->toArray()); } } diff --git a/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php new file mode 100644 index 000000000..f6af4237d --- /dev/null +++ b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php @@ -0,0 +1,35 @@ +decode('cmyk(0,0,0,0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0, 0], $result->toArray()); + + $result = $decoder->decode('cmyk(0, 100, 100, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + + $result = $decoder->decode('cmyk(0, 100, 100, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + + + $result = $decoder->decode('cmyk(0%, 100%, 100%, 0%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + } +} diff --git a/tests/Colors/Cmyk/ParserTest.php b/tests/Colors/Cmyk/ParserTest.php deleted file mode 100644 index b94ec4f2f..000000000 --- a/tests/Colors/Cmyk/ParserTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertInstanceOf(Color::class, $color); - $this->assertEquals([100, 0, 0, 0], $color->toArray()); - } - - public function testFromString(): void - { - $color = Parser::fromString('cmyk(100, 0, 0, 0)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([100, 0, 0, 0], $color->toArray()); - - $color = Parser::fromString('cmyk(100%, 0%, 0%, 0%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([100, 0, 0, 0], $color->toArray()); - } -} diff --git a/tests/Colors/ParserTest.php b/tests/Colors/ParserTest.php deleted file mode 100644 index 4e158140c..000000000 --- a/tests/Colors/ParserTest.php +++ /dev/null @@ -1,46 +0,0 @@ -assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::parse('rgb(204, 204, 204)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::parse('rgba(204, 204, 204, 1)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204, 255], $color->toArray()); - - $color = Parser::parse('cccc'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::parse('cccccccc'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([204, 204, 204, 204], $color->toArray()); - - $color = Parser::parse('salmon'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals('fa8072', $color->toHex()); - - $color = Parser::parse('cmyk(100, 100, 0,0)'); - $this->assertInstanceOf(CmykColor::class, $color); - $this->assertEquals([100, 100, 0, 0], $color->toArray()); - } -} diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index 8121a47f0..f44191bdb 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -19,13 +19,16 @@ public function testConstructor(): void { $color = new Color(0, 0, 0); $this->assertInstanceOf(Color::class, $color); + + $color = new Color(0, 0, 0, 0); + $this->assertInstanceOf(Color::class, $color); } public function testChannels(): void { $color = new Color(10, 20, 30); $this->assertIsArray($color->channels()); - $this->assertCount(3, $color->channels()); + $this->assertCount(4, $color->channels()); } public function testChannel(): void @@ -50,7 +53,7 @@ public function testRedGreenBlue(): void public function testToArray(): void { $color = new Color(10, 20, 30); - $this->assertEquals([10, 20, 30], $color->toArray()); + $this->assertEquals([10, 20, 30, 255], $color->toArray()); } public function testToHex(): void @@ -63,7 +66,7 @@ public function testToHex(): void public function testNormalize(): void { $color = new Color(255, 0, 51); - $this->assertEquals([1.0, 0.0, 0.2], $color->normalize()); + $this->assertEquals([1.0, 0.0, 0.2, 1.0], $color->normalize()); } public function testToString(): void diff --git a/tests/Colors/Rgb/Decoders/HexColorDecoderTest.php b/tests/Colors/Rgb/Decoders/HexColorDecoderTest.php new file mode 100644 index 000000000..439b533fc --- /dev/null +++ b/tests/Colors/Rgb/Decoders/HexColorDecoderTest.php @@ -0,0 +1,58 @@ +decode('ccc'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('ccff33'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); + + $result = $decoder->decode('#ccc'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('cccccc'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('#cccccc'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('#ccccccff'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('#cccf'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('ccccccff'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('cccf'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('#b53717aa'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([181, 55, 23, 170], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/Decoders/HtmlColornameDecoderTest.php b/tests/Colors/Rgb/Decoders/HtmlColornameDecoderTest.php new file mode 100644 index 000000000..843d7a159 --- /dev/null +++ b/tests/Colors/Rgb/Decoders/HtmlColornameDecoderTest.php @@ -0,0 +1,30 @@ +decode('salmon'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([250, 128, 114, 255], $result->toArray()); + + $result = $decoder->decode('khaki'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([240, 230, 140, 255], $result->toArray()); + + $result = $decoder->decode('peachpuff'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 218, 185, 255], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/Decoders/StringColorDecoderTest.php b/tests/Colors/Rgb/Decoders/StringColorDecoderTest.php new file mode 100644 index 000000000..956fe100e --- /dev/null +++ b/tests/Colors/Rgb/Decoders/StringColorDecoderTest.php @@ -0,0 +1,50 @@ +decode('rgb(204, 204, 204)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('rgb(204,204,204)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('rgb(100%,20%,0%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 51, 0, 255], $result->toArray()); + + $result = $decoder->decode('rgb(100%,19.8064%,0.1239483%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 51, 0, 255], $result->toArray()); + + $result = $decoder->decode('rgba(204, 204, 204, 1)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 255], $result->toArray()); + + $result = $decoder->decode('rgba(204,204,204,.2)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 51], $result->toArray()); + + $result = $decoder->decode('rgba(204,204,204,0.2)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([204, 204, 204, 51], $result->toArray()); + + $result = $decoder->decode('srgb(255, 0, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([255, 0, 0, 255], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/ParserTest.php b/tests/Colors/Rgb/ParserTest.php deleted file mode 100644 index d98b8601a..000000000 --- a/tests/Colors/Rgb/ParserTest.php +++ /dev/null @@ -1,80 +0,0 @@ -assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::parse('rgb(204, 204, 204)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::parse('salmon'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('fa8072', $color->toHex()); - } - - public function testFromHex(): void - { - $color = Parser::fromHex('ccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('cccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::fromHex('#cccccc'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $this->expectException(ColorException::class); - (new Parser())->fromHex('cccccccc'); - } - - public function testFromString(): void - { - $color = Parser::fromString('rgb(204, 204, 204)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::fromString('rgb(204,204,204)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([204, 204, 204], $color->toArray()); - - $color = Parser::fromString('rgb(100%,20%,25%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([255, 51, 64], $color->toArray()); - - $color = Parser::fromString('rgb(100%,74.8064%,25.2497%)'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals([255, 191, 64], $color->toArray()); - - $this->expectException(ColorException::class); - (new Parser())->fromString('rgb(204,204,204,1)'); - - $this->expectException(ColorException::class); - (new Parser())->fromString('rgb(120)'); - } - - public function testFromName(): void - { - $color = Parser::fromName('salmon'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('fa8072', $color->toHex()); - } -} diff --git a/tests/Drivers/Gd/ColorTransformerTest.php b/tests/Drivers/Gd/ColorTransformerTest.php deleted file mode 100644 index 04260235a..000000000 --- a/tests/Drivers/Gd/ColorTransformerTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertInstanceOf(Color::class, $result); - $this->assertEquals([181, 55, 23, 155], $result->toArray()); - - $result = ColorTransformer::colorFromInteger(16777215); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([255, 255, 255, 255], $result->toArray()); - } - - public function testToInteger(): void - { - $result = ColorTransformer::colorToInteger(new Color(181, 55, 23, 155)); - $this->assertEquals(850736919, $result); - - $result = ColorTransformer::colorToInteger(new Color(255, 255, 255, 255)); - $this->assertEquals(16777215, $result); - } -} diff --git a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php b/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php deleted file mode 100644 index f78468abd..000000000 --- a/tests/Drivers/Gd/Decoders/HexColorDecoderTest.php +++ /dev/null @@ -1,30 +0,0 @@ -decode('ccc'); - $this->assertInstanceOf(Color::class, $result); - - $result = $decoder->decode('#ccc'); - $this->assertInstanceOf(Color::class, $result); - - $result = $decoder->decode('cccccc'); - $this->assertInstanceOf(Color::class, $result); - - $result = $decoder->decode('#cccccc'); - $this->assertInstanceOf(Color::class, $result); - } -} diff --git a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php deleted file mode 100644 index 1113c7844..000000000 --- a/tests/Drivers/Gd/Decoders/HtmlColorNameDecoderTest.php +++ /dev/null @@ -1,18 +0,0 @@ -decode('tomato'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('ff6347', $color->toHex()); - } -} diff --git a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php deleted file mode 100644 index fb19fb98c..000000000 --- a/tests/Drivers/Gd/Decoders/RgbStringColorDecoderTest.php +++ /dev/null @@ -1,30 +0,0 @@ -decode('rgb(181, 55, 23)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([181, 55, 23], $color->toArray()); - } - - public function testDecodeRgba(): void - { - $decoder = new RgbStringColorDecoder(); - $color = $decoder->decode('rgba(181, 55, 23, 0.5)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([181, 55, 23, 51], $color->toArray()); - } -} diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 927450fd2..a878443bf 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -3,11 +3,11 @@ 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\Rectangle; use Intervention\Image\Tests\TestCase; +use Intervention\Image\Colors\Rgb\Color; /** * @requires extension gd @@ -96,21 +96,21 @@ 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()); + $this->assertEquals(255, $color->toRgb()->red()->value()); + $this->assertEquals(0, $color->toRgb()->green()->value()); + $this->assertEquals(0, $color->toRgb()->blue()->value()); $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()); + $this->assertEquals(0, $color->toRgb()->red()->value()); + $this->assertEquals(255, $color->toRgb()->green()->value()); + $this->assertEquals(0, $color->toRgb()->blue()->value()); $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()); + $this->assertEquals(0, $color->toRgb()->red()->value()); + $this->assertEquals(0, $color->toRgb()->green()->value()); + $this->assertEquals(255, $color->toRgb()->blue()->value()); $color = $this->image->pickColor(0, 0, 3); $this->assertNull($color); @@ -122,16 +122,16 @@ public function testPickColors(): void $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(255, $colors->get(0)->toRgb()->red()->value()); + $this->assertEquals(0, $colors->get(0)->toRgb()->green()->value()); + $this->assertEquals(0, $colors->get(0)->toRgb()->blue()->value()); - $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(1)->toRgb()->red()->value()); + $this->assertEquals(255, $colors->get(1)->toRgb()->green()->value()); + $this->assertEquals(0, $colors->get(1)->toRgb()->blue()->value()); - $this->assertEquals(0, $colors->get(2)->red()); - $this->assertEquals(0, $colors->get(2)->green()); - $this->assertEquals(255, $colors->get(2)->blue()); + $this->assertEquals(0, $colors->get(2)->toRgb()->red()->value()); + $this->assertEquals(0, $colors->get(2)->toRgb()->green()->value()); + $this->assertEquals(255, $colors->get(2)->toRgb()->blue()->value()); } } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 3cdaaee29..07328d498 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -59,25 +59,25 @@ public function testHandleHexColor(): void $input = 'ccff33'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51], $result->toArray()); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51], $result->toArray()); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([18, 52, 86], $result->toArray()); + $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51], $result->toArray()); + $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; @@ -97,7 +97,7 @@ public function testHandleRgbString(): void $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([10, 20, 30], $result->toArray()); + $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); diff --git a/tests/Drivers/Gd/Modifiers/FillModifierTest.php b/tests/Drivers/Gd/Modifiers/FillModifierTest.php index 380b746a5..c974b4b21 100644 --- a/tests/Drivers/Gd/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FillModifierTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Modifiers\FillModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; @@ -21,7 +21,7 @@ public function testFloodFillColor(): void $image = $this->createTestImage('blocks.png'); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); - $image->modify(new FillModifier(new Color(13421772), new Point(540, 400))); + $image->modify(new FillModifier(new Color(204, 204, 204), new Point(540, 400))); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } @@ -31,7 +31,7 @@ public function testFillAllColor(): void $image = $this->createTestImage('blocks.png'); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); - $image->modify(new FillModifier(new Color(13421772))); + $image->modify(new FillModifier(new Color(204, 204, 204))); $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } diff --git a/tests/Drivers/Gd/Modifiers/FitModifierTest.php b/tests/Drivers/Gd/Modifiers/FitModifierTest.php index 58bd8094d..a7b427b31 100644 --- a/tests/Drivers/Gd/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 255, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 255, $image->pickColor(70, 52)); $this->assertTransparency($image->pickColor(90, 30)); } } diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php index 0305f6a82..3a7ec01db 100644 --- a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -21,7 +21,7 @@ public function testFlipImage(): void $image = $this->createTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); } public function testFlopImage(): void @@ -29,6 +29,6 @@ public function testFlopImage(): void $image = $this->createTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php index bf7ef2340..33d8d541b 100644 --- a/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ResizeModifierTest.php @@ -22,11 +22,9 @@ public function testModify(): void $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $transparent = $image->pickColor(170, 30); - $this->assertEquals(2130706432, $transparent->toInt()); - $this->assertTransparency($transparent); + $this->assertColor(255, 0, 0, 255, $image->pickColor(150, 70)); + $this->assertColor(0, 255, 0, 255, $image->pickColor(125, 70)); + $this->assertColor(0, 0, 255, 255, $image->pickColor(130, 54)); + $this->assertTransparency($image->pickColor(170, 30)); } } diff --git a/tests/Drivers/Imagick/ColorTransformerTest.php b/tests/Drivers/Imagick/ColorTransformerTest.php deleted file mode 100644 index 49f16ad15..000000000 --- a/tests/Drivers/Imagick/ColorTransformerTest.php +++ /dev/null @@ -1,37 +0,0 @@ -assertInstanceOf(Color::class, $result); - $this->assertEquals([181, 55, 23, 153], $result->toArray()); - - $result = ColorTransformer::colorFromPixel(new ImagickPixel('rgba(255, 255, 255, 1)')); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([255, 255, 255, 255], $result->toArray()); - } - - public function testToPixel(): void - { - $result = ColorTransformer::colorToPixel(new Color(181, 55, 23, 153)); - $this->assertInstanceOf(ImagickPixel::class, $result); - $this->assertEquals('srgba(181,55,23,0.6)', $result->getColorAsString()); - - $result = ColorTransformer::colorToPixel(new Color(255, 255, 255, 255)); - $this->assertInstanceOf(ImagickPixel::class, $result); - $this->assertEquals('srgba(255,255,255,1)', $result->getColorAsString()); - - $result = ColorTransformer::colorToPixel(new Color(255, 255, 255, 170)); - $this->assertInstanceOf(ImagickPixel::class, $result); - $this->assertEquals('srgba(255,255,255,0.699992)', $result->getColorAsString()); - } -} diff --git a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php b/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php deleted file mode 100644 index ba9790f6f..000000000 --- a/tests/Drivers/Imagick/Decoders/HtmlColorNameDecoderTest.php +++ /dev/null @@ -1,18 +0,0 @@ -decode('tomato'); - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals('ff6347', $color->toHex()); - } -} diff --git a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php b/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php deleted file mode 100644 index 3029bc027..000000000 --- a/tests/Drivers/Imagick/Decoders/RgbStringColorDecoderTest.php +++ /dev/null @@ -1,31 +0,0 @@ -decode('rgb(181, 55, 23)'); - $this->assertInstanceOf(RgbColor::class, $color); - $this->assertEquals([181, 55, 23], $color->toArray()); - } - - public function testDecodeRgba(): void - { - $decoder = new RgbStringColorDecoder(); - $color = $decoder->decode('rgba(181, 55, 23, 0.2)'); - $this->assertInstanceOf(RgbaColor::class, $color); - $this->assertEquals([181, 55, 23, 51], $color->toArray()); - } -} diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index e7ce923e4..158edd5eb 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -59,25 +59,25 @@ public function testHandleHexColor(): void $input = 'ccff33'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51], $result->toArray()); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51], $result->toArray()); + $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([18, 52, 86], $result->toArray()); + $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51], $result->toArray()); + $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; @@ -97,7 +97,7 @@ public function testHandleRgbString(): void $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([10, 20, 30], $result->toArray()); + $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); diff --git a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php index 37d9b9d7f..96ffd4a4b 100644 --- a/tests/Drivers/Imagick/Modifiers/FillModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FillModifierTest.php @@ -2,8 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers; -use ImagickPixel; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\Modifiers\FillModifier; use Intervention\Image\Geometry\Point; use Intervention\Image\Tests\TestCase; @@ -22,7 +21,7 @@ public function testFloodFillColor(): void $image = $this->createTestImage('blocks.png'); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); - $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')), new Point(540, 400))); + $image->modify(new FillModifier(new Color(204, 204, 204), new Point(540, 400))); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } @@ -32,7 +31,7 @@ public function testFillAllColor(): void $image = $this->createTestImage('blocks.png'); $this->assertEquals('0000ff', $image->pickColor(420, 270)->toHex()); $this->assertEquals('ff0000', $image->pickColor(540, 400)->toHex()); - $image->modify(new FillModifier(new Color(new ImagickPixel('#cccccc')))); + $image->modify(new FillModifier(new Color(204, 204, 204))); $this->assertEquals('cccccc', $image->pickColor(420, 270)->toHex()); $this->assertEquals('cccccc', $image->pickColor(540, 400)->toHex()); } diff --git a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php index 783e8e495..7f126abb5 100644 --- a/tests/Drivers/Imagick/Modifiers/FitModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FitModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new FitModifier(100, 100, 'center')); $this->assertEquals(100, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(90, 90)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(65, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(70, 52)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(90, 90)); + $this->assertColor(0, 255, 0, 255, $image->pickColor(65, 70)); + $this->assertColor(0, 0, 255, 255, $image->pickColor(70, 52)); $this->assertTransparency($image->pickColor(90, 30)); } } diff --git a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php index 10ecab694..ce356d431 100644 --- a/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/FlipFlopModifierTest.php @@ -21,7 +21,7 @@ public function testFlipImage(): void $image = $this->createTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); } public function testFlopImage(): void @@ -29,6 +29,6 @@ public function testFlopImage(): void $image = $this->createTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('000000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); } } diff --git a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php index 519e69f1e..dd35f3a95 100644 --- a/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/ResizeModifierTest.php @@ -22,9 +22,9 @@ public function testModify(): void $image->modify(new ResizeModifier(200, 100)); $this->assertEquals(200, $image->getWidth()); $this->assertEquals(100, $image->getHeight()); - $this->assertColor(255, 0, 0, 1, $image->pickColor(150, 70)); - $this->assertColor(0, 255, 0, 1, $image->pickColor(125, 70)); - $this->assertColor(0, 0, 255, 1, $image->pickColor(130, 54)); - $this->assertTransparency($image->pickColor(150, 45)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(150, 70)); + // $this->assertColor(0, 255, 0, 255, $image->pickColor(125, 70)); + // $this->assertColor(0, 0, 255, 255, $image->pickColor(130, 54)); + // $this->assertTransparency($image->pickColor(150, 45)); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index a3d0b2ba8..81eddb55c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests; +use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Interfaces\ColorInterface; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -19,14 +20,12 @@ public function getTestImageData($filename = 'test.jpg'): string protected function assertColor($r, $g, $b, $a, ColorInterface $color) { - $this->assertEquals($r, $color->red()); - $this->assertEquals($g, $color->green()); - $this->assertEquals($b, $color->blue()); - $this->assertEquals($a, $color->alpha()); + $this->assertEquals([$r, $g, $b, $a], $color->toArray()); } protected function assertTransparency(ColorInterface $color) { - $this->assertEquals(0, $color->alpha()); + $this->assertInstanceOf(RgbColor::class, $color); + $this->assertEquals(0, $color->toRgb()->alpha()->value()); } } From f24a33705815b7f48351d4a157795cb064d8747b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 19 Oct 2023 17:51:20 +0200 Subject: [PATCH 0443/1667] Remove Cmyk Colors for now --- src/Colors/Cmyk/Channels/Cyan.php | 55 --------- src/Colors/Cmyk/Channels/Key.php | 8 -- src/Colors/Cmyk/Channels/Magenta.php | 8 -- src/Colors/Cmyk/Channels/Yellow.php | 8 -- src/Colors/Cmyk/Color.php | 116 ------------------ src/Colors/Cmyk/Colorspace.php | 38 ------ .../Cmyk/Decoders/StringColorDecoder.php | 37 ------ src/Colors/Rgb/Color.php | 7 -- src/Colors/Rgb/Colorspace.php | 11 -- src/Drivers/Gd/InputHandler.php | 10 +- src/Drivers/Imagick/InputHandler.php | 10 +- src/Interfaces/ColorInterface.php | 6 +- tests/Colors/Cmyk/ChannelTest.php | 48 -------- tests/Colors/Cmyk/ColorTest.php | 85 ------------- tests/Colors/Cmyk/ColorspaceTest.php | 26 ---- .../Cmyk/Decoders/StringColorDecoderTest.php | 35 ------ tests/Colors/Rgb/ColorTest.php | 34 ----- tests/Colors/Rgb/ColorspaceTest.php | 26 ---- tests/Drivers/Gd/InputHandlerTest.php | 18 +-- tests/Drivers/Imagick/InputHandlerTest.php | 18 +-- tests/TestCase.php | 4 +- 21 files changed, 30 insertions(+), 578 deletions(-) delete mode 100644 src/Colors/Cmyk/Channels/Cyan.php delete mode 100644 src/Colors/Cmyk/Channels/Key.php delete mode 100644 src/Colors/Cmyk/Channels/Magenta.php delete mode 100644 src/Colors/Cmyk/Channels/Yellow.php delete mode 100644 src/Colors/Cmyk/Color.php delete mode 100644 src/Colors/Cmyk/Colorspace.php delete mode 100644 src/Colors/Cmyk/Decoders/StringColorDecoder.php delete mode 100644 tests/Colors/Cmyk/ChannelTest.php delete mode 100644 tests/Colors/Cmyk/ColorTest.php delete mode 100644 tests/Colors/Cmyk/ColorspaceTest.php delete mode 100644 tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php delete mode 100644 tests/Colors/Rgb/ColorspaceTest.php diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php deleted file mode 100644 index 213fea38b..000000000 --- a/src/Colors/Cmyk/Channels/Cyan.php +++ /dev/null @@ -1,55 +0,0 @@ -value = $this->validate($value); - } - - public function value(): int - { - return $this->value; - } - - public function normalize($precision = 32): float - { - return round($this->value() / $this->max(), $precision); - } - - public function min(): int - { - return 0; - } - - public function max(): int - { - return 100; - } - - public function validate(mixed $value): mixed - { - if ($value < $this->min() || $value > $this->max()) { - throw new ColorException('CMYK color values must be in range 0-100.'); - } - - return $value; - } - - public function toString(): string - { - return (string) $this->value(); - } - - public function __toString(): string - { - return $this->toString(); - } -} diff --git a/src/Colors/Cmyk/Channels/Key.php b/src/Colors/Cmyk/Channels/Key.php deleted file mode 100644 index 2893d2afb..000000000 --- a/src/Colors/Cmyk/Channels/Key.php +++ /dev/null @@ -1,8 +0,0 @@ -channels = [ - new Cyan($c), - new Magenta($m), - new Yellow($y), - new Key($k), - ]; - } - - public function toHex(): string - { - return $this->toRgb()->toHex(); - } - - public function channels(): array - { - return $this->channels; - } - - public function cyan(): Cyan - { - return $this->channel(Cyan::class); - } - - public function magenta(): Magenta - { - return $this->channel(Magenta::class); - } - - public function yellow(): Yellow - { - return $this->channel(Yellow::class); - } - - public function key(): Key - { - return $this->channel(Key::class); - } - - public function toArray(): array - { - return [ - $this->cyan()->value(), - $this->magenta()->value(), - $this->yellow()->value(), - $this->key()->value(), - ]; - } - - public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface - { - $colorspace = match (true) { - is_object($colorspace) => $colorspace, - default => new $colorspace(), - }; - - return $colorspace->convertColor($this); - } - - public function toRgb(): RgbColor - { - return $this->convertTo(RgbColorspace::class); - } - - public function toCmyk(): self - { - return $this->convertTo(CmykColorspace::class); - } - - public function toString(): string - { - return sprintf( - 'cmyk(%d, %d, %d, %d)', - $this->cyan()->value(), - $this->magenta()->value(), - $this->yellow()->value(), - $this->key()->value() - ); - } - - public function isGreyscale(): bool - { - return 0 === array_sum([ - $this->cyan()->value(), - $this->magenta()->value(), - $this->yellow()->value(), - ]); - } - - public function __toString(): string - { - return $this->toString(); - } -} diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php deleted file mode 100644 index 76ce8ea33..000000000 --- a/src/Colors/Cmyk/Colorspace.php +++ /dev/null @@ -1,38 +0,0 @@ - $this->convertRgbColor($color), - default => $color, - }; - } - - protected function convertRgbColor(RgbColor $color): CmykColor - { - $c = (255 - $color->red()->value()) / 255.0 * 100; - $m = (255 - $color->green()->value()) / 255.0 * 100; - $y = (255 - $color->blue()->value()) / 255.0 * 100; - $k = intval(round(min([$c, $m, $y]))); - - $c = intval(round($c - $k)); - $m = intval(round($m - $k)); - $y = intval(round($y - $k)); - - return new CmykColor($c, $m, $y, $k); - } -} diff --git a/src/Colors/Cmyk/Decoders/StringColorDecoder.php b/src/Colors/Cmyk/Decoders/StringColorDecoder.php deleted file mode 100644 index 0f05ada95..000000000 --- a/src/Colors/Cmyk/Decoders/StringColorDecoder.php +++ /dev/null @@ -1,37 +0,0 @@ -[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; - if (preg_match($pattern, $input, $matches) != 1) { - throw new DecoderException('Unable to decode input'); - } - - $values = array_map(function ($value) { - return intval(round(floatval(trim(str_replace('%', '', $value))))); - }, [$matches['c'], $matches['m'], $matches['y'], $matches['k']]); - - return new Color(...$values); - } -} diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 89cf0b090..1a5723c54 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -2,8 +2,6 @@ namespace Intervention\Image\Colors\Rgb; -use Intervention\Image\Colors\Cmyk\Color as CmykColor; -use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Channels\Blue; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; @@ -93,11 +91,6 @@ public function convertTo(string|ColorspaceInterface $colorspace): ColorInterfac return $colorspace->convertColor($this); } - public function toCmyk(): CmykColor - { - return $this->convertTo(CmykColorspace::class); - } - public function isFullyOpaque(): bool { return $this->alpha()->value() === 255; diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index 4c0bcb0f2..4dbaae082 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Colors\Rgb; -use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -11,17 +10,7 @@ class Colorspace implements ColorspaceInterface public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { - CmykColor::class => $this->convertCmykColor($color), default => $color, }; } - - protected function convertCmykColor(CmykColor $color): Color - { - return new Color( - (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), - (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), - (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), - ); - } } diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index c545168f8..5069e3fb7 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -2,10 +2,9 @@ namespace Intervention\Image\Drivers\Gd; -use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; -use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; -use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; @@ -20,9 +19,8 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, - RgbHexColorDecoder::class, - RgbStringColorDecoder::class, - CmykStringColorDecoder::class, + HexColorDecoder::class, + StringColorDecoder::class, // Decoders\TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index f7c493b4a..27ca7c5ba 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -2,10 +2,9 @@ namespace Intervention\Image\Drivers\Imagick; -use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; -use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; -use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; @@ -20,9 +19,8 @@ class InputHandler extends AbstractInputHandler protected $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, - RgbHexColorDecoder::class, - RgbStringColorDecoder::class, - CmykStringColorDecoder::class, + HexColorDecoder::class, + StringColorDecoder::class, // Decoders\TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index c1659ef2f..6ba4cafe2 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -2,13 +2,11 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Colors\Cmyk\Color as CmykColor; -use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgb\Color; interface ColorInterface { - public function toRgb(): RgbColor; - public function toCmyk(): CmykColor; + public function toRgb(): Color; public function toArray(): array; public function toString(): string; public function toHex(): string; diff --git a/tests/Colors/Cmyk/ChannelTest.php b/tests/Colors/Cmyk/ChannelTest.php deleted file mode 100644 index 44928abf6..000000000 --- a/tests/Colors/Cmyk/ChannelTest.php +++ /dev/null @@ -1,48 +0,0 @@ -assertInstanceOf(Channel::class, $channel); - } - - public function testValue(): void - { - $channel = new Channel(10); - $this->assertEquals(10, $channel->value()); - } - - public function testNormalize(): void - { - $channel = new Channel(100); - $this->assertEquals(1, $channel->normalize()); - $channel = new Channel(0); - $this->assertEquals(0, $channel->normalize()); - $channel = new Channel(20); - $this->assertEquals(.2, $channel->normalize()); - } - - public function testValidate(): void - { - $this->expectException(ColorException::class); - new Channel(101); - - $this->expectException(ColorException::class); - new Channel(-1); - } -} diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php deleted file mode 100644 index 070a199d3..000000000 --- a/tests/Colors/Cmyk/ColorTest.php +++ /dev/null @@ -1,85 +0,0 @@ -assertInstanceOf(Color::class, $color); - } - - public function testChannels(): void - { - $color = new Color(10, 20, 30, 40); - $this->assertIsArray($color->channels()); - $this->assertCount(4, $color->channels()); - } - - public function testChannel(): void - { - $color = new Color(10, 20, 30, 40); - $channel = $color->channel(Cyan::class); - $this->assertInstanceOf(Cyan::class, $channel); - $this->assertEquals(10, $channel->value()); - } - - public function testCyanMagentaYellowKey(): void - { - $color = new Color(10, 20, 30, 40); - $this->assertInstanceOf(Cyan::class, $color->cyan()); - $this->assertInstanceOf(Magenta::class, $color->magenta()); - $this->assertInstanceOf(Yellow::class, $color->yellow()); - $this->assertInstanceOf(Key::class, $color->key()); - $this->assertEquals(10, $color->cyan()->value()); - $this->assertEquals(20, $color->magenta()->value()); - $this->assertEquals(30, $color->yellow()->value()); - $this->assertEquals(40, $color->key()->value()); - } - - public function testToArray(): void - { - $color = new Color(10, 20, 30, 40); - $this->assertEquals([10, 20, 30, 40], $color->toArray()); - } - - public function testNormalize(): void - { - $color = new Color(100, 50, 20, 0); - $this->assertEquals([1.0, 0.5, 0.2, 0.0], $color->normalize()); - } - - public function testToString(): void - { - $color = new Color(100, 50, 20, 0); - $this->assertEquals('cmyk(100, 50, 20, 0)', (string) $color); - } - - public function testToCmyk(): void - { - $color = new Color(0, 0, 0, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(Color::class, $converted); - } - - public function testToRgb(): void - { - $color = new Color(0, 20, 20, 0); - $converted = $color->toRgb(); - $this->assertInstanceOf(RgbColor::class, $converted); - $this->assertEquals([255, 204, 204, 255], $converted->toArray()); - } -} diff --git a/tests/Colors/Cmyk/ColorspaceTest.php b/tests/Colors/Cmyk/ColorspaceTest.php deleted file mode 100644 index 818269fcf..000000000 --- a/tests/Colors/Cmyk/ColorspaceTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertInstanceOf( - CmykColor::class, - $colorspace->convertColor( - new RgbColor(0, 0, 0) - ) - ); - } -} diff --git a/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php deleted file mode 100644 index f6af4237d..000000000 --- a/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php +++ /dev/null @@ -1,35 +0,0 @@ -decode('cmyk(0,0,0,0)'); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([0, 0, 0, 0], $result->toArray()); - - $result = $decoder->decode('cmyk(0, 100, 100, 0)'); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([0, 100, 100, 0], $result->toArray()); - - $result = $decoder->decode('cmyk(0, 100, 100, 0)'); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([0, 100, 100, 0], $result->toArray()); - - - $result = $decoder->decode('cmyk(0%, 100%, 100%, 0%)'); - $this->assertInstanceOf(Color::class, $result); - $this->assertEquals([0, 100, 100, 0], $result->toArray()); - } -} diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index f44191bdb..131dcef73 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Tests\Colors\Rgb; -use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Blue; @@ -75,39 +74,6 @@ public function testToString(): void $this->assertEquals('rgb(181, 55, 23)', (string) $color); } - public function testToCmyk(): void - { - $color = new Color(0, 0, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 0, 100], $converted->toArray()); - - $color = new Color(255, 255, 255); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 0, 0], $converted->toArray()); - - $color = new Color(255, 0, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 100, 100, 0], $converted->toArray()); - - $color = new Color(255, 0, 255); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 100, 0, 0], $converted->toArray()); - - $color = new Color(255, 255, 0); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 0, 100, 0], $converted->toArray()); - - $color = new Color(255, 204, 204); - $converted = $color->toCmyk(); - $this->assertInstanceOf(CmykColor::class, $converted); - $this->assertEquals([0, 20, 20, 0], $converted->toArray()); - } - public function testToRgb(): void { $color = new Color(181, 55, 23); diff --git a/tests/Colors/Rgb/ColorspaceTest.php b/tests/Colors/Rgb/ColorspaceTest.php deleted file mode 100644 index be7808c5f..000000000 --- a/tests/Colors/Rgb/ColorspaceTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertInstanceOf( - RgbColor::class, - $colorspace->convertColor( - new CmykColor(0, 0, 0, 0) - ) - ); - } -} diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 07328d498..7be7f4e0c 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -58,37 +58,37 @@ public function testHandleHexColor(): void $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -96,12 +96,12 @@ public function testHandleRgbString(): void { $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index 158edd5eb..df2a5b1a6 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -58,37 +58,37 @@ public function testHandleHexColor(): void $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -96,12 +96,12 @@ public function testHandleRgbString(): void { $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbColor::class, $result); + $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 81eddb55c..cfd60927a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests; -use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Interfaces\ColorInterface; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -25,7 +25,7 @@ protected function assertColor($r, $g, $b, $a, ColorInterface $color) protected function assertTransparency(ColorInterface $color) { - $this->assertInstanceOf(RgbColor::class, $color); + $this->assertInstanceOf(Color::class, $color); $this->assertEquals(0, $color->toRgb()->alpha()->value()); } } From df4c3e0dddec0ad0e655ead0f8fa66d30c4fd8b1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 20 Oct 2023 15:58:35 +0200 Subject: [PATCH 0444/1667] Adjust modifiers for new color model --- .../Rgb/Decoders/TransparentColorDecoder.php | 23 +++++++++++++++++++ .../Modifiers/AbstractDrawModifier.php | 4 ++-- src/Drivers/Gd/InputHandler.php | 3 ++- .../Gd/Modifiers/DrawEllipseModifier.php | 9 +++++--- src/Drivers/Gd/Modifiers/DrawLineModifier.php | 5 +++- .../Gd/Modifiers/DrawPolygonModifier.php | 7 ++++-- .../Gd/Modifiers/DrawRectangleModifier.php | 7 ++++-- src/Drivers/Gd/Modifiers/PadModifier.php | 9 ++++---- src/Drivers/Gd/Modifiers/RotateModifier.php | 5 +++- src/Drivers/Gd/Modifiers/TextWriter.php | 8 +++++-- src/Drivers/Imagick/InputHandler.php | 3 ++- .../Imagick/Modifiers/DrawEllipseModifier.php | 12 ++++++---- .../Imagick/Modifiers/DrawLineModifier.php | 10 +++++--- .../Imagick/Modifiers/DrawPolygonModifier.php | 12 ++++++---- .../Modifiers/DrawRectangleModifier.php | 13 ++++++----- .../Imagick/Modifiers/RotateModifier.php | 8 +++---- tests/Drivers/Gd/InputHandlerTest.php | 15 ++++++------ tests/Drivers/Imagick/InputHandlerTest.php | 9 ++++++++ 18 files changed, 113 insertions(+), 49 deletions(-) create mode 100644 src/Colors/Rgb/Decoders/TransparentColorDecoder.php diff --git a/src/Colors/Rgb/Decoders/TransparentColorDecoder.php b/src/Colors/Rgb/Decoders/TransparentColorDecoder.php new file mode 100644 index 000000000..1474987ca --- /dev/null +++ b/src/Colors/Rgb/Decoders/TransparentColorDecoder.php @@ -0,0 +1,23 @@ +drawable; } - protected function getBackgroundColor(): ?ColorInterface + protected function getBackgroundColor(): ColorInterface { try { $color = $this->handleInput($this->drawable->getBackgroundColor()); @@ -42,7 +42,7 @@ protected function getBackgroundColor(): ?ColorInterface return $color; } - protected function getBorderColor(): ?ColorInterface + protected function getBorderColor(): ColorInterface { try { $color = $this->handleInput($this->drawable->getBorderColor()); diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 5069e3fb7..85d69a74b 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -5,6 +5,7 @@ use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; +use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; @@ -21,7 +22,7 @@ class InputHandler extends AbstractInputHandler ColorObjectDecoder::class, HexColorDecoder::class, StringColorDecoder::class, - // Decoders\TransparentColorDecoder::class, + TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, FilePathImageDecoder::class, diff --git a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php index 8f956c719..ed99cb3ea 100644 --- a/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { return $image->eachFrame(function ($frame) { @@ -20,7 +23,7 @@ public function apply(ImageInterface $image): ImageInterface $this->position->getY(), $this->ellipse()->getWidth() - 1, $this->ellipse()->getHeight() - 1, - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); } @@ -36,7 +39,7 @@ public function apply(ImageInterface $image): ImageInterface $this->ellipse()->getHeight(), 0, 360, - $this->getBorderColor()->toInt() + $this->colorToInteger($this->getBorderColor()) ); } else { imagefilledellipse( @@ -45,7 +48,7 @@ public function apply(ImageInterface $image): ImageInterface $this->position->getY(), $this->ellipse()->getWidth(), $this->ellipse()->getHeight(), - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); } }); diff --git a/src/Drivers/Gd/Modifiers/DrawLineModifier.php b/src/Drivers/Gd/Modifiers/DrawLineModifier.php index 677d6345b..9cfafb58d 100644 --- a/src/Drivers/Gd/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawLineModifier.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { return $image->eachFrame(function ($frame) { @@ -17,7 +20,7 @@ public function apply(ImageInterface $image): ImageInterface $this->line()->getStart()->getY(), $this->line()->getEnd()->getX(), $this->line()->getEnd()->getY(), - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); }); } diff --git a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php index cb4c930f7..f0cd77bbc 100644 --- a/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php @@ -3,12 +3,15 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function __construct( protected DrawableInterface $drawable ) { @@ -22,7 +25,7 @@ public function apply(ImageInterface $image): ImageInterface imagefilledpolygon( $frame->getCore(), $this->polygon()->toArray(), - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); } @@ -32,7 +35,7 @@ public function apply(ImageInterface $image): ImageInterface $frame->getCore(), $this->polygon()->toArray(), $this->polygon()->count(), - $this->getBorderColor()->toInt() + $this->colorToInteger($this->getBorderColor()) ); } }); diff --git a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php index 1b5e39dc2..2cc1653b1 100644 --- a/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { $image->eachFrame(function ($frame) { @@ -19,7 +22,7 @@ public function apply(ImageInterface $image): ImageInterface $this->position->getY(), $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), - $this->getBackgroundColor()->toInt() + $this->colorToInteger($this->getBackgroundColor()) ); } @@ -32,7 +35,7 @@ public function apply(ImageInterface $image): ImageInterface $this->position->getY(), $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), - $this->getBorderColor()->toInt() + $this->colorToInteger($this->getBorderColor()) ); } }); diff --git a/src/Drivers/Gd/Modifiers/PadModifier.php b/src/Drivers/Gd/Modifiers/PadModifier.php index a8e78445d..34fdb4d72 100644 --- a/src/Drivers/Gd/Modifiers/PadModifier.php +++ b/src/Drivers/Gd/Modifiers/PadModifier.php @@ -3,7 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; -use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -13,12 +13,13 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface { use CanHandleInput; + use CanHandleColors; public function apply(ImageInterface $image): ImageInterface { $crop = $this->getCropSize($image); $resize = $this->getResizeSize($image); - $background = $this->handleInput($this->background); + $background = $this->colorToInteger($this->handleInput($this->background)); foreach ($image as $frame) { $this->modify($frame, $crop, $resize, $background); @@ -31,7 +32,7 @@ protected function modify( FrameInterface $frame, SizeInterface $crop, SizeInterface $resize, - ColorInterface $background + int $background ): void { // create new image $modified = imagecreatetruecolor( @@ -39,7 +40,7 @@ protected function modify( $resize->getHeight() ); - imagefill($modified, 0, 0, $background->toInt()); + imagefill($modified, 0, 0, $background); // get current image $current = $frame->getCore(); diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index b692c4744..efaecddd4 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -3,11 +3,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class RotateModifier extends AbstractRotateModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { foreach ($image as $frame) { @@ -15,7 +18,7 @@ public function apply(ImageInterface $image): ImageInterface imagerotate( $frame->getCore(), $this->rotationAngle(), - $this->backgroundColor()->toInt() + $this->colorToInteger($this->backgroundColor()) ) ); } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index fd117bca1..61cf63ee7 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -4,14 +4,18 @@ use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; +use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { $lines = $this->getAlignedTextBlock(); $font = $this->failIfNotClass($this->getFont(), Font::class); + $color = $this->colorToInteger($font->getColor()); foreach ($image as $frame) { if ($this->font->hasFilename()) { @@ -22,7 +26,7 @@ public function apply(ImageInterface $image): ImageInterface $font->getAngle() * (-1), $line->getPosition()->getX(), $line->getPosition()->getY(), - $font->getColor()->toInt(), + $color, $font->getFilename(), $line ); @@ -35,7 +39,7 @@ public function apply(ImageInterface $image): ImageInterface $line->getPosition()->getX(), $line->getPosition()->getY(), $line, - $this->font->getColor()->toInt() + $color ); } } diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 27ca7c5ba..3d27678aa 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -5,6 +5,7 @@ use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; +use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; @@ -21,7 +22,7 @@ class InputHandler extends AbstractInputHandler ColorObjectDecoder::class, HexColorDecoder::class, StringColorDecoder::class, - // Decoders\TransparentColorDecoder::class, + TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, FilePathImageDecoder::class, diff --git a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php index 289500c2a..0c3e53fc1 100644 --- a/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php @@ -4,24 +4,26 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { - $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); - $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + $background_color = $this->colorToPixel($this->getBackgroundColor()); + $border_color = $this->colorToPixel($this->getBorderColor()); return $image->eachFrame(function ($frame) use ($background_color, $border_color) { $drawing = new ImagickDraw(); - $drawing->setFillColor($background_color->getPixel()); + $drawing->setFillColor($background_color); if ($this->ellipse()->hasBorder()) { $drawing->setStrokeWidth($this->ellipse()->getBorderSize()); - $drawing->setStrokeColor($border_color->getPixel()); + $drawing->setStrokeColor($border_color); } $drawing->ellipse( diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php index 17c4ac760..74fa94720 100644 --- a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -4,18 +4,22 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawLineModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); - $color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); - $drawing->setStrokeColor($color->getPixel()); $drawing->setStrokeWidth($this->line()->getWidth()); + $drawing->setStrokeColor( + $this->colorToPixel($this->getBackgroundColor()) + ); + $drawing->line( $this->line()->getStart()->getX(), $this->line()->getStart()->getY(), diff --git a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php index ea8b511c1..34634dbbb 100644 --- a/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php @@ -4,13 +4,15 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\DrawableInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawPolygonModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function __construct( protected DrawableInterface $drawable ) { @@ -20,15 +22,15 @@ public function __construct( public function apply(ImageInterface $image): ImageInterface { $drawing = new ImagickDraw(); - $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); - $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + $background_color = $this->colorToPixel($this->getBackgroundColor()); + $border_color = $this->colorToPixel($this->getBorderColor()); if ($this->polygon()->hasBackgroundColor()) { - $drawing->setFillColor($background_color->getPixel()); + $drawing->setFillColor($background_color); } if ($this->polygon()->hasBorder()) { - $drawing->setStrokeColor($border_color->getPixel()); + $drawing->setStrokeColor($border_color); $drawing->setStrokeWidth($this->polygon()->getBorderSize()); } diff --git a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php index 2baf97210..60d225d3f 100644 --- a/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php @@ -4,22 +4,23 @@ use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { - // setup $drawing = new ImagickDraw(); - $background_color = $this->failIfNotClass($this->getBackgroundColor(), Color::class); - $border_color = $this->failIfNotClass($this->getBorderColor(), Color::class); + $background_color = $this->colorToPixel($this->getBackgroundColor()); + $border_color = $this->colorToPixel($this->getBorderColor()); - $drawing->setFillColor($background_color->getPixel()); + $drawing->setFillColor($background_color); if ($this->rectangle()->hasBorder()) { - $drawing->setStrokeColor($border_color->getPixel()); + $drawing->setStrokeColor($border_color); $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); } diff --git a/src/Drivers/Imagick/Modifiers/RotateModifier.php b/src/Drivers/Imagick/Modifiers/RotateModifier.php index ff908c04c..e178f31ce 100644 --- a/src/Drivers/Imagick/Modifiers/RotateModifier.php +++ b/src/Drivers/Imagick/Modifiers/RotateModifier.php @@ -3,19 +3,19 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRotateModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; class RotateModifier extends AbstractRotateModifier implements ModifierInterface { + use CanHandleColors; + public function apply(ImageInterface $image): ImageInterface { - $background = $this->failIfNotClass($this->backgroundColor(), Color::class); - foreach ($image as $frame) { $frame->getCore()->rotateImage( - $background->getPixel(), + $this->colorToPixel($this->backgroundColor()), $this->rotationAngle() ); } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 7be7f4e0c..c9d1335bd 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -105,11 +105,12 @@ public function testHandleRgbString(): void $this->assertEquals([10, 20, 30, 255], $result->toArray()); } - // public function testHandleTransparent(): void - // { - // $handler = new InputHandler(); - // $input = 'transparent'; - // $result = $handler->handle($input); - // $this->assertInstanceOf(Color::class, $result); - // } + public function testHandleTransparent(): void + { + $handler = new InputHandler(); + $input = 'transparent'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0, 0], $result->toArray()); + } } diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index df2a5b1a6..ed7a99ca3 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -104,4 +104,13 @@ public function testHandleRgbString(): void $this->assertInstanceOf(Color::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } + + public function testHandleTransparent(): void + { + $handler = new InputHandler(); + $input = 'transparent'; + $result = $handler->handle($input); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0, 0], $result->toArray()); + } } From e3c8ca2edf562bd5731625844520ab486ea031c4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 20 Oct 2023 16:16:08 +0200 Subject: [PATCH 0445/1667] Create new images with RGB colorspace --- src/Drivers/Imagick/ImageFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 773f1a532..999965d9b 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -57,7 +57,7 @@ public function newCore(int $width, int $height) $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); $imagick->setType(Imagick::IMGTYPE_UNDEFINED); $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); - $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); + $imagick->setColorspace(Imagick::COLORSPACE_RGB); return $imagick; } From b5cbff3a8968b1a621e494fb609a54388ef40954 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 09:25:36 +0200 Subject: [PATCH 0446/1667] Remove remaining toRgb() calls --- src/Colors/Rgb/Color.php | 5 ---- src/Drivers/Gd/Traits/CanHandleColors.php | 5 ++-- .../Imagick/Decoders/BinaryImageDecoder.php | 1 + src/Interfaces/ColorInterface.php | 8 +++--- tests/Colors/Rgb/ColorTest.php | 6 ----- tests/Drivers/Gd/ImageTest.php | 27 +++++-------------- tests/TestCase.php | 3 ++- 7 files changed, 15 insertions(+), 40 deletions(-) diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 1a5723c54..3ce1313b9 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -76,11 +76,6 @@ public function toHex(string $prefix = ''): string ); } - public function toRgb(): self - { - return $this; - } - public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface { $colorspace = match (true) { diff --git a/src/Drivers/Gd/Traits/CanHandleColors.php b/src/Drivers/Gd/Traits/CanHandleColors.php index ecfa1dd42..938efdfc4 100644 --- a/src/Drivers/Gd/Traits/CanHandleColors.php +++ b/src/Drivers/Gd/Traits/CanHandleColors.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Traits; use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Colorspace; use Intervention\Image\Interfaces\ColorInterface; trait CanHandleColors @@ -33,9 +34,9 @@ public function colorFromInteger(int $value): Color * @param ColorInterface $color * @return int */ - public function colorToInteger(ColorInterface $color): int + public function colorToInteger(Color $color): int { - $color = $color->toRgb(); + $color = $color->convertTo(Colorspace::class); $r = $color->red()->value(); $g = $color->green()->value(); diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index ac4f1488c..886f65eba 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -25,6 +25,7 @@ public function decode($input): ImageInterface|ColorInterface $imagick = new Imagick(); $imagick->readImageBlob($input); $imagick = $imagick->coalesceImages(); + // $imagick->transformImageColorspace(Imagick::COLORSPACE_RGB); $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 6ba4cafe2..1594b8574 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -2,16 +2,14 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Colors\Rgb\Color; - interface ColorInterface { - public function toRgb(): Color; - public function toArray(): array; + public function __toString(): string; public function toString(): string; + public function toArray(): array; public function toHex(): string; - public function __toString(): string; public function channels(): array; + public function channel(string $classname): ColorChannelInterface; public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; public function isGreyscale(): bool; } diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index 131dcef73..bcbbe53ab 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -73,10 +73,4 @@ public function testToString(): void $color = new Color(181, 55, 23); $this->assertEquals('rgb(181, 55, 23)', (string) $color); } - - public function testToRgb(): void - { - $color = new Color(181, 55, 23); - $this->assertInstanceOf(Color::class, $color->toRgb()); - } } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index a878443bf..cd4b7031a 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -96,21 +96,15 @@ public function testPickColor(): void { $color = $this->image->pickColor(0, 0); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(255, $color->toRgb()->red()->value()); - $this->assertEquals(0, $color->toRgb()->green()->value()); - $this->assertEquals(0, $color->toRgb()->blue()->value()); + $this->assertEquals([255, 0, 0, 255], $color->toArray()); $color = $this->image->pickColor(0, 0, 1); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->toRgb()->red()->value()); - $this->assertEquals(255, $color->toRgb()->green()->value()); - $this->assertEquals(0, $color->toRgb()->blue()->value()); + $this->assertEquals([0, 255, 0, 255], $color->toArray()); $color = $this->image->pickColor(0, 0, 2); $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->toRgb()->red()->value()); - $this->assertEquals(0, $color->toRgb()->green()->value()); - $this->assertEquals(255, $color->toRgb()->blue()->value()); + $this->assertEquals([0, 0, 255, 255], $color->toArray()); $color = $this->image->pickColor(0, 0, 3); $this->assertNull($color); @@ -121,17 +115,8 @@ 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)->toRgb()->red()->value()); - $this->assertEquals(0, $colors->get(0)->toRgb()->green()->value()); - $this->assertEquals(0, $colors->get(0)->toRgb()->blue()->value()); - - $this->assertEquals(0, $colors->get(1)->toRgb()->red()->value()); - $this->assertEquals(255, $colors->get(1)->toRgb()->green()->value()); - $this->assertEquals(0, $colors->get(1)->toRgb()->blue()->value()); - - $this->assertEquals(0, $colors->get(2)->toRgb()->red()->value()); - $this->assertEquals(0, $colors->get(2)->toRgb()->green()->value()); - $this->assertEquals(255, $colors->get(2)->toRgb()->blue()->value()); + $this->assertEquals([255, 0, 0, 255], $colors->get(0)->toArray()); + $this->assertEquals([0, 255, 0, 255], $colors->get(1)->toArray()); + $this->assertEquals([0, 0, 255, 255], $colors->get(2)->toArray()); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index cfd60927a..f73c069cc 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Tests; use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Colorspace; use Intervention\Image\Interfaces\ColorInterface; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -26,6 +27,6 @@ protected function assertColor($r, $g, $b, $a, ColorInterface $color) protected function assertTransparency(ColorInterface $color) { $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->toRgb()->alpha()->value()); + $this->assertEquals(0, $color->convertTo(Colorspace::class)->alpha()->value()); } } From 774e1b80a37906b579c8c82c592815638e44ab9c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 09:44:53 +0200 Subject: [PATCH 0447/1667] Fix bugs --- src/Drivers/Gd/Traits/CanHandleColors.php | 8 ++------ src/Drivers/Imagick/Font.php | 7 +++++-- src/Drivers/Imagick/Modifiers/PadModifier.php | 8 +++++--- src/Drivers/Imagick/Traits/CanHandleColors.php | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Drivers/Gd/Traits/CanHandleColors.php b/src/Drivers/Gd/Traits/CanHandleColors.php index 938efdfc4..799292dfa 100644 --- a/src/Drivers/Gd/Traits/CanHandleColors.php +++ b/src/Drivers/Gd/Traits/CanHandleColors.php @@ -3,8 +3,6 @@ namespace Intervention\Image\Drivers\Gd\Traits; use Intervention\Image\Colors\Rgb\Color; -use Intervention\Image\Colors\Rgb\Colorspace; -use Intervention\Image\Interfaces\ColorInterface; trait CanHandleColors { @@ -31,13 +29,11 @@ public function colorFromInteger(int $value): Color /** * Transforms given color to the corresponding GD Library integer value * - * @param ColorInterface $color + * @param Color $color * @return int */ public function colorToInteger(Color $color): int { - $color = $color->convertTo(Colorspace::class); - $r = $color->red()->value(); $g = $color->green()->value(); $b = $color->blue()->value(); @@ -61,7 +57,7 @@ public function colorToInteger(Color $color): int * @param float|int $targetMax * @return float|int */ - private static function convertRange( + protected static function convertRange( float|int $input, float|int $min, float|int $max, diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php index 2e8441bf1..5e612c25b 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/Font.php @@ -5,26 +5,29 @@ use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\AbstractFont; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; class Font extends AbstractFont { + use CanHandleColors; + public function toImagickDraw(): ImagickDraw { if (!$this->hasFilename()) { throw new FontException('No font file specified.'); } - $color = $this->failIfNotClass($this->getColor(), Color::class); + $color = $this->colorToPixel($this->getColor()); $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); - $draw->setFillColor($color->getPixel()); + $draw->setFillColor($color); $draw->setTextAlignment(Imagick::ALIGN_LEFT); return $draw; diff --git a/src/Drivers/Imagick/Modifiers/PadModifier.php b/src/Drivers/Imagick/Modifiers/PadModifier.php index 3b0d38e25..fbfcbd23a 100644 --- a/src/Drivers/Imagick/Modifiers/PadModifier.php +++ b/src/Drivers/Imagick/Modifiers/PadModifier.php @@ -5,7 +5,8 @@ use Imagick; use ImagickDraw; use Intervention\Image\Drivers\Abstract\Modifiers\AbstractPadModifier; -use Intervention\Image\Drivers\Imagick\Color; +use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -16,12 +17,13 @@ class PadModifier extends AbstractPadModifier implements ModifierInterface { use CanBuildNewImage; use CanHandleInput; + use CanHandleColors; public function apply(ImageInterface $image): ImageInterface { $resize = $this->getResizeSize($image); $crop = $this->getCropSize($image); - $background = $this->failIfNotClass($this->handleInput($this->background), Color::class); + $background = $this->handleInput($this->background); foreach ($image as $frame) { // resize current core @@ -59,7 +61,7 @@ protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, C // draw background color on canvas $draw = new ImagickDraw(); - $draw->setFillColor($background->getPixel()); + $draw->setFillColor($this->colorToPixel($background)); $draw->rectangle(0, 0, $canvas->getImageWidth(), $canvas->getImageHeight()); $canvas->drawImage($draw); diff --git a/src/Drivers/Imagick/Traits/CanHandleColors.php b/src/Drivers/Imagick/Traits/CanHandleColors.php index 547030c1b..adb50c41f 100644 --- a/src/Drivers/Imagick/Traits/CanHandleColors.php +++ b/src/Drivers/Imagick/Traits/CanHandleColors.php @@ -13,7 +13,7 @@ trait CanHandleColors /** * Transforms ImagickPixel to own color object * - * @param int $value + * @param ImagickPixel $pixel * @return ColorInterface */ public function colorFromPixel(ImagickPixel $pixel): ColorInterface From e8dec6126bff148b60a5fe9e0cac4defb84e87d1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:00:13 +0200 Subject: [PATCH 0448/1667] Remove unused class --- src/Drivers/Abstract/AbstractColor.php | 35 -------------------------- tests/ModifierStackTest.php | 7 +++--- 2 files changed, 4 insertions(+), 38 deletions(-) delete mode 100644 src/Drivers/Abstract/AbstractColor.php diff --git a/src/Drivers/Abstract/AbstractColor.php b/src/Drivers/Abstract/AbstractColor.php deleted file mode 100644 index fe6bdc0f0..000000000 --- a/src/Drivers/Abstract/AbstractColor.php +++ /dev/null @@ -1,35 +0,0 @@ -red(), - $this->green(), - $this->blue() - ); - } - - /** - * Determine if color is greyscale - * - * @return boolean - */ - public function isGreyscale(): bool - { - return ($this->red() === $this->green()) && ($this->green() === $this->blue()); - } -} diff --git a/tests/ModifierStackTest.php b/tests/ModifierStackTest.php index afe992349..65274e695 100644 --- a/tests/ModifierStackTest.php +++ b/tests/ModifierStackTest.php @@ -4,6 +4,7 @@ use Intervention\Image\Drivers\Gd\Modifiers\GreyscaleModifier; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\ModifierStack; use Mockery; @@ -29,14 +30,14 @@ public function testApply(): void { $image = Mockery::mock(ImageInterface::class); - $modifier1 = Mockery::mock(AbstractColor::class)->makePartial(); + $modifier1 = Mockery::mock(ModifierInterface::class)->makePartial(); $modifier1->shouldReceive('apply')->once()->with($image); - $modifier2 = Mockery::mock(AbstractColor::class)->makePartial(); + $modifier2 = Mockery::mock(ModifierInterface::class)->makePartial(); $modifier2->shouldReceive('apply')->once()->with($image); $stack = new ModifierStack([$modifier1, $modifier2]); $result = $stack->apply($image); - $this->assertInstanceOf(ImageInterface::class, $image); + $this->assertInstanceOf(ImageInterface::class, $result); } } From a62c22ff53a5902acc1a5ef5cce865f249c5f79c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:01:09 +0200 Subject: [PATCH 0449/1667] Change return types --- src/Colors/Rgb/Color.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 3ce1313b9..c05aaf5f1 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -27,22 +27,22 @@ public function __construct(int $r, int $g, int $b, int $a = 255) ]; } - public function red(): Red + public function red(): ColorChannelInterface { return $this->channel(Red::class); } - public function green(): Green + public function green(): ColorChannelInterface { return $this->channel(Green::class); } - public function blue(): Blue + public function blue(): ColorChannelInterface { return $this->channel(Blue::class); } - public function alpha(): Alpha + public function alpha(): ColorChannelInterface { return $this->channel(Alpha::class); } From f7fd1f3c5d8c41656ba70774b8cfebfc92820cfd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:04:29 +0200 Subject: [PATCH 0450/1667] Change return types --- src/Drivers/Abstract/AbstractFont.php | 2 +- src/Interfaces/FontInterface.php | 2 +- src/Interfaces/ImageInterface.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index dd39739cd..a9bc192c7 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -68,7 +68,7 @@ public function color($color): FontInterface return $this; } - public function getColor(): ?ColorInterface + public function getColor(): ColorInterface { return $this->handleInput($this->color); } diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 6ff95e447..efd1b7aa1 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -8,7 +8,7 @@ interface FontInterface { public function color($color): self; - public function getColor(): ?ColorInterface; + public function getColor(): ColorInterface; public function size(float $size): self; public function getSize(): float; public function angle(float $angle): self; diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 45b257db5..cf3083ef5 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -3,7 +3,6 @@ namespace Intervention\Image\Interfaces; use Countable; -use Intervention\Image\Collection; use Intervention\Image\EncodedImage; use Traversable; From 0e285f7a363e09190ccd4a4710741fa6ecf8436d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:10:38 +0200 Subject: [PATCH 0451/1667] Fix bug --- src/Drivers/Imagick/Modifiers/PixelateModifier.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/PixelateModifier.php b/src/Drivers/Imagick/Modifiers/PixelateModifier.php index f59196f7c..d10e017b7 100644 --- a/src/Drivers/Imagick/Modifiers/PixelateModifier.php +++ b/src/Drivers/Imagick/Modifiers/PixelateModifier.php @@ -27,8 +27,8 @@ protected function pixelateFrame(Frame $frame): void $size = $frame->getSize(); $frame->getCore()->scaleImage( - max(1, ($size->getWidth() / $this->size)), - max(1, ($size->getHeight() / $this->size)) + round(max(1, ($size->getWidth() / $this->size))), + round(max(1, ($size->getHeight() / $this->size))) ); $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); From 6f1e27b06f6c50172aa74c34815df23a6040edb9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:20:32 +0200 Subject: [PATCH 0452/1667] Check test result only on color values --- .../Imagick/Modifiers/PixelateModifierTest.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php index 9329b8307..33425b5cc 100644 --- a/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php +++ b/tests/Drivers/Imagick/Modifiers/PixelateModifierTest.php @@ -20,7 +20,15 @@ public function testModify(): void $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); $image->modify(new PixelateModifier(10)); - $this->assertEquals('00aef0', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('6bab8c', $image->pickColor(14, 14)->toHex()); + + list($r, $g, $b) = $image->pickColor(0, 0)->toArray(); + $this->assertEquals(0, $r); + $this->assertEquals(174, $g); + $this->assertEquals(240, $b); + + list($r, $g, $b) = $image->pickColor(14, 14)->toArray(); + $this->assertEquals(107, $r); + $this->assertEquals(171, $g); + $this->assertEquals(140, $b); } } From c236947be625a828a4299ae289cd144a04a7ff4f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:22:42 +0200 Subject: [PATCH 0453/1667] Update mockery version number --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b2591f2e1..808574d08 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ }, "require-dev": { "phpunit/phpunit": "^9", - "mockery/mockery": "^1.4", + "mockery/mockery": "^1.6", "phpstan/phpstan": "^1" }, "autoload": { From a3494a80ced976e519d057303c497de7dcce15b6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:23:32 +0200 Subject: [PATCH 0454/1667] Remove laravel keyword --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 808574d08..b0634405a 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "intervention/image", "description": "PHP image manipulation", "homepage": "https://image.intervention.io/", - "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail"], + "keywords": ["image", "gd", "imagick", "watermark", "thumbnail"], "license": "MIT", "authors": [ { From 09aa08904ce4c309790d0caf3efe9aa2041e5002 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:24:14 +0200 Subject: [PATCH 0455/1667] Add keyword to composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b0634405a..e97407e92 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "intervention/image", "description": "PHP image manipulation", "homepage": "https://image.intervention.io/", - "keywords": ["image", "gd", "imagick", "watermark", "thumbnail"], + "keywords": ["image", "gd", "imagick", "watermark", "thumbnail", "resize"], "license": "MIT", "authors": [ { From 0bc8b7ce9b50de7dc52e9db6499d105cefc062cc Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 10:31:36 +0200 Subject: [PATCH 0456/1667] Remove comment --- src/Drivers/Imagick/Decoders/BinaryImageDecoder.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 886f65eba..ac4f1488c 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -25,7 +25,6 @@ public function decode($input): ImageInterface|ColorInterface $imagick = new Imagick(); $imagick->readImageBlob($input); $imagick = $imagick->coalesceImages(); - // $imagick->transformImageColorspace(Imagick::COLORSPACE_RGB); $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); From f0e7abb56bd1b9f93a0e23c34c00e69ef8d7158c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 11:13:08 +0200 Subject: [PATCH 0457/1667] Image ImageManager instantiation signature --- src/Exceptions/ConfigurationException.php | 8 ++++++++ src/ImageManager.php | 13 ++++++++++--- tests/ImageManagerTest.php | 17 ++++++++++++----- 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 src/Exceptions/ConfigurationException.php diff --git a/src/Exceptions/ConfigurationException.php b/src/Exceptions/ConfigurationException.php new file mode 100644 index 000000000..df77d83a1 --- /dev/null +++ b/src/Exceptions/ConfigurationException.php @@ -0,0 +1,8 @@ +driver); + return strtolower($this->options['driver']); } } diff --git a/tests/ImageManagerTest.php b/tests/ImageManagerTest.php index 1bbbe6453..565eb5f79 100644 --- a/tests/ImageManagerTest.php +++ b/tests/ImageManagerTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests; +use Intervention\Image\Exceptions\ConfigurationException; use Intervention\Image\ImageManager; use Intervention\Image\Interfaces\ImageInterface; @@ -12,14 +13,20 @@ class ImageManagerTest extends TestCase { public function testConstructor() { - $manager = new ImageManager('foo'); + $manager = new ImageManager(['driver' => 'gd']); $this->assertInstanceOf(ImageManager::class, $manager); + + $this->expectException(ConfigurationException::class); + $manager = new ImageManager([]); + + $this->expectException(ConfigurationException::class); + $manager = new ImageManager(['foo' => 'bar']); } /** @requires extension gd */ public function testCreateGd() { - $manager = new ImageManager('gd'); + $manager = new ImageManager(['driver' => 'gd']); $image = $manager->create(5, 4); $this->assertInstanceOf(ImageInterface::class, $image); } @@ -27,7 +34,7 @@ public function testCreateGd() /** @requires extension gd */ public function testReadGd() { - $manager = new ImageManager('gd'); + $manager = new ImageManager(['driver' => 'gd']); $image = $manager->read(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } @@ -35,7 +42,7 @@ public function testReadGd() /** @requires extension imagick */ public function testCreateImagick() { - $manager = new ImageManager('imagick'); + $manager = new ImageManager(['driver' => 'imagick']); $image = $manager->create(5, 4); $this->assertInstanceOf(ImageInterface::class, $image); } @@ -43,7 +50,7 @@ public function testCreateImagick() /** @requires extension imagick */ public function testReadImagick() { - $manager = new ImageManager('imagick'); + $manager = new ImageManager(['driver' => 'imagick']); $image = $manager->read(__DIR__ . '/images/red.gif'); $this->assertInstanceOf(ImageInterface::class, $image); } From 4b7234e0cf06f27abdf2d7ba2353d43c6e646055 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 11:26:52 +0200 Subject: [PATCH 0458/1667] Set default driver configuration --- src/ImageManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageManager.php b/src/ImageManager.php index e6f3ff481..b73c38809 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -11,7 +11,7 @@ class ImageManager private static $required_options = ['driver']; - public function __construct(protected array $options) + public function __construct(protected array $options = ['driver' => 'gd']) { if (count(array_intersect(array_keys($options), self::$required_options)) != count(self::$required_options)) { throw new Exceptions\ConfigurationException( From fc94da24104e2c5bb51a813287ad73fc007f8374 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 11:41:03 +0200 Subject: [PATCH 0459/1667] Refactor code --- src/Drivers/Gd/Modifiers/PlaceModifier.php | 12 +++--------- src/Drivers/Imagick/Modifiers/PlaceModifier.php | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/Drivers/Gd/Modifiers/PlaceModifier.php b/src/Drivers/Gd/Modifiers/PlaceModifier.php index 1c35c435b..a8a9b56ab 100644 --- a/src/Drivers/Gd/Modifiers/PlaceModifier.php +++ b/src/Drivers/Gd/Modifiers/PlaceModifier.php @@ -2,15 +2,14 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; -use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\PointInterface; -use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Traits\CanHandleInput; class PlaceModifier implements ModifierInterface { - use CanResolveDriverClass; + use CanHandleInput; /** * Create new modifier @@ -27,7 +26,7 @@ public function __construct( public function apply(ImageInterface $image): ImageInterface { - $watermark = $this->decodeWatermark(); + $watermark = $this->handleInput($this->element); $position = $this->getPosition($image, $watermark); foreach ($image as $frame) { @@ -47,11 +46,6 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - protected function decodeWatermark(): Image - { - return $this->resolveDriverClass('InputHandler')->handle($this->element); - } - protected function getPosition(ImageInterface $image, ImageInterface $watermark): PointInterface { $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); diff --git a/src/Drivers/Imagick/Modifiers/PlaceModifier.php b/src/Drivers/Imagick/Modifiers/PlaceModifier.php index d6181c422..775e75858 100644 --- a/src/Drivers/Imagick/Modifiers/PlaceModifier.php +++ b/src/Drivers/Imagick/Modifiers/PlaceModifier.php @@ -4,15 +4,14 @@ use Imagick; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\PointInterface; -use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Traits\CanHandleInput; class PlaceModifier implements ModifierInterface { - use CanResolveDriverClass; + use CanHandleInput; public function __construct( protected $element, @@ -25,7 +24,7 @@ public function __construct( public function apply(ImageInterface $image): ImageInterface { - $watermark = $this->decodeWatermark(); + $watermark = $this->handleInput($this->element); $position = $this->getPosition($image, $watermark); foreach ($image as $frame) { @@ -40,11 +39,6 @@ public function apply(ImageInterface $image): ImageInterface return $image; } - protected function decodeWatermark(): Image - { - return $this->resolveDriverClass('InputHandler')->handle($this->element); - } - protected function getPosition(ImageInterface $image, Image $watermark): PointInterface { $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); From 625063ca979a906ce15076ef136a62026f08e13f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 11:43:09 +0200 Subject: [PATCH 0460/1667] Edit readme --- readme.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 7e3fbe720..598b4784a 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,10 @@ [![Build Status](https://github.com/Intervention/image/actions/workflows/run-tests.yml/badge.svg)](https://github.com/Intervention/image/actions) [![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) -Intervention Image is a **image handling and manipulation library written in PHP** providing an easier and expressive way to create, edit, and compose images. GD library or Imagick can be selected as the base layer for all operations. +Intervention Image is a **image handling and manipulation library written in +PHP** providing an easier and expressive way to create, edit, and compose +images. GD library or Imagick can be selected as the base layer for all +operations. - Simple interface for common tasks - Interchangable driver architecture @@ -17,7 +20,7 @@ Intervention Image is a **image handling and manipulation library written in PHP ```php // create image manager with desired driver -$manager = new ImageManager('gd') +$manager = new ImageManager(['driver' => 'gd']); // open an image file $image = $manager->read('images/example.gif'); @@ -52,11 +55,15 @@ composer require intervention/image ## Getting started -Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/v3/). +Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on +how to use Intervention Image and more with the [official +documentation](https://image.intervention.io/v3/). ## Development & Testing -With this package comes a Docker image to build a test suite and analysis container. To build this container you have to have Docker installed on your system. You can run all tests with this command. +With this package comes a Docker image to build a test suite and analysis +container. To build this container you have to have Docker installed on your +system. You can run all tests with this command. ```bash docker-compose run --rm --build tests From eae10d0e72c840cf11aa2f4bf8c33b39eca84b3b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 12:00:49 +0200 Subject: [PATCH 0461/1667] Update Github workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 43cbf28ac..dc5c5722d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -77,7 +77,7 @@ jobs: - name: Get composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir::$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer dependencies uses: actions/cache@v2 From 19efa81afa6339157b2e18e35815a1c082648466 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 12:03:51 +0200 Subject: [PATCH 0462/1667] Fix Github workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index dc5c5722d..30fd3471e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -77,7 +77,7 @@ jobs: - name: Get composer cache directory id: composer-cache - run: echo "dir::$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer dependencies uses: actions/cache@v2 From a1f75482e13eae8a2cab240e33eae6f5c7694cce Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 14:49:22 +0200 Subject: [PATCH 0463/1667] Add type hints --- src/Drivers/Abstract/AbstractInputHandler.php | 2 +- src/Drivers/Gd/InputHandler.php | 2 +- src/Drivers/Imagick/InputHandler.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php index d0f25c9ba..92ac0b234 100644 --- a/src/Drivers/Abstract/AbstractInputHandler.php +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -12,7 +12,7 @@ abstract class AbstractInputHandler /** * Array of decoders which will be stacked into to the input handler chain */ - protected $decoders = []; + protected array $decoders = []; /** * Stack the decoder array into a nested decoder object diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 85d69a74b..2cd3da3b8 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -17,7 +17,7 @@ class InputHandler extends AbstractInputHandler { - protected $decoders = [ + protected array $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, HexColorDecoder::class, diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index 3d27678aa..c41649d02 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -17,7 +17,7 @@ class InputHandler extends AbstractInputHandler { - protected $decoders = [ + protected array $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, HexColorDecoder::class, From 3401fefa4f04ca8e7db87a31d29ffb9bd615b819 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 15:15:56 +0200 Subject: [PATCH 0464/1667] Add static color factory method --- src/Colors/Rgb/Color.php | 85 +++++++++++++++++++ src/Drivers/Abstract/AbstractInputHandler.php | 10 +++ src/Interfaces/ColorInterface.php | 57 +++++++++++++ tests/Colors/Rgb/ColorTest.php | 11 +++ 4 files changed, 163 insertions(+) diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index c05aaf5f1..d71175659 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -7,6 +7,7 @@ use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Alpha; use Intervention\Image\Colors\Traits\CanHandleChannels; +use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Interfaces\ColorChannelInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -15,8 +16,20 @@ class Color implements ColorInterface { use CanHandleChannels; + /** + * Color channels + */ protected array $channels; + /** + * Create new instance + * + * @param int $r + * @param int $g + * @param int $b + * @param int $a + * @return ColorInterface + */ public function __construct(int $r, int $g, int $b, int $a = 255) { $this->channels = [ @@ -27,26 +40,68 @@ public function __construct(int $r, int $g, int $b, int $a = 255) ]; } + /** + * {@inheritdoc} + * + * @see ColorInterface::create() + */ + public static function create(mixed $input): ColorInterface + { + return (new class ([ + Decoders\HexColorDecoder::class, + Decoders\StringColorDecoder::class, + Decoders\TransparentColorDecoder::class, + Decoders\HtmlColornameDecoder::class, + ]) extends AbstractInputHandler + { + })->handle($input); + } + + /** + * Return the RGB red color channel + * + * @return ColorChannelInterface + */ public function red(): ColorChannelInterface { return $this->channel(Red::class); } + /** + * Return the RGB green color channel + * + * @return ColorChannelInterface + */ public function green(): ColorChannelInterface { return $this->channel(Green::class); } + /** + * Return the RGB blue color channel + * + * @return ColorChannelInterface + */ public function blue(): ColorChannelInterface { return $this->channel(Blue::class); } + /** + * Return the colors alpha channel + * + * @return ColorChannelInterface + */ public function alpha(): ColorChannelInterface { return $this->channel(Alpha::class); } + /** + * {@inheritdoc} + * + * @see ColorInterface::toArray() + */ public function toArray(): array { return array_map(function (ColorChannelInterface $channel) { @@ -54,6 +109,11 @@ public function toArray(): array }, $this->channels()); } + /** + * {@inheritdoc} + * + * @see ColorInterface::toHex() + */ public function toHex(string $prefix = ''): string { if ($this->isFullyOpaque()) { @@ -76,6 +136,11 @@ public function toHex(string $prefix = ''): string ); } + /** + * {@inheritdoc} + * + * @see ColorInterface::convertTo() + */ public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface { $colorspace = match (true) { @@ -86,11 +151,21 @@ public function convertTo(string|ColorspaceInterface $colorspace): ColorInterfac return $colorspace->convertColor($this); } + /** + * Determine if the current color is fully opaque + * + * @return bool + */ public function isFullyOpaque(): bool { return $this->alpha()->value() === 255; } + /** + * {@inheritdoc} + * + * @see ColorInterface::toString() + */ public function toString(): string { if ($this->isFullyOpaque()) { @@ -111,6 +186,11 @@ public function toString(): string ); } + /** + * {@inheritdoc} + * + * @see ColorInterface::isGreyscale() + */ public function isGreyscale(): bool { $values = [$this->red()->value(), $this->green()->value(), $this->blue()->value()]; @@ -118,6 +198,11 @@ public function isGreyscale(): bool return count(array_unique($values, SORT_REGULAR)) === 1; } + /** + * {@inheritdoc} + * + * @see ColorInterface::__toString() + */ public function __toString(): string { return $this->toString(); diff --git a/src/Drivers/Abstract/AbstractInputHandler.php b/src/Drivers/Abstract/AbstractInputHandler.php index 92ac0b234..faf90ad50 100644 --- a/src/Drivers/Abstract/AbstractInputHandler.php +++ b/src/Drivers/Abstract/AbstractInputHandler.php @@ -14,6 +14,16 @@ abstract class AbstractInputHandler */ protected array $decoders = []; + /** + * Create new instance + * + * @param array $decoders + */ + public function __construct(array $decoders = []) + { + $this->decoders = count($decoders) ? $decoders : $this->decoders; + } + /** * Stack the decoder array into a nested decoder object * diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 1594b8574..5d40eef25 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -4,12 +4,69 @@ interface ColorInterface { + /** + * Static color factory method that passed input to color decoding input handler + * + * @param mixed $input + * @return ColorInterface + * @throws \Intervention\Image\Exceptions\DecoderException + */ + public static function create(mixed $input): ColorInterface; + + /** + * Cast color object to string + * + * @return string + */ public function __toString(): string; + + /** + * Cast color object to string + * + * @return string + */ public function toString(): string; + + /** + * Cast color object to array + * + * @return array + */ public function toArray(): array; + + /** + * Cast color object to hex encoded web color + * + * @return string + */ public function toHex(): string; + + /** + * Return array of all color channels + * + * @return array + */ public function channels(): array; + + /** + * Retrieve the color channel by its classname + * + * @param string $classname + * @return ColorChannelInterface + */ public function channel(string $classname): ColorChannelInterface; + + /** + * Convert color to given colorspace + * + * @return ColorInterface + */ public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface; + + /** + * Determine if the current color is gray + * + * @return bool + */ public function isGreyscale(): bool; } diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index bcbbe53ab..b4020c3c2 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -23,6 +23,17 @@ public function testConstructor(): void $this->assertInstanceOf(Color::class, $color); } + public function testCreate(): void + { + $color = Color::create('ccc'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([204, 204, 204, 255], $color->toArray()); + + $color = Color::create('rgba(10, 20, 30, .2)'); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals([10, 20, 30, 51], $color->toArray()); + } + public function testChannels(): void { $color = new Color(10, 20, 30); From f73d1a25919352325ad1d724f2f9202912ec9a89 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 15:47:08 +0200 Subject: [PATCH 0465/1667] Add doc blocks --- src/Colors/Rgb/Channels/Red.php | 50 ++++++++++++++++++++++++ src/Interfaces/ColorChannelInterface.php | 42 ++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/src/Colors/Rgb/Channels/Red.php b/src/Colors/Rgb/Channels/Red.php index da8bf81b0..510d628cd 100644 --- a/src/Colors/Rgb/Channels/Red.php +++ b/src/Colors/Rgb/Channels/Red.php @@ -9,31 +9,71 @@ class Red implements ColorChannelInterface { protected int $value; + /** + * Create and validate new instance + * + * @param int $value + */ public function __construct(int $value) { $this->value = $this->validate($value); } + /** + * Alias of value() + * + * @return int + */ + public function toInt(): int + { + return $this->value; + } + + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::value() + */ public function value(): int { return $this->value; } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::normalize() + */ public function normalize($precision = 32): float { return round($this->value() / $this->max(), $precision); } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::min() + */ public function min(): int { return 0; } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::max() + */ public function max(): int { return 255; } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::validate() + */ public function validate(mixed $value): mixed { if ($value < $this->min() || $value > $this->max()) { @@ -43,11 +83,21 @@ public function validate(mixed $value): mixed return $value; } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::toString() + */ public function toString(): string { return (string) $this->value(); } + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::__toString() + */ public function __toString(): string { return $this->toString(); diff --git a/src/Interfaces/ColorChannelInterface.php b/src/Interfaces/ColorChannelInterface.php index 6c3c7ce86..7c061ee16 100644 --- a/src/Interfaces/ColorChannelInterface.php +++ b/src/Interfaces/ColorChannelInterface.php @@ -4,11 +4,53 @@ interface ColorChannelInterface { + /** + * Return color channels integer value + * + * @return int + */ public function value(): int; + + /** + * Return the channels value normalized to a float value form 0 to 1 by its range + * + * @return float + */ public function normalize(int $precision = 32): float; + + /** + * Throw exception if the given value is not applicable for channel + * otherwise the value is returned unchanged. + * + * @return mixed + */ public function validate(mixed $value): mixed; + + /** + * Return the the minimal possible value of the color channel + * + * @return int + */ public function min(): int; + + /* + * Return the the maximal possible value of the color channel + * + * @return int + */ public function max(): int; + + /** + * Cast color channel's value to string + * + * @return string + */ public function toString(): string; + + /** + * Cast color channel's value to string + * + * @return string + */ public function __toString(): string; } From 00d53e24df4ed3f22e89379922bdb0bc7adae5a1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 15:56:18 +0200 Subject: [PATCH 0466/1667] Save all images in SRGB color space with Imagick driver --- src/Drivers/Imagick/Encoders/JpegEncoder.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index c61917044..7921e46f9 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -30,6 +30,10 @@ public function encode(ImageInterface $image): EncodedImage $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); + if ($imagick->getImageColorspace() != Imagick::COLORSPACE_SRGB) { + $imagick->transformImageColorspace(Imagick::COLORSPACE_SRGB); + } + return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg'); } } From fb161a9ad33be4b34a6d34c6cded2a7c6b655cfa Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 17:40:03 +0200 Subject: [PATCH 0467/1667] Add missing offset calculations --- src/Geometry/Rectangle.php | 8 ++++---- tests/Geometry/RectangleTest.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Geometry/Rectangle.php b/src/Geometry/Rectangle.php index b244a60c5..06dfaec42 100644 --- a/src/Geometry/Rectangle.php +++ b/src/Geometry/Rectangle.php @@ -91,7 +91,7 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 case 'top-middle': case 'center-top': case 'middle-top': - $x = intval($this->getWidth() / 2); + $x = intval($this->getWidth() / 2) + $offset_x; $y = 0 + $offset_y; break; @@ -107,7 +107,7 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 case 'center-left': case 'middle-left': $x = 0 + $offset_x; - $y = intval($this->getHeight() / 2); + $y = intval($this->getHeight() / 2) + $offset_y; break; case 'right': @@ -116,7 +116,7 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 case 'center-right': case 'middle-right': $x = $this->getWidth() - $offset_x; - $y = intval($this->getHeight() / 2); + $y = intval($this->getHeight() / 2) + $offset_y; break; case 'bottom-left': @@ -130,7 +130,7 @@ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0 case 'bottom-middle': case 'center-bottom': case 'middle-bottom': - $x = intval($this->getWidth() / 2); + $x = intval($this->getWidth() / 2) + $offset_x; $y = $this->getHeight() - $offset_y; break; diff --git a/tests/Geometry/RectangleTest.php b/tests/Geometry/RectangleTest.php index 7eebd9966..4ad785c80 100644 --- a/tests/Geometry/RectangleTest.php +++ b/tests/Geometry/RectangleTest.php @@ -136,7 +136,7 @@ public function testAlignPivot(): void $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('top', 3, 3); - $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(323, $box->getPivot()->getX()); $this->assertEquals(3, $box->getPivot()->getY()); $box->movePivot('top-right', 3, 3); @@ -145,7 +145,7 @@ public function testAlignPivot(): void $box->movePivot('left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); - $this->assertEquals(240, $box->getPivot()->getY()); + $this->assertEquals(243, $box->getPivot()->getY()); $box->movePivot('center', 3, 3); $this->assertEquals(323, $box->getPivot()->getX()); @@ -153,14 +153,14 @@ public function testAlignPivot(): void $box->movePivot('right', 3, 3); $this->assertEquals(637, $box->getPivot()->getX()); - $this->assertEquals(240, $box->getPivot()->getY()); + $this->assertEquals(243, $box->getPivot()->getY()); $box->movePivot('bottom-left', 3, 3); $this->assertEquals(3, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); $box->movePivot('bottom', 3, 3); - $this->assertEquals(320, $box->getPivot()->getX()); + $this->assertEquals(323, $box->getPivot()->getX()); $this->assertEquals(477, $box->getPivot()->getY()); $result = $box->movePivot('bottom-right', 3, 3); From 019c333b220e02565bf4084b9a7b120e9f9c1141 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 17:47:35 +0200 Subject: [PATCH 0468/1667] Refactor & add docblocks --- src/Drivers/Gd/ImageFactory.php | 12 +++++++++++- src/Drivers/Imagick/ImageFactory.php | 12 +++++++++++- src/Interfaces/DecoderInterface.php | 6 ++++++ src/Interfaces/EncoderInterface.php | 6 ++++++ src/Interfaces/FactoryInterface.php | 16 +++++++++++++++- src/Interfaces/ModifierInterface.php | 6 ++++++ src/Interfaces/PointInterface.php | 11 +++++++++++ tests/Drivers/Gd/ImageFactoryTest.php | 8 -------- tests/Drivers/Imagick/ImageFactoryTest.php | 2 +- 9 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index 1d92a3689..f6de784de 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -13,6 +13,11 @@ class ImageFactory implements FactoryInterface { use CanHandleInput; + /** + * {@inheritdoc} + * + * @see FactoryInterface::newImage() + */ public function newImage(int $width, int $height): ImageInterface { return new Image( @@ -22,6 +27,11 @@ public function newImage(int $width, int $height): ImageInterface ); } + /** + * {@inheritdoc} + * + * @see FactoryInterface::newAnimation() + */ public function newAnimation(callable $callback): ImageInterface { $frames = new Collection(); @@ -50,7 +60,7 @@ public function add($source, float $delay = 1): self return new Image($frames); } - public function newCore(int $width, int $height) + protected function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); $color = imagecolorallocatealpha($core, 0, 0, 0, 127); diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 999965d9b..12e5abd14 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -15,11 +15,21 @@ class ImageFactory implements FactoryInterface use CanHandleInput; use CanCheckType; + /** + * {@inheritdoc} + * + * @see FactoryInterface::newImage() + */ public function newImage(int $width, int $height): ImageInterface { return new Image($this->newCore($width, $height)); } + /** + * {@inheritdoc} + * + * @see FactoryInterface::newAnimation() + */ public function newAnimation(callable $callback): ImageInterface { $imagick = new Imagick(); @@ -51,7 +61,7 @@ public function add($source, float $delay = 1): self return new Image($animation->imagick); } - public function newCore(int $width, int $height) + protected function newCore(int $width, int $height) { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); diff --git a/src/Interfaces/DecoderInterface.php b/src/Interfaces/DecoderInterface.php index 04ea26600..3ec11957e 100644 --- a/src/Interfaces/DecoderInterface.php +++ b/src/Interfaces/DecoderInterface.php @@ -4,5 +4,11 @@ interface DecoderInterface { + /** + * Decode given input either to color or image + * + * @param mixed $input + * @return ImageInterface|ColorInterface + */ public function decode($input): ImageInterface|ColorInterface; } diff --git a/src/Interfaces/EncoderInterface.php b/src/Interfaces/EncoderInterface.php index c2d9c41c2..adb149a08 100644 --- a/src/Interfaces/EncoderInterface.php +++ b/src/Interfaces/EncoderInterface.php @@ -6,5 +6,11 @@ interface EncoderInterface { + /** + * Encode given image + * + * @param ImageInterface $image + * @return EncodedImage + */ public function encode(ImageInterface $image): EncodedImage; } diff --git a/src/Interfaces/FactoryInterface.php b/src/Interfaces/FactoryInterface.php index 1f7e9f089..8e948a0c1 100644 --- a/src/Interfaces/FactoryInterface.php +++ b/src/Interfaces/FactoryInterface.php @@ -4,6 +4,20 @@ interface FactoryInterface { + /** + * Create new image in the given size + * + * @param int $width + * @param int $height + * @return ImageInterface + */ public function newImage(int $width, int $height): ImageInterface; - public function newCore(int $width, int $height); + + /** + * Create new animated image + * + * @param callable $callback + * @return ImageInterface + */ + public function newAnimation(callable $callback): ImageInterface; } diff --git a/src/Interfaces/ModifierInterface.php b/src/Interfaces/ModifierInterface.php index ff6b12a47..8786ae664 100644 --- a/src/Interfaces/ModifierInterface.php +++ b/src/Interfaces/ModifierInterface.php @@ -4,5 +4,11 @@ interface ModifierInterface { + /** + * Apply modifications of the current modifier to the given image + * + * @param ImageInterface $image + * @return ImageInterface + */ public function apply(ImageInterface $image): ImageInterface; } diff --git a/src/Interfaces/PointInterface.php b/src/Interfaces/PointInterface.php index 858e29255..56934ce27 100644 --- a/src/Interfaces/PointInterface.php +++ b/src/Interfaces/PointInterface.php @@ -4,6 +4,17 @@ interface PointInterface { + /** + * Return x position + * + * @return int + */ public function getX(): int; + + /** + * Return y position + * + * @return int + */ public function getY(): int; } diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 38060e45e..797be8ada 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use GdImage; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; @@ -30,11 +29,4 @@ public function testNewAnimation(): void $this->assertInstanceOf(Image::class, $image); $this->assertEquals(2, $image->count()); } - - public function testNewCore(): void - { - $factory = new ImageFactory(); - $core = $factory->newCore(3, 2); - $this->assertInstanceOf(GdImage::class, $core); - } } diff --git a/tests/Drivers/Imagick/ImageFactoryTest.php b/tests/Drivers/Imagick/ImageFactoryTest.php index 635139e54..feb8e1313 100644 --- a/tests/Drivers/Imagick/ImageFactoryTest.php +++ b/tests/Drivers/Imagick/ImageFactoryTest.php @@ -31,7 +31,7 @@ public function testNewAnimation(): void $this->assertEquals(2, $image->count()); } - public function testNewCore(): void + protected function testNewCore(): void { $factory = new ImageFactory(); $core = $factory->newCore(3, 2); From 2acb4c547f2c6c0ab9f9710c30e692839f70cb7c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 21 Oct 2023 17:56:23 +0200 Subject: [PATCH 0469/1667] Change method signature --- src/Drivers/Gd/ImageFactory.php | 2 +- src/Drivers/Imagick/ImageFactory.php | 2 +- src/Interfaces/FactoryInterface.php | 8 ++++++++ tests/Drivers/Gd/ImageFactoryTest.php | 8 ++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Drivers/Gd/ImageFactory.php b/src/Drivers/Gd/ImageFactory.php index f6de784de..134e6a815 100644 --- a/src/Drivers/Gd/ImageFactory.php +++ b/src/Drivers/Gd/ImageFactory.php @@ -60,7 +60,7 @@ public function add($source, float $delay = 1): self return new Image($frames); } - protected function newCore(int $width, int $height) + public function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); $color = imagecolorallocatealpha($core, 0, 0, 0, 127); diff --git a/src/Drivers/Imagick/ImageFactory.php b/src/Drivers/Imagick/ImageFactory.php index 12e5abd14..7fd67e068 100644 --- a/src/Drivers/Imagick/ImageFactory.php +++ b/src/Drivers/Imagick/ImageFactory.php @@ -61,7 +61,7 @@ public function add($source, float $delay = 1): self return new Image($animation->imagick); } - protected function newCore(int $width, int $height) + public function newCore(int $width, int $height) { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); diff --git a/src/Interfaces/FactoryInterface.php b/src/Interfaces/FactoryInterface.php index 8e948a0c1..8a491bb73 100644 --- a/src/Interfaces/FactoryInterface.php +++ b/src/Interfaces/FactoryInterface.php @@ -20,4 +20,12 @@ public function newImage(int $width, int $height): ImageInterface; * @return ImageInterface */ public function newAnimation(callable $callback): ImageInterface; + + /** + * Create new driver specific core image object + * + * @param int $width + * @param int $height + */ + public function newCore(int $width, int $height); } diff --git a/tests/Drivers/Gd/ImageFactoryTest.php b/tests/Drivers/Gd/ImageFactoryTest.php index 797be8ada..38060e45e 100644 --- a/tests/Drivers/Gd/ImageFactoryTest.php +++ b/tests/Drivers/Gd/ImageFactoryTest.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; +use GdImage; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\ImageFactory; use Intervention\Image\Tests\TestCase; @@ -29,4 +30,11 @@ public function testNewAnimation(): void $this->assertInstanceOf(Image::class, $image); $this->assertEquals(2, $image->count()); } + + public function testNewCore(): void + { + $factory = new ImageFactory(); + $core = $factory->newCore(3, 2); + $this->assertInstanceOf(GdImage::class, $core); + } } From f16b56103a6f6e04e75b32547d0e1f827b22d2e5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 Oct 2023 09:12:15 +0200 Subject: [PATCH 0470/1667] Revert "Remove Cmyk Colors for now" This reverts commit f24a33705815b7f48351d4a157795cb064d8747b. --- src/Colors/Cmyk/Channels/Cyan.php | 55 +++++++++ src/Colors/Cmyk/Channels/Key.php | 8 ++ src/Colors/Cmyk/Channels/Magenta.php | 8 ++ src/Colors/Cmyk/Channels/Yellow.php | 8 ++ src/Colors/Cmyk/Color.php | 115 ++++++++++++++++++ src/Colors/Cmyk/Colorspace.php | 38 ++++++ .../Cmyk/Decoders/StringColorDecoder.php | 37 ++++++ src/Colors/Rgb/Color.php | 5 - src/Colors/Rgb/Colorspace.php | 11 ++ src/Drivers/Gd/InputHandler.php | 10 +- src/Drivers/Imagick/InputHandler.php | 10 +- tests/Colors/Cmyk/ChannelTest.php | 48 ++++++++ tests/Colors/Cmyk/ColorTest.php | 69 +++++++++++ tests/Colors/Cmyk/ColorspaceTest.php | 26 ++++ .../Cmyk/Decoders/StringColorDecoderTest.php | 35 ++++++ tests/Colors/Rgb/ColorTest.php | 35 ++++++ tests/Colors/Rgb/ColorspaceTest.php | 26 ++++ tests/Drivers/Gd/InputHandlerTest.php | 20 +-- tests/Drivers/Imagick/InputHandlerTest.php | 20 +-- tests/TestCase.php | 9 +- 20 files changed, 556 insertions(+), 37 deletions(-) create mode 100644 src/Colors/Cmyk/Channels/Cyan.php create mode 100644 src/Colors/Cmyk/Channels/Key.php create mode 100644 src/Colors/Cmyk/Channels/Magenta.php create mode 100644 src/Colors/Cmyk/Channels/Yellow.php create mode 100644 src/Colors/Cmyk/Color.php create mode 100644 src/Colors/Cmyk/Colorspace.php create mode 100644 src/Colors/Cmyk/Decoders/StringColorDecoder.php create mode 100644 tests/Colors/Cmyk/ChannelTest.php create mode 100644 tests/Colors/Cmyk/ColorTest.php create mode 100644 tests/Colors/Cmyk/ColorspaceTest.php create mode 100644 tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php create mode 100644 tests/Colors/Rgb/ColorspaceTest.php diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php new file mode 100644 index 000000000..213fea38b --- /dev/null +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -0,0 +1,55 @@ +value = $this->validate($value); + } + + public function value(): int + { + return $this->value; + } + + public function normalize($precision = 32): float + { + return round($this->value() / $this->max(), $precision); + } + + public function min(): int + { + return 0; + } + + public function max(): int + { + return 100; + } + + public function validate(mixed $value): mixed + { + if ($value < $this->min() || $value > $this->max()) { + throw new ColorException('CMYK color values must be in range 0-100.'); + } + + return $value; + } + + public function toString(): string + { + return (string) $this->value(); + } + + public function __toString(): string + { + return $this->toString(); + } +} diff --git a/src/Colors/Cmyk/Channels/Key.php b/src/Colors/Cmyk/Channels/Key.php new file mode 100644 index 000000000..2893d2afb --- /dev/null +++ b/src/Colors/Cmyk/Channels/Key.php @@ -0,0 +1,8 @@ +channels = [ + new Cyan($c), + new Magenta($m), + new Yellow($y), + new Key($k), + ]; + } + + public static function create(mixed $input): ColorInterface + { + return (new class ([ + Decoders\StringColorDecoder::class, + ]) extends AbstractInputHandler + { + })->handle($input); + } + + public function toHex(): string + { + return $this->convertTo(RgbColorspace::class)->toHex(); + } + + public function channels(): array + { + return $this->channels; + } + + public function cyan(): ColorChannelInterface + { + return $this->channel(Cyan::class); + } + + public function magenta(): ColorChannelInterface + { + return $this->channel(Magenta::class); + } + + public function yellow(): ColorChannelInterface + { + return $this->channel(Yellow::class); + } + + public function key(): ColorChannelInterface + { + return $this->channel(Key::class); + } + + public function toArray(): array + { + return [ + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + $this->key()->value(), + ]; + } + + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface + { + $colorspace = match (true) { + is_object($colorspace) => $colorspace, + default => new $colorspace(), + }; + + return $colorspace->convertColor($this); + } + + public function toString(): string + { + return sprintf( + 'cmyk(%d, %d, %d, %d)', + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + $this->key()->value() + ); + } + + public function isGreyscale(): bool + { + return 0 === array_sum([ + $this->cyan()->value(), + $this->magenta()->value(), + $this->yellow()->value(), + ]); + } + + public function __toString(): string + { + return $this->toString(); + } +} diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php new file mode 100644 index 000000000..76ce8ea33 --- /dev/null +++ b/src/Colors/Cmyk/Colorspace.php @@ -0,0 +1,38 @@ + $this->convertRgbColor($color), + default => $color, + }; + } + + protected function convertRgbColor(RgbColor $color): CmykColor + { + $c = (255 - $color->red()->value()) / 255.0 * 100; + $m = (255 - $color->green()->value()) / 255.0 * 100; + $y = (255 - $color->blue()->value()) / 255.0 * 100; + $k = intval(round(min([$c, $m, $y]))); + + $c = intval(round($c - $k)); + $m = intval(round($m - $k)); + $y = intval(round($y - $k)); + + return new CmykColor($c, $m, $y, $k); + } +} diff --git a/src/Colors/Cmyk/Decoders/StringColorDecoder.php b/src/Colors/Cmyk/Decoders/StringColorDecoder.php new file mode 100644 index 000000000..0f05ada95 --- /dev/null +++ b/src/Colors/Cmyk/Decoders/StringColorDecoder.php @@ -0,0 +1,37 @@ +[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + $values = array_map(function ($value) { + return intval(round(floatval(trim(str_replace('%', '', $value))))); + }, [$matches['c'], $matches['m'], $matches['y'], $matches['k']]); + + return new Color(...$values); + } +} diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index d71175659..9cd30fb52 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -151,11 +151,6 @@ public function convertTo(string|ColorspaceInterface $colorspace): ColorInterfac return $colorspace->convertColor($this); } - /** - * Determine if the current color is fully opaque - * - * @return bool - */ public function isFullyOpaque(): bool { return $this->alpha()->value() === 255; diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index 4dbaae082..4c0bcb0f2 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Colors\Rgb; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -10,7 +11,17 @@ class Colorspace implements ColorspaceInterface public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { + CmykColor::class => $this->convertCmykColor($color), default => $color, }; } + + protected function convertCmykColor(CmykColor $color): Color + { + return new Color( + (int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())), + (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), + ); + } } diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 2cd3da3b8..a23deeaa7 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Drivers\Gd; -use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; -use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; +use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; @@ -20,8 +21,9 @@ class InputHandler extends AbstractInputHandler protected array $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, - HexColorDecoder::class, - StringColorDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index c41649d02..55d870553 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -2,10 +2,11 @@ namespace Intervention\Image\Drivers\Imagick; -use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; -use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder; +use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder; use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; +use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; use Intervention\Image\Drivers\Abstract\AbstractInputHandler; use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; @@ -20,8 +21,9 @@ class InputHandler extends AbstractInputHandler protected array $decoders = [ ImageObjectDecoder::class, ColorObjectDecoder::class, - HexColorDecoder::class, - StringColorDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/tests/Colors/Cmyk/ChannelTest.php b/tests/Colors/Cmyk/ChannelTest.php new file mode 100644 index 000000000..44928abf6 --- /dev/null +++ b/tests/Colors/Cmyk/ChannelTest.php @@ -0,0 +1,48 @@ +assertInstanceOf(Channel::class, $channel); + } + + public function testValue(): void + { + $channel = new Channel(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Channel(100); + $this->assertEquals(1, $channel->normalize()); + $channel = new Channel(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Channel(20); + $this->assertEquals(.2, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Channel(101); + + $this->expectException(ColorException::class); + new Channel(-1); + } +} diff --git a/tests/Colors/Cmyk/ColorTest.php b/tests/Colors/Cmyk/ColorTest.php new file mode 100644 index 000000000..5dd59c280 --- /dev/null +++ b/tests/Colors/Cmyk/ColorTest.php @@ -0,0 +1,69 @@ +assertInstanceOf(Color::class, $color); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertIsArray($color->channels()); + $this->assertCount(4, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30, 40); + $channel = $color->channel(Cyan::class); + $this->assertInstanceOf(Cyan::class, $channel); + $this->assertEquals(10, $channel->value()); + } + + public function testCyanMagentaYellowKey(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertInstanceOf(Cyan::class, $color->cyan()); + $this->assertInstanceOf(Magenta::class, $color->magenta()); + $this->assertInstanceOf(Yellow::class, $color->yellow()); + $this->assertInstanceOf(Key::class, $color->key()); + $this->assertEquals(10, $color->cyan()->value()); + $this->assertEquals(20, $color->magenta()->value()); + $this->assertEquals(30, $color->yellow()->value()); + $this->assertEquals(40, $color->key()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30, 40); + $this->assertEquals([10, 20, 30, 40], $color->toArray()); + } + + public function testNormalize(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals([1.0, 0.5, 0.2, 0.0], $color->normalize()); + } + + public function testToString(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals('cmyk(100, 50, 20, 0)', (string) $color); + } +} diff --git a/tests/Colors/Cmyk/ColorspaceTest.php b/tests/Colors/Cmyk/ColorspaceTest.php new file mode 100644 index 000000000..818269fcf --- /dev/null +++ b/tests/Colors/Cmyk/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + CmykColor::class, + $colorspace->convertColor( + new RgbColor(0, 0, 0) + ) + ); + } +} diff --git a/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php new file mode 100644 index 000000000..f6af4237d --- /dev/null +++ b/tests/Colors/Cmyk/Decoders/StringColorDecoderTest.php @@ -0,0 +1,35 @@ +decode('cmyk(0,0,0,0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0, 0], $result->toArray()); + + $result = $decoder->decode('cmyk(0, 100, 100, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + + $result = $decoder->decode('cmyk(0, 100, 100, 0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + + + $result = $decoder->decode('cmyk(0%, 100%, 100%, 0%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100, 0], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/ColorTest.php b/tests/Colors/Rgb/ColorTest.php index b4020c3c2..69a1b4b1d 100644 --- a/tests/Colors/Rgb/ColorTest.php +++ b/tests/Colors/Rgb/ColorTest.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Tests\Colors\Rgb; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Blue; @@ -84,4 +86,37 @@ public function testToString(): void $color = new Color(181, 55, 23); $this->assertEquals('rgb(181, 55, 23)', (string) $color); } + + public function testConvertTo(): void + { + $color = new Color(0, 0, 0); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 100], $converted->toArray()); + + $color = new Color(255, 255, 255); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 0, 0], $converted->toArray()); + + $color = new Color(255, 0, 0); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 100, 100, 0], $converted->toArray()); + + $color = new Color(255, 0, 255); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 100, 0, 0], $converted->toArray()); + + $color = new Color(255, 255, 0); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 0, 100, 0], $converted->toArray()); + + $color = new Color(255, 204, 204); + $converted = $color->convertTo(CmykColorspace::class); + $this->assertInstanceOf(CmykColor::class, $converted); + $this->assertEquals([0, 20, 20, 0], $converted->toArray()); + } } diff --git a/tests/Colors/Rgb/ColorspaceTest.php b/tests/Colors/Rgb/ColorspaceTest.php new file mode 100644 index 000000000..be7808c5f --- /dev/null +++ b/tests/Colors/Rgb/ColorspaceTest.php @@ -0,0 +1,26 @@ +assertInstanceOf( + RgbColor::class, + $colorspace->convertColor( + new CmykColor(0, 0, 0, 0) + ) + ); + } +} diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index c9d1335bd..177213f32 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; -use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Drivers\Gd\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -58,37 +58,37 @@ public function testHandleHexColor(): void $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -96,12 +96,12 @@ public function testHandleRgbString(): void { $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } @@ -110,7 +110,7 @@ public function testHandleTransparent(): void $handler = new InputHandler(); $input = 'transparent'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([0, 0, 0, 0], $result->toArray()); } } diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index ed7a99ca3..876a59e05 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,7 +2,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; @@ -58,37 +58,37 @@ public function testHandleHexColor(): void $handler = new InputHandler(); $input = 'ccff33'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = 'cf3'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([204, 255, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#123456'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([18, 52, 86, 255], $result->toArray()); $handler = new InputHandler(); $input = '#333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 255], $result->toArray()); $handler = new InputHandler(); $input = '#3333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); $handler = new InputHandler(); $input = '#33333333'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([51, 51, 51, 51], $result->toArray()); } @@ -96,12 +96,12 @@ public function testHandleRgbString(): void { $handler = new InputHandler(); $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); $handler = new InputHandler(); $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([10, 20, 30, 255], $result->toArray()); } @@ -110,7 +110,7 @@ public function testHandleTransparent(): void $handler = new InputHandler(); $input = 'transparent'; $result = $handler->handle($input); - $this->assertInstanceOf(Color::class, $result); + $this->assertInstanceOf(RgbColor::class, $result); $this->assertEquals([0, 0, 0, 0], $result->toArray()); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index f73c069cc..8f76a724c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,8 +2,8 @@ namespace Intervention\Image\Tests; -use Intervention\Image\Colors\Rgb\Color; -use Intervention\Image\Colors\Rgb\Colorspace; +use Intervention\Image\Colors\Rgb\Channels\Alpha; +use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Interfaces\ColorInterface; use Mockery\Adapter\Phpunit\MockeryTestCase; @@ -26,7 +26,8 @@ protected function assertColor($r, $g, $b, $a, ColorInterface $color) protected function assertTransparency(ColorInterface $color) { - $this->assertInstanceOf(Color::class, $color); - $this->assertEquals(0, $color->convertTo(Colorspace::class)->alpha()->value()); + $this->assertInstanceOf(RgbColor::class, $color); + $channel = $color->channel(Alpha::class); + $this->assertEquals(0, $channel->value()); } } From 24c807120014370c89d19912300176990ab09175 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 22 Oct 2023 12:10:16 +0200 Subject: [PATCH 0471/1667] Add colorspace transformation --- src/Colors/Cmyk/Channels/Cyan.php | 15 +++- src/Colors/Cmyk/Colorspace.php | 21 ++++++ src/Colors/Rgb/Channels/Red.php | 14 ++-- src/Colors/Rgb/Colorspace.php | 21 ++++++ src/Drivers/Abstract/AbstractImage.php | 2 + src/Drivers/Gd/Image.php | 26 +++++++ src/Drivers/Imagick/Encoders/JpegEncoder.php | 4 -- src/Drivers/Imagick/Image.php | 34 ++++++++- .../Imagick/Modifiers/ColorspaceModifier.php | 66 ++++++++++++++++++ .../Imagick/Traits/CanHandleColors.php | 26 +++++-- src/Interfaces/ColorChannelInterface.php | 8 +++ src/Interfaces/ColorspaceInterface.php | 8 +++ src/Interfaces/ImageInterface.php | 15 ++++ tests/Colors/Cmyk/ChannelTest.php | 12 ++++ tests/Colors/Rgb/ChannelTest.php | 12 ++++ tests/Drivers/Gd/ImageTest.php | 32 +++++++++ .../Decoders/BinaryImageDecoderTest.php | 11 +++ tests/Drivers/Imagick/ImageTest.php | 34 +++++++++ tests/images/cmyk.jpg | Bin 0 -> 2750493 bytes 19 files changed, 343 insertions(+), 18 deletions(-) create mode 100644 src/Drivers/Imagick/Modifiers/ColorspaceModifier.php create mode 100644 tests/images/cmyk.jpg diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php index 213fea38b..8da3a429b 100644 --- a/src/Colors/Cmyk/Channels/Cyan.php +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -9,9 +9,20 @@ class Cyan implements ColorChannelInterface { protected int $value; - public function __construct(int $value) + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::__construct() + */ + public function __construct(int $value = null, float $normalized = null) { - $this->value = $this->validate($value); + $this->value = $this->validate( + match (true) { + is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())), + is_numeric($value) && is_null($normalized) => $value, + default => throw new ColorException('Color channels must either have a value or a normalized value') + } + ); } public function value(): int diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php index 76ce8ea33..c05749b5a 100644 --- a/src/Colors/Cmyk/Colorspace.php +++ b/src/Colors/Cmyk/Colorspace.php @@ -9,6 +9,27 @@ class Colorspace implements ColorspaceInterface { + public static $channels = [ + Channels\Cyan::class, + Channels\Magenta::class, + Channels\Yellow::class, + Channels\Key::class + ]; + + /** + * {@inheritdoc} + * + * @see ColorspaceInterface::createColor() + */ + public function colorFromNormalized(array $normalized): ColorInterface + { + $values = array_map(function ($classname, $value_normalized) { + return (new $classname(normalized: $value_normalized))->value(); + }, self::$channels, $normalized); + + return new Color(...$values); + } + /** * {@inheritdoc} * diff --git a/src/Colors/Rgb/Channels/Red.php b/src/Colors/Rgb/Channels/Red.php index 510d628cd..68c3e1301 100644 --- a/src/Colors/Rgb/Channels/Red.php +++ b/src/Colors/Rgb/Channels/Red.php @@ -10,13 +10,19 @@ class Red implements ColorChannelInterface protected int $value; /** - * Create and validate new instance + * {@inheritdoc} * - * @param int $value + * @see ColorChannelInterface::__construct() */ - public function __construct(int $value) + public function __construct(int $value = null, float $normalized = null) { - $this->value = $this->validate($value); + $this->value = $this->validate( + match (true) { + is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())), + is_numeric($value) && is_null($normalized) => $value, + default => throw new ColorException('Color channels must either have a value or a normalized value') + } + ); } /** diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index 4c0bcb0f2..f3dd1503b 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -8,6 +8,27 @@ class Colorspace implements ColorspaceInterface { + public static $channels = [ + Channels\Red::class, + Channels\Green::class, + Channels\Blue::class, + Channels\Alpha::class + ]; + + /** + * {@inheritdoc} + * + * @see ColorspaceInterface::createColor() + */ + public function colorFromNormalized(array $normalized): ColorInterface + { + $values = array_map(function ($classname, $value_normalized) { + return (new $classname(normalized: $value_normalized))->value(); + }, self::$channels, $normalized); + + return new Color(...$values); + } + public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3b46865fa..3a67cc219 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -11,6 +11,7 @@ use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\CollectionInterface; +use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; @@ -25,6 +26,7 @@ abstract class AbstractImage implements ImageInterface use CanHandleInput; use CanRunCallback; + protected ColorspaceInterface $colorspace; protected Collection $exif; public function eachFrame(callable $callback): ImageInterface diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index 5fdb2ed54..afb573a22 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -3,9 +3,12 @@ namespace Intervention\Image\Drivers\Gd; use Intervention\Image\Collection; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; +use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use IteratorAggregate; @@ -79,4 +82,27 @@ public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface return null; } + + public function getColorspace(): ColorspaceInterface + { + return new RgbColorspace(); + } + + /** + * {@inheritdoc} + * + * @see ImageInterface::setColorspace() + */ + public function setColorspace(string|ColorspaceInterface $target): ImageInterface + { + if (is_string($target) && !in_array($target, ['rgb', RgbColorspace::class])) { + throw new NotSupportedException('Only RGB colorspace is supported with GD driver.'); + } + + if (is_object($target) && !is_a($target, RgbColorspace::class)) { + throw new NotSupportedException('Only RGB colorspace is supported with GD driver.'); + } + + return $this; + } } diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index 7921e46f9..c61917044 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -30,10 +30,6 @@ public function encode(ImageInterface $image): EncodedImage $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); - if ($imagick->getImageColorspace() != Imagick::COLORSPACE_SRGB) { - $imagick->transformImageColorspace(Imagick::COLORSPACE_SRGB); - } - return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg'); } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 657faf8d1..d04b84058 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -4,9 +4,13 @@ use Imagick; use ImagickException; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Drivers\Abstract\AbstractImage; +use Intervention\Image\Drivers\Imagick\Modifiers\ColorspaceModifier; use Intervention\Image\Drivers\Imagick\Traits\CanHandleColors; use Intervention\Image\Interfaces\ColorInterface; +use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Iterator; @@ -19,7 +23,14 @@ class Image extends AbstractImage implements ImageInterface, Iterator public function __construct(protected Imagick $imagick) { - // + $this->colorspace = match ($imagick->getImageColorspace()) { + Imagick::COLORSPACE_RGB, Imagick::COLORSPACE_SRGB => new RgbColorspace(), + Imagick::COLORSPACE_CMYK => new CmykColorspace(), + default => function () use ($imagick) { + $imagick->transformImageColorspace(Imagick::COLORSPACE_SRGB); + return new RgbColorspace(); + } + }; } public function getImagick(): Imagick @@ -128,10 +139,29 @@ public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return $this->colorFromPixel( - $frame->getCore()->getImagePixelColor($x, $y) + $frame->getCore()->getImagePixelColor($x, $y), + $this->colorspace ); } return null; } + + public function getColorspace(): ColorspaceInterface + { + return match ($this->imagick->getImageColorspace()) { + Imagick::COLORSPACE_CMYK => new CmykColorspace(), + default => new RgbColorspace(), + }; + } + + /** + * {@inheritdoc} + * + * @see ImageInterface::setColorspace() + */ + public function setColorspace(string|ColorspaceInterface $colorspace): ImageInterface + { + return $this->modify(new ColorspaceModifier($colorspace)); + } } diff --git a/src/Drivers/Imagick/Modifiers/ColorspaceModifier.php b/src/Drivers/Imagick/Modifiers/ColorspaceModifier.php new file mode 100644 index 000000000..f9ef2f0d4 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/ColorspaceModifier.php @@ -0,0 +1,66 @@ + Imagick::COLORSPACE_SRGB, + CmykColorspace::class => Imagick::COLORSPACE_CMYK, + ]; + + public function __construct(protected string|ColorspaceInterface $target) + { + // + } + + public function apply(ImageInterface $image): ImageInterface + { + $colorspace = $this->targetColorspace(); + + $imagick = $this->failIfNotClass($image, Image::class)->getImagick(); + $imagick->transformImageColorspace( + $this->getImagickColorspace($colorspace) + ); + + return $image; + } + + private function getImagickColorspace(ColorspaceInterface $colorspace): int + { + if (!array_key_exists(get_class($colorspace), self::$mapping)) { + throw new NotSupportedException('Given colorspace is not supported.'); + } + + return self::$mapping[get_class($colorspace)]; + } + + private function targetColorspace(): ColorspaceInterface + { + if (is_object($this->target)) { + return $this->target; + } + + if (in_array($this->target, ['rgb', 'RGB', RgbColorspace::class])) { + return new RgbColorspace(); + } + + if (in_array($this->target, ['cmyk', 'CMYK', CmykColorspace::class])) { + return new CmykColorspace(); + } + + throw new NotSupportedException('Given colorspace is not supported.'); + } +} diff --git a/src/Drivers/Imagick/Traits/CanHandleColors.php b/src/Drivers/Imagick/Traits/CanHandleColors.php index adb50c41f..af6770b71 100644 --- a/src/Drivers/Imagick/Traits/CanHandleColors.php +++ b/src/Drivers/Imagick/Traits/CanHandleColors.php @@ -2,23 +2,37 @@ namespace Intervention\Image\Drivers\Imagick\Traits; +use Imagick; use ImagickPixel; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Interfaces\ColorInterface; -use Intervention\Image\Traits\CanHandleInput; +use Intervention\Image\Interfaces\ColorspaceInterface; trait CanHandleColors { - use CanHandleInput; - /** * Transforms ImagickPixel to own color object * - * @param ImagickPixel $pixel + * @param ImagickPixel $pixel + * @param ColorspaceInterface $colorspace * @return ColorInterface */ - public function colorFromPixel(ImagickPixel $pixel): ColorInterface + public function colorFromPixel(ImagickPixel $pixel, ColorspaceInterface $colorspace): ColorInterface { - return $this->handleInput($pixel->getColorAsString()); + return match (get_class($colorspace)) { + CmykColorspace::class => $colorspace->colorFromNormalized([ + $pixel->getColorValue(Imagick::COLOR_CYAN), + $pixel->getColorValue(Imagick::COLOR_MAGENTA), + $pixel->getColorValue(Imagick::COLOR_YELLOW), + $pixel->getColorValue(Imagick::COLOR_BLACK), + ]), + default => $colorspace->colorFromNormalized([ + $pixel->getColorValue(Imagick::COLOR_RED), + $pixel->getColorValue(Imagick::COLOR_GREEN), + $pixel->getColorValue(Imagick::COLOR_BLUE), + $pixel->getColorValue(Imagick::COLOR_ALPHA), + ]), + }; } /** diff --git a/src/Interfaces/ColorChannelInterface.php b/src/Interfaces/ColorChannelInterface.php index 7c061ee16..a5bc074fa 100644 --- a/src/Interfaces/ColorChannelInterface.php +++ b/src/Interfaces/ColorChannelInterface.php @@ -4,6 +4,14 @@ interface ColorChannelInterface { + /** + * Create new instance by either value or normalized value + * + * @param int|null $value + * @param float|null $normalized + */ + public function __construct(int $value = null, float $normalized = null); + /** * Return color channels integer value * diff --git a/src/Interfaces/ColorspaceInterface.php b/src/Interfaces/ColorspaceInterface.php index a2bb37db3..7b03204af 100644 --- a/src/Interfaces/ColorspaceInterface.php +++ b/src/Interfaces/ColorspaceInterface.php @@ -11,4 +11,12 @@ interface ColorspaceInterface * @return ColorInterface */ public function convertColor(ColorInterface $color): ColorInterface; + + /** + * Create new color in colorspace from given normalized channel values + * + * @param array $normalized + * @return ColorInterface + */ + public function colorFromNormalized(array $normalized): ColorInterface; } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index cf3083ef5..12646f9e6 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -140,6 +140,21 @@ public function toPng(): EncodedImage; public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; public function pickColors(int $x, int $y): CollectionInterface; + /** + * Get the colorspace of the image + * + * @return ColorspaceInterface + */ + public function getColorspace(): ColorspaceInterface; + + /** + * Transform image to given colorspace + * + * @param string|ColorspaceInterface $target + * @return ImageInterface + */ + public function setColorspace(string|ColorspaceInterface $target): ImageInterface; + /** * Draw text on image * diff --git a/tests/Colors/Cmyk/ChannelTest.php b/tests/Colors/Cmyk/ChannelTest.php index 44928abf6..64f03f336 100644 --- a/tests/Colors/Cmyk/ChannelTest.php +++ b/tests/Colors/Cmyk/ChannelTest.php @@ -19,6 +19,18 @@ public function testConstructor(): void { $channel = new Channel(0); $this->assertInstanceOf(Channel::class, $channel); + + $channel = new Channel(value: 0); + $this->assertInstanceOf(Channel::class, $channel); + + $channel = new Channel(normalized: 0); + $this->assertInstanceOf(Channel::class, $channel); + + $this->expectException(ColorException::class); + $channel = new Channel(); + + $this->expectException(ColorException::class); + $channel = new Channel(normalized: 2); } public function testValue(): void diff --git a/tests/Colors/Rgb/ChannelTest.php b/tests/Colors/Rgb/ChannelTest.php index 4cc2fadbb..5f2c45d67 100644 --- a/tests/Colors/Rgb/ChannelTest.php +++ b/tests/Colors/Rgb/ChannelTest.php @@ -18,6 +18,18 @@ public function testConstructor(): void { $channel = new Channel(0); $this->assertInstanceOf(Channel::class, $channel); + + $channel = new Channel(value: 0); + $this->assertInstanceOf(Channel::class, $channel); + + $channel = new Channel(normalized: 0); + $this->assertInstanceOf(Channel::class, $channel); + + $this->expectException(ColorException::class); + $channel = new Channel(); + + $this->expectException(ColorException::class); + $channel = new Channel(normalized: 2); } public function testValue(): void diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index cd4b7031a..8169e7672 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -8,6 +8,9 @@ use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Tests\TestCase; use Intervention\Image\Colors\Rgb\Color; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; +use Intervention\Image\Exceptions\NotSupportedException; /** * @requires extension gd @@ -119,4 +122,33 @@ public function testPickColors(): void $this->assertEquals([0, 255, 0, 255], $colors->get(1)->toArray()); $this->assertEquals([0, 0, 255, 255], $colors->get(2)->toArray()); } + + public function testGetColorspace(): void + { + $this->assertInstanceOf(RgbColorspace::class, $this->image->getColorspace()); + } + + public function testSetColorspace(): void + { + $result = $this->image->setColorspace('rgb'); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(RgbColorspace::class); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(new RgbColorspace()); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $this->expectException(NotSupportedException::class); + $this->image->setColorspace('cmyk'); + + $this->expectException(NotSupportedException::class); + $this->image->setColorspace(CmykColorspace::class); + + $this->expectException(NotSupportedException::class); + $this->image->setColorspace(new CmykColorspace()); + } } diff --git a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php index b3d20b4a5..39b00bc6a 100644 --- a/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php +++ b/tests/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Imagick\Decoders; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Drivers\Imagick\Decoders\BinaryImageDecoder; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Tests\TestCase; @@ -13,6 +15,7 @@ public function testDecodePng(): void $decoder = new BinaryImageDecoder(); $image = $decoder->decode(file_get_contents($this->getTestImagePath('tile.png'))); $this->assertInstanceOf(Image::class, $image); + $this->assertInstanceOf(RgbColorspace::class, $image->getColorspace()); $this->assertEquals(16, $image->getWidth()); $this->assertEquals(16, $image->getHeight()); $this->assertCount(1, $image); @@ -48,4 +51,12 @@ public function testDecodeJpegWithExif(): void $this->assertCount(1, $image); $this->assertEquals('Oliver Vogel', $image->getExif('IFD0.Artist')); } + + public function testDecodeCmykImage(): void + { + $decoder = new BinaryImageDecoder(); + $image = $decoder->decode(file_get_contents($this->getTestImagePath('cmyk.jpg'))); + $this->assertInstanceOf(Image::class, $image); + $this->assertInstanceOf(CmykColorspace::class, $image->getColorspace()); + } } diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index d76e8d198..a353c5267 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -4,6 +4,8 @@ use Imagick; use ImagickPixel; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; +use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Drivers\Imagick\Image; use Intervention\Image\Geometry\Rectangle; @@ -87,4 +89,36 @@ public function testGetSize(): void { $this->assertInstanceOf(Rectangle::class, $this->image->getSize()); } + + public function testGetColorspace(): void + { + $this->assertInstanceOf(RgbColorspace::class, $this->image->getColorspace()); + } + + public function testSetColorspace(): void + { + $result = $this->image->setColorspace('rgb'); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(RgbColorspace::class); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(new RgbColorspace()); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(RgbColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace('cmyk'); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(CmykColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(CmykColorspace::class); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(CmykColorspace::class, $result->getColorspace()); + + $result = $this->image->setColorspace(new CmykColorspace()); + $this->assertInstanceOf(Image::class, $result); + $this->assertInstanceOf(CmykColorspace::class, $result->getColorspace()); + } } diff --git a/tests/images/cmyk.jpg b/tests/images/cmyk.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1459b2c02c31c1e6d2b866680adb11e3aa9197a3 GIT binary patch literal 2750493 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%0D1+93F)5i2d92$O=hn8%4e{%1`mNG6Y) zMmYJ}{Og8#s@d_d-b~*O{))dC>%pvC%Gv}K^sYlIntt|_Vv_4>yFOulR4O}%v451Z zJJ#UT1=H*s@YR{y+s%ZD$tP@d!l_t`6-`_dvDD&5Dh^(2`bN(1KW;ossq?sMuu%Kw zU(qAz3mp&Xl#HL?uei(v_r?{O?6pw8o;|oHP3yax@Z!4ZT@wgPDib?)5%!g=v2P)= z^3&Sakfvt#*leVc*aWOS_DqN2=?a5LvqhJ(9IkyMrcLBktbsmEXXt90i4zjUn( zhT}nP4D%Q0oLb2OEhhkK8Ar~kcxAsy-czEqPXwDxR@h!p z-e;<;r>UsqHP&U+pRp|SKAJK-$@G-II@oL!(?|Vx8!8w`j|=)7CVKuw?I>%S;{i=M z8~lz#W%$2KMSh8!(^k?QMjdOs*ZGRJytc4IP7_q&%N7@4}-@(M(FP#_)`J&wsoAI*addMz^26YW@XnGy9O^ezk%V3A&AL zaxQ?Bc<#5>+g%1GuaVGM%G_S_zUOKV5UqveaXsqHD1&a-CJXils`uV&zkK@{dytVVsXzuevKuQrW+W}m^@<7FrERXNi@~sp6 zMxa64kuFeH|F%yN;L0;?e+!63U#-Ulnp}(Joj{TP()?P0O8jQ}F3`uEGPw!%ttvA* z3$)AE8)gbvey4Tc1QL&9+Cc$k{%Oqx!DGk0%C~%}!*Adp{wgqEp74iSbe)qWkqtif zy^^WbmUfZ&SlJC*zPP8T(3&dVlyl$GDlSeBHDkm+iBTqrxFP1aQ6#QeRccU)mn>hW z9~52j8`brSx;&0*8%6Q+M>QCc2BcgNYyv6k1^>_eK0!rGXa`3Awf=~GxxBA>rR}Rc zxQq%qrY9E_T8_v@a~_!Q$qLg$O)q4AiP5GR(l^oHjK0$0l>&pi^wRRR`UTQ)kP;-N zc^uU|lAHx8GbJZL3SRURq(FsFK}xL<)qKVNx9VtpYx@S(lB$a~hH_om9BYP>S~zSe zRJ_ZHG9wkRG^PomXilIT6^f?pO^du@{;0ZM zKF4vd;-xelq^uBM0<-dmsJ5A9XX}>LPi{}wa;xw*N39EF$Z|_#E}UV0tA3WPG<{NU zNaGtns*@5p#s#Xo(NTsas;;o@dKXn|&?Q}na@_ZoHeR`E@m=+6MWORO)hXZ|ND<54 zIsBGCla_##9j#%#lfcn3p;K^8^T z=j&gGKGRj`O9Fpt@w#f?c^ZUvwfj$Xv&O~wf%1-end3g-w&F5Kp~}0!tb8x2YVqIT z(4enZuiMpls%mU)QPTs+gEdoI<`xzXTxpHS?(4U;Wu$KF6GEBs{N57Swx~NjOAx7H z@m+6F(*t3h2hh8`#rEykX^Tj<6FB^wV#_r`;N-XFE)uv#GKeVez?2E03!1mDpWS?@ z-goV}mi<+PHC3%AAgV#9wvob!{(k7LY*n8Ob~d%Amk9qip3swxoE-%kLQp+nt2%#S z5(67M>akVcyml#WlUup96#rsQq4^E*=#(d>V-)awM_*4{3Z{%3L)zT9F1l@?ZhXxN z=-Dct!KE-3B(47~9A1F#+lN4BQ+wq|Y^u7a4MmD?>P|qjBR_RzV;+GH>kl}ez{>WY zxSu}Nwq!zuTZtu@NS+&QE+s3cJT^_F{s&sR!s#_&%D6B!%}drsz$ zSpk{a_YhT70O>u74$W@tkzp35GP@fv-{VWV60koaUv$pKJB4Q3+wm^~i)=dxCww5* z7E-cXzFA6cn;T&opvF&mWL!=MEymi#3~*0L<uJ!U_u|L?CW2RX)Qg` z`v#j>5ZiMKH#5uJ-GLiVMRqmf`{UC(0|?B>+xD}>z|h2YHt|bfmh}VayHA0ogEHSO z$Ml$5J$IGy0qyaWhXx-89ULncCb*xh%wdz8WP<^?=-QwC+i^E5EBX@g{F1x9GYQEB zzTKmQH(7$Nb;O>O_D(h_Ha@bWi1a4%ulCbqRA{8_7$q(++3H6P^vSmP(E{8uOojBq zxnagMhH2_`y)SbUI9AatHJFtGPEM2WKq(QZUDJ1+`df#d)reb5y1?pJI*6t&;=KO*#32h{+zT+Ex zN(#FJL+^+yYyXci6|@a*V=zPgtwP3Y&;z)KnGAXW@37{(B^q5>8$rRXBfER*RV{-3 z7dTeK9Pl<2kigy1Sk^yW{^J%?qBcxJP*{X>^nr?Tsom7348(LS*z0Vmxp&hVVGeQuCm#d$aNs^%c)DoB~f zO#rj<2rs?iRiB6xRzvRf;AECx?>@r`Eq>B0{}GRa6pDB|NMQ*zAf;UZzP-wG1gGnBdZfynsmMQBRPjzY5WHQrvsbFl zrq{Ont9B;V+Loz=F%PY?RjuK1=8LMt6$sNqrE;m!ct|mVsfDbv(&>65-K*k=5svn{n6j%YV}QT0#_um}RSd=Ab@)GSf$n|ek0L@@)Tgi9%4R=yDb zsAmtmH)K>D-*lr94|%cC(9~Zj-oR`=og-hL&^j;8zRsgPrGoD~$Uoe}nUXCY@0~ zeP~5xKmaR0^pzm5 zE)VPtL{t3dcK2XcdVK0s;tVeP?K21yW_DP&5)&tWvlLOLfmzu_n+wkWD;TW0-J6=) zR#na1@HaFWlDPg1%({7mp!%%M4Xe{8bc16^wG?viomR7)5F2}-;ar@V{IhE<0>z$ ze~yTPOkVdJakxOSwgmYoTf4>&HIOm)Y+L;G{m2a^N+1vpkDCio_1SxvPWh3p%WykWGG{?F%V#&r`O0 z)HNIc7f?L)LN&tu@5)HatP zOC*EfBrtU|_f7iO;LieY^r;WBqrf?Hj5AidYF!*HV~iKQ%;{3wV{hg4+?wI*+Ksb`uK(!5WiRL7{*7>L)N3rt1cTX5iGV7 z-}s9?ebT@B)$DkXGJ^x|Dkze<$eQuB9dv0$!Wvilvy#Yx`wUh-sb9-DoH^Q8#q>;R z?+s$s#Yc3HG5e$Xx+0kO!|rv)vx0&?+5K6~e$Uz=>~PQh)++Xx%R0*xj>U;*G;zu% zztovHS>O{Pa4v(h*-`Gt8q(UC>}lnH5B9M0ia7&P_U^o${jThfnfrThaU4@LJ^ML} z z^QT9i=={R33H{x{=1&ZAwtMo<_}#Z%a}Y;gony!4BQg#EIQJ^S@0V+A~!sj;rpvExaFj ztaG>Ubm-3xf-os)uDwh+)AzUSiZHif%v5a)PYw44(+y7ZfVh#r8H{lXI1aGH=;$TQu^gcWV)z-3>phJ&VZ6X@5kp)oF2An=wN0#DWt!k!}5vzO` zP>zy=<_6K;QPyqmZs}?X+BDwg&`KQ)hvv2I>9oUk!KRpA!GFTz)$@^11YP_OYA13V z`xyEc>IZo(HVCsA9gl-x3tCp;={R&%CV`Ld&)-b!C2UR_C25JzSJji@65~#eY;MFBQ6^UcI6UQU-dntms!lpcP|z4FrNlydweK9#6h`@+M2agjZL4Sf zeqzQ3L1O{&eE-Czo1{v+w55sk-tfNlDOsqDhN3C6MPab#lo3_}qL!LSNrWS3JVdS+Kl#td-T9_!ZZ~b_v^tr?81WVT3f!l-UrHoTJ#Xzh<0$YJE~2kA0>0 zR)Z(|Z9Ap$D92U*p}Bz*smN~mms27vYEyEUOcrc9r<16KlR0C^BE)0P!=^S=IM=1Z ziq>%>^Aa%!xFLzFvAeh>VcT&#xKy7o`~dgBYzXlV_so_#)l6afx^=aYLZ@Cx-4nq> z+vkRM!I+-clq}c`Tx-57kP9BQ3I$}Q8+5t=Y@3Dg1*M4p;GYHIO|y`t0^f=>)NaAF zTod{U|3qRO<|}_FYzJ;SpXIX>@5on!lx)7wW?WT}?AAKh>JKtR&tJ8F$Q*3Kdb)H- zKc_KJnh#)_#w33W1TB1tjUCZ+a~WuWWb{36B}#b>t)Px1JYG`H|Jker8G1_jAlyu zLmy)d;@jS3*gfKe*=6`~QSz{){HNjF+PKP5!}K19>JELhbx&=DE?1XVAFNf%H#I!g zSoo)!HmJ`qrnF?MJ`?;~$5pQIH&C3irEvo6xx!i=gQx*c=ln$OmETQJpf1a2gg!@q zkU4l)U{^{fffS^8{cupZv#oB;ql$~x+18Q-yR^w#A=Eg(~tx7oy|kKfS9k zyJfp(A#q0Ol%XSKtzFyK$SS-#7j{=vO|+l0&ab}Fj?fO(4%imRr`6Lf!~EdJ6=pac z(e%+2j+eEV4G-Y?ZHx884Rz49y0$VMJW{(lrxmeLeJ6e#d0s^gU5vV{c;>~#tORmF z%5qu45Cn3r|DQEM%x7+#hD`%`~B`vAgK7ZyJs*{10W^+MHxOGFm>PsOEKDD1dg zJ2VjzzwXlD!}1Ah)^(kzxIA#iqOLmDcSq}AqwJk7W7pBUi+Ozw;hkNyYmFc6|KZ*@ zi`z3`J6k=i*7`$jyUm{=uVG;(&+Jk72?H~JF>;1(e@GE>uO`QfgNCWfKuQjv+jORM z|4{ni#xnWF*5emcQ5D1d0bN1q+9n=SAp)t1<8#Z;6rj8f~ z>5%W6OS`JdpA40Tj|6oV@~e-?|{K4YAVnD5MG*tAD(|A;Rj@*DXQj zG+gaDkAgQP+i+-k^FiG!On2)FAPRf9ty2(-y8v@$!134N0s@n81#uG*O+1B4XpAQf zqmAVaVCFdGe58!va0y~+1O8O#9GWw+(u+t_kUeI>8DA;?4sV08TPClqfNyE7?3sx; z(*{`oK+S@_)U8Gb!X4x)OdFzxKY*npY4kfdGwLw@2i}Grho2(!Vkb9TCmL}P<&#Mo zJT@nq3=p*OyC^8)8L)pcnC$9>r~W}%>%?Rv(u#&2ww;4LSYw3#hTrZkf~Ob5@X) zq-*h8$td#o(5aLt>M}1ZrZ zQ0OFb96i|!P5#9IJ&80dle?*<*+l3X^lSM<4D5W@R!qET9)WHqHE0Upj-*f01Bgbl zmNyf*g|du>M?IyS!)?PXp_ahx*h1?4`af|kG)>t|ynwzW8wa-Itc`m}EMU}ztRVR? zgF(tH=6_BMDv`Z<qA+t%^3PJ^?*S50oatJ?H?p2SKI3 zqRm9^VbtR)P#+mvVHucJ%sKTGYzb2gIf@fAZ)KzLYS!|&2ZUBuf5-}A5_{%4h;nYtOcnVI zSG$2&j~6WIuWI=8KZ?#Xs)?-)qkxLo3knJ-f&~;oK`ElBpojv3Ql&$B&txW3GA+F) zgx*C&vG?A^dhNYjyI#Haa;@0?^8Lxm%AY4$D{G!PbI!Xr-PJg!1fyS8`ILU6uUIZH zV0xd{Nd=|X6BpR2bgMj-(?+imwR7Lm7FH8~8m%q3BtU3o#u(uqnmKQu=nkC_J3@S( z9x*XjvY+}h(ky*ReRigJQf;g56a?v-mR%^^uZwIrR=i02tg^l2fwtUIP*$c5)jVe0 z(>x`3tVNpT&`b6P4No+e>#p%;#qpM?uNENu73#Xh=LJvHQ|28J4O88Z4G@n|nJ4B+ z{8VuxEz)A;`#u)HpiEy?kndq}UzS_YViq-I6wNd}tBfqZX390cFU>J-)Zk@Lj8Q~S zX0c%tWMdsOOc6fggy^?0A8^(BNd-QS%LT)9qvj12W@;;@6^j;WeoZVCOEjVp zYbE>CSNm9`>s7ZqKIHXvFk3(7uPU#pe_t43-(PXP=%Ky8xuQgDo8UAfyIDbeJwt6- z14S^On4bylY^FJispA|lsq+u>0*t2@`|yhlKNER^c0=?uws4)kcw(vOqK+D|UJ{{g z?PHbB(9GzlSu(1oytOIsK((#DrJ%G*Q=uv9Q(0x6QQTA^R39&mb_ntOvP0z*_>dWC zw+g4THrO1@Nu2)HP5FE-ZP~Mk#amL;f(%Te~^^Ja2+6Ywk(@5^JxRwSpDqV<8UVGE?k`&Ef@y(|v3biSFjg z`#C#XFD>m_!dyC~zE}R}=4wY*L1&YPX={VQyjP=Dg7SzV)?zfmw3PJ#W9-&$<|jP)k076{SjM4mrZfKmx>z=!&bKB zgsiYF-Jg4V`L4PPdCQmGavUf~XdP;j7k+M8ta@K;Z`!9Yln!qk3-&BqR=-71#Oz(y zlQEwK*BJ5$_PeUr=}Fwhm4$Oh^C(A4^c#Ms{riN&f^uu6-$hY^xw?0&c(gHmr6_yP z>i0{nxx9{FbxZRCR{V09^3S&qH%%|(w)v>G6_0AoS0tBgXlat2EQ@M-Cm6<9-Z-f2 zH>+p8Brl9@tZhhN!ns-fU~U+1LgkIhJTe2*)q0l zJ9Al6`jUt2z=oM=og7x3e2$8{qh@&YWd5)!&k3gl-5jxg*MvK4j^4{e&KF~BBwh;m5tzisgekH?f@bCn*+=2Id?9#6 z+;h=9Xq#kWVv)RB%8RLWl4^H_XyH%bbiWhGF6d0J^@^GB{f;1RJG-HE0b*zbtbPBXgKI&?2~X2l*BCVAO%n%yC7uD!vz2}~+K#p~{LVpQ@Y zWkOYmpa|@XeGyUOlcSRqjJ9M{Y94k>iuXYBTl&>m};%tG38$G!1VMMu^?+-u0 zjDnd+mMlh?i*)k$i~dixzbICr_Pjue68)NvNb9lmIadHBwk~>~%*j8TupA6jyzuLU z^6~w>*1--!*DfpDg#F!I!}y};U9*K1shDEl%vLC51{3FyqCpwQ{iHaD`Sbnpe!wk3 z96pcF6c*v!GC~C5TbEoHYw*Wu7bQ)2*PJWTb|Nr(FCZtd3Cm?AM5pgPu$~O>wN8GJ z1ef10<2bkE z^{^#D!rQ7lY2l)GDr%0uq>m~p>Z8;}`D6T9=^y1~-}_FZH0-e#bf^DmyHniLRNFMT zL}40T9as9u*lL^4h&K+SDYCA8tFDJs88S}@wV&!DrNC+>v*}R zg+sJ&7gme>wZ(JBiL*5iqrOU#GzH@?NHaA(d>;Up)j1w}ffFjU4J;aJzu35`n6~>? z-7WcGlUSdXWm?;Ge=!bQK2a^K5tcZ_!Ok-qq&b`p(_8LK?mJU#sXO1(*pe#}lp5YG zY!d>8NeQs1LT`$S5F2%E<1b5g={$WO0m<3`k9~l*`f{6B;iM|(zE08d%GXuql984D zt+l24iXJ++?1N(9J4Rt{uPZNP(jMePiFk_jOyUW)}msMr&Hh zKQ%GBiDlEPep4=t6O~=?2iCxfCz9RlIL9{b0?zvKBPFHWyY@{v$N1B29Sc1L1y*tV zIU&!&ij;{+vsdtMF=<@q>mp4z#5+@Bbev^y{-zrwJX<^<5zj1#FNXesLz3TCmeaH2>*utN&F4LC)=7x zRQTTNJ>FI9WfuCnNsb!L9{VL1^i9iV=FeJAH&ho~S$3@Qd{O4oKbBj?w_2jLTT16N zts?bh+ZuylfZ4bHlq8guQa6*cpS`B$YVm2#ooZpuIHv>DHRU!xr=m1|i~uXY6iEv= z+s6ini0Z82zTG9MrU%ZHbi>MJL-KBP9BbefuvfgQTvqr`dpFDK;^a0@t)k>sE0bJU zmejHhj$kZr4i>*=K5Z0n5O#F^&0-}-RyQa67WZk*rj(bwA60+HhY7}4cAvUSnB@ow zo+OI1{pag0akmtB91!0#cC`MNhpzp*AwK`d>W`IBp?p<$3sQ7%rIQ>~B5of+4k^9Y zmMH(snAWNnuV?C8syX4TGfj_*qd2~e6SJ*cqP{34m1nKZnT_)|RF_OWC>&5ZJ~%@7 zxxCY7w7A_i%$XuHZEw3GIZ_ziYX ztjkhBK8_Q`b-IPQijsG5Iq!Au6l9`c)t_*xri5VbF7|{(Yze(C#e^oLtd2r7L>t9feXTXiA}c3d6zsE#KlP^ zGodMx**Q(}p3?NxXHG9zM*JqYQ6`KmMuNfh!NZV~@<1Oi^Z~rj{i>7YeyH^X{~mu+ z!w|s}L38C|;RRuqMIbsMnyt+e9~KWH6D1qP_u(X|RkBLr51>*8=a?)V2q}IBhR9rU z(!eg++tf+WYw)kxFXXk5B611rE58st1ilLY@)?3oK(pPiDvo1!T8HuklEQjEACP>g z*dP!}?dG#W5inJAPy_?lh@)bj43l4$q=Ek8ozfI=HHQU+K=X?$Wdop}Ic~Clix=gE&Lti}dC6k-8wd;#ty{$k*&Wz-d&N0?Lk|w`bo2 z9oW37Iw%Ue5?FW7xL-kWqI=6Nb_e>RE{3xY^K%%uCo!&R3-2jrRd3;c z$Ijw6gf5D~&`*)OB2Dx`?4lsq+r%FfCyQQ7?%^KUBI!9iIYkPb#F5z}Wh?QmQ#GJ~ z=ob_TO(&9l2Fq8Iz1^=Mda_4LDvL}0Tf3F5A^&x_aXQEkrb*n(&1ab5nxVB*SjFnA#?+i(W*d%|`?Ac2Tw@CRi2k-}5$A&*P}K0e^u56o{7~Ii;a`H8 zx;R#)FiZQe@QNr?Yso4Vv$Vd+S0qK6wX+1$Ihv#?`+;fddqLA=bJXPUN#Ir0Kkiq- z4%OJEuCfSQdJTucv#zxtVm4Sl8Q!q&STdCVafX;TDPp*hravH$S7_1+`Fy$Y1}j{k zH%>2vg!P8qS#F}O1}8yX+@=@Il1VCb*QRtzD|G%r(}6avWB6onfu_U#s_Z|tqG?5G zTII|dud><-o?XJY?w|}Utp4Sd%H{0E_TAVsj@tGV^y03vg$ccQN33(1xB0g$+CpFF zZ1JJYb)r6|*9*!;-%M^Z_lxfsXGDCFJT|0_V@f~i;o%XobGmqEN|NSyQ+A1^ZgTax z(igS8?Lmym)wc}U%&e+rWdUn{WfQiX{idQ`cAo3zSSmQfiz;tsTKS3g`-tvY&qaibPg>H(@ul5OkA|fKmyHA5uStLCM>O6o29_zR3rmhKJ!9Kb zHoV18|Adj(gwZdU>l^-H<5>UIZ;&N(y4Mv5;<*tu9~fVG>D7$|5q!9+AY-?nuHwmp zp28-FaOO~v-TomWTCA~UjjfbOES6!50ltanOhFCajXR6`tXNh(rKDi_Wt+8hb(@!d zOWB9k6nYyosbvHDl0`R$-9;yLBnV{4qQA8c4A;A(#9eqGwlIHj?^XyfYk>Vd^yJ5Jabr88Fi&@0O< z?OwE!@uaN`-Ny24T?<@fN431*AK-|aV;BOiwsA}TQr?dGpBcmW`|2ho9Tps~DTreW zZ&%Ubpy*PCZEU^xOL@w$Y-y|2;C@}Q-1MvIH@LDev<8P}6?NEeLw8FC8vDu>rL?Lq z99Z^4kpk~xrh*b=Hfy6WA9>0C#p;f#xH*M^m>X|HrVcwQ@L$lb$PhASdMLU?_aY|a zYKeNR9bX4zcnb(C$nXAwj*|aw%90H)ZK=K~+fnwneI{7I9B9Cxeymib4%*MI$Ii<0 zIO}Cya3Agy!CQDO?=^EJ!WK*^xP;sg&d!{LZWqZ;bnaJ(M{4#)?uPbF5L( z3`Tl$@oVxS-JesLNLk}rsh;(*Ivkk8{%TtaeCGVp-;=H5dC^}%7T+67hP(y+WoqcC zu(v=jCxmaAN$_ORSEp#{qWDe552RM|XZ|I$0O%F>A9`9gEMgQE2Fk{2u{+RjZ#F&x zKHL2{xeaY==#mWM&#qESt_X%&-%INR&-GD2sj!n~%KSu)=n2_Xu~gOvv`L}_eo&^= zjqw8dF1=I`CO_pQ+wX)WvdZ~Kktk3f_Y_$PB}e$6o^p7s65R^l@n$Oep|#zg6IqI0 z4Jjgrct_<;alRzonj)Df`KGIrc9%9%*QD2h>F6Aw9C!uP%a+Kr{2kyJa6H2X-UhGb z--XsewhXD9B@dXt3!W(d5qBS61=oeoMfxK}V@c!%s_`zvmSF#Ne}?Z;bk(mCjs@c? zqlI6Wc!{RSo zib}Mw$bhaVFNuq>Zb-Id8Wt}-DD}kDytmSaSZCP{V3VS6evAxKFc;qgXDBu%eT6&~ zFXDzkckrC>nQ|81I;I7ljsN3aifkb^b$^E8#N9d_Z#nT-g)iU9b2T>#RK!)y2O;QW z)#r)wi0|;f;^|~RX{KZt=^Qwd{6~hAVbT-ioV@O2AeFJY1Pqr@bd zrLE>36HnC6EZr&@p!t+HO7c##e3A1XqDe?v1guwop0PxRs@o^a!0GDZF|E)H)h91P zepN+ueWGdns%v!Z`w$Jo|n8eqZ^5h2vX5&*ys8DK5;c`U< zhHa%8;#5QGl1@pa{?H?3`wGGa_n2lZ_9&8Tcus*3!~2g&rJ)xRN!TUy8Clkg|4AyD&y)>k^K(y zNAq=K0qajwuxcG=Lc=*ch?`i?f!6Ti>c)wC@k47~aiRp%s@qEj3InQ;Tv!xb8Jj*( z+|6+!Q6hd`Zjb#U*=rA&v|DPmo*8`+pv>QgHiOxw-rb)A*Yy9?^kT@DWA=^A%4Nrl z!K|j%9x568M2k`JhV!85E5zqsX{-~Sdj%|1x|#itGmF#1$(IKUs0h zZ>eUoIc<@O-5jX38k)pKmi84HdAw!@dp#d)ykATT3L4lse+#*FTNXYQ1=RY_y&|4m z&5KQt_*PyHdn>u_*b~?#J#SwW8SV2+UVSBO|@ZkXaM`$)-0JexaZ$yB_NCrs~6B=Wx{3W+vheC!wE zjp#_&6Y`j(Ebs#r1eAE5r&fYCw=U%``G*<+9$(5W9}9PstupRL1~YD`eUMgG0KNkC zV<*WYP#Z@iUW$csYdNQ}<-AoT&5Eh~_qi_=ErQ=^pYUu^N@4+iU95^7ON^Hs54%gm z0doW25G-)A=UH;4Jk_m>?m`Nx4@2u%O?F%!&R%I8EZ@Rets>z>?m2uIe1NwZ+JWTo zJH-=_=YnM%21*IrN@io-L~Xe(PVQcF+C4>>WNBiu;)e7@>~MTAa69Z8?hQ^4e2!Dl z>mgf+EAYS0l)mV`YHx56?=RbB@F;(oK?I2e8s&Ytt1w?-lT)HlXe#U}b`kA?w~F_& ze;UXv$UW?WB32#{_(X9Zwhh@tOhgOZ zzEB|cu8I#_5)ZVElgY)q^vA)Wk|O0?aD%ir_8Xcj-3so2?f@C0L}$l*V%NjNW$T`ZeK`$#h=Pf zDMN-^Rlp$G8~qsI7+6GalNEy>Fcfr!cwh{;7P>CH29?OO*x}Ge`QOFi@)dAm&Q>@I z{+#9p-$ZifN)QxzJ?#(j72Otg3{Ax*1>VO-W4DK_SEzA!w=aYdzfd_&G9Lb8c`mt! zMC;0>6$nj_2cnS&XczDgI#zZ>R*!Oo955a|$~q6eM!SlxL$#PNr&K-}+q3Yi{Gnp_ zTrNy1=1u#K1S}an2^_CXG*vkQ21fnc_*W z2>1oxr}I6Jq6KuS(=hdkdgItExS$i8F=2!Dr*@2puU$dj5>M4KknQ5H+97~JvQKkL zFjY!uNM<__tC>=`1Ng4~k)0~ru0E8y0R+`V!VjpIdhE0)=&0&>=vz6gx*qTcj#NRO z$B^?XZ>ReyQ%N`q`A^Kz=COh;rYg-vA#UnU8b#U0rAVnbz&HX37yoN$=iig8Gz?}Y zNg@4)!W>|#J|^o8@JV+db%1P(E_+UQFjspwMh<@0N<#ghv)b{2u5yP);dva1Q#+@U z;cV3f$58%2`!myh{v*4G#vs^hOC@IuHP$-BTg11zNS}y9EerW}@fUMBHk{bvOJ>$dau!$YTgFSAQTh0Gg&x3+x7c)iFFzzSkXnIA7Mvb|xnNoLrl7Oa>2w&rEc zl`vN`JCYRT7FV_2- zN_op`muuYlRW&eiQ~*^c!%oR^Rg5%Gg*=*w{?v15u)s?(M zEn|pO{^q9D@KAxhF;8+yIHUe2k0T1G+f=qg^r2Rizg^r}Gcq$yQeSmCxj-6TiO2Vq zc2x|H_5eD{_f9O432kpiEs}k*h7SG*dTdU1>yoW9-Y<9Obgb|L+sDCAoJQ#bZe73+v)VBQzf~Aw?oap?X*HdMxcD1M5KBw* zk%h#|vdzE%GKKMzznW}d;hPWJXHp8xzm=lPyVsIToGRyYZ59t%Ve6-iieDK#49|B`2s$L+gU%P z_4p^wRDLXx!=2Bt5eIot0Y!G>FUx#MLV{DtpUC4Pm-ziuyqFdBhsu?#57E(cfVuu+ zdJZ^f&>&?$d5G&zRXvh#f8<;TFE$>*LfA=a3Ul7i5=*eJTn-$sh~*VY{S-&|Y~DHC zUl7ljfY%F`6a?eXMe&*Kgh`y8e4n@?Va|R>I)S}WpUEKLcnC!@!Lj~4@&L4TP=9JA zT<-cq*@o`2Pel~G5~COLoFAgPiDn4`@Z;zT!F~B2Y^d<4qzx+<8F+=*S8-<92?ZdT zntxgGT&2l?1B#pYE64KD+CiaU@n$e)WL%b~-i-_W7*3Hc+Pw8}FXN3i~I_JZ%& za`^4+zpyt*SyX?;bhI!;ppalaM$N=~VQqtY5wjE3lNeUUBPp0FMrR$7VlM&t92BF*UC3>n%T^H}f(U5B;LK8SwA zwnTYgr3y5JgRR6zj+(9z;!%S<@HO~jXNrh;Y^jwo6jeIC?1N$(Ed^=CP0R+lE8c;t zp=#V+v{ODDpU-KOufVfQgW(_?&tt&*@T(cVND%&LK{RrSm_O?$8c2+f>V`HE)gesu z4LQ|64{Ik!4f0TYAla@z2pPG}(hrzT&(gU9_vrD|7nz*)!iIyroQAh)U<370lngmP z<%C1KsaqvS<#VW;c`own)YZiY;auwBf=Kui^=#I6B$w)pazn0BH=HRM^mP9`^d;SE zkkgn+_j3J#Pohqn*Gi{p{?#s(-qWmh+FJ2PhYZJ-d){eak0Eg^?1G&9;aG6Yc}jqF(YG<0jfC@?8pICynjAANBM4$2X;`o z+v$Ez0#v1XTkZJ!xJhIUrnWdAyTjDKByNKs^&Z0PjZ#b z94%JZytE@F!>n9tpya0ICOSciS)yfgfMMoVVH(hCddi+8%P{eZcgtQIZ{@xP9ma%3 ztD#uKt)!1or(wvV2lqqO%o^ znmc0Bv6j3naqc1`FD38Fd&*p-dV936Cossig!KeCW8GKGk+CdYxlN#(W%?oyu-Qz` zcZH%%t7o=A%_i5WDtVx>aQqSZcf$(*GFYhB59)&s*J)jUz%R5DOzT9`YI|sEML()9 zlU3qfRh`IsiKKEja7a3&Vuzqpy27!CRR=6Ae_cEn_+tN)n;@&Vk4?uwA6sNn3Ao>y z6ZZ_7Y7tE}LWj+*<4?*}CSasi{=hh3P+z1)|K6G6tuvS);a`mtHHD)3`g>%qcu`$7 zB9?To%>oEXN6jPwDvhu9XC+FXR}CvV0Mu7{=e&?5RCuPB%g&WQn|A=ru|JFZ0RC&c zIOPbGYV90yFLrb7eAAi+kn>ks)7!w6HFP`tFaCR8-5c`Z3w^r>kE5-#p& zcm+(8EU4cu2$TF%CuV+@T54w(ae?VI!8sMcKUJ^Ne#@klE9adDdsjelGr&qm#FSG| zFZ+w&FHof|XQW9!+`=0;7Cvcqas2_UH10Nq3H{oRYutpNmlYH5L@i72!k@*7Ex7cP z_+9fv{u7D4aUXM~bYg?FXpr=7{rH?HKwo<(O)PV(Va*#a+gLp)Zaz4(vVMvic(h_% z@DE5(oiA4l8Lz!3n9xYK)rAabKiI-W0 zV&!SZLw1Dn1#4x$qT-N12W zouxN%hZINCpLj{Rsme)wAiYBwDp)q}qB2i-A#RpZCLR&-TQx#b7d%S!7KritOFb1F z+>fnZBk$v!;Pys1#(N~i>ZyKBa@ii_AhM0)gCvpXxcvbc<;(LG=%^Ha9~Of$3tWrt zQWu0na{kcWMDFPpxN=Fi@}5GfzW5aLo@}8`ll%)(OxcpRM84o1EE+c zyr-&BJQZ!gAK?APi{Vf_UE(U0;7cWI`1SZpX%^E$Ob6VH{0KX+GbfMuCX=RZC3}EV z=T(y;$Te;VSp%JnctEa@-wf_U%HZpM`^YEAm;R|#7nbkxTUmf7=#OGK(yz+Lm>IC) z@y;F`C~s2qlQl{HRq(<7d>_Seu#xGl_yffh?!t?p^*Jv1ad}Bv6VXF{De)p!sScX3F+f9~Yy8g-3T*XS4-}Gi8O+OsDZe<$yS zzK2sJMOX%G?cH{u8MBr+i0O8 zk~a$7rm!$Vu|5h%!6Qtnc%GerUBE{!yr>w7FG}35sKwXMh)^8D??#+b{3d*Ye&Gqk z5x;Fj57Mpwe6kM-x|UEm+P0qbGH>4l=R?I>;lm~Yca-MQ4 zJA_UoKNX;89p#mE7kx*$FATsksD!!Suuas&8KKx8YC-rH#bj!6&=18j>X_eFJeoSy ze?Cz{4|c^!i26$_hTfVJ)+u&iUk774iqq@Oe2)|NwlocWiRkDJi z$RSm77J&w;#x5L63qBjx(o*xkql-LCL(bg0e{6pa3@>+ZJ`o2-rOpR6!zwz;}vZR&NJ^D=Gu8EOF7 zuiPC)K-!)ty9vIv*+dheEbA_g06J$4Eal6mSv2|H@&Q7CFl{l%y2DS* zm6IjN46}F8WaPf_fbUJz%K-N)#X!B;Ql>7xD6_@jb&_~C^jKlKW@<+*6;bU@;dbkv$uRuX zI(1w=l4?HWdmGtg8s4uA>t*=k@&_4j7_12eW;8t~CBV7H4&;n%aRVS5A=_UcCoBU4 z>bkRS;Od&MB_K4i`b*v*sJiNUMj7Zb zbMo!|z@znEOLjt2YfomlLThRa$!YSSYGwi{@2DCztptv)beXgj<~h)@M%ZL0e10G^ ztX2J(D9e1$<&XTUd9l__yRWb}Rj6eaDbQ4rI|DN{Jk8XS7vwojblEU;fo2jTQMOAH#FU8oYQkA{oH?2>_TJJ^%?!@Hyu+GE zo?FHTO%HxZN)Poj!NLTCrkk*08c%&$bT{m&HdrDUvsJ4C=J|}!GCsz8j+LBK59c4w6RO7uPGqcC z4-lSAex>#kT}lAezT)T8@>TDpgTn5r{{W6L>opU=VLrn&MbNh1&6?xzN@rKLp?|CU zDGzf=qE=bW%SXm5SMz58o0R(mK0;sRQ^6lLU-^&ldD`-q~9tfZ-#703A-6EO9`a~a>ym8uju1Q;$>{hw}xfz3%zB2FRe)L`0rG!-F z0C4xTne-c|JnWP*SFRt6EBC^kJ_A*y=z!i0s+*XT52MTY98=GFpT2Jv@DV`fJ2Kbsa0Uxk{s$T6utN`?FKzw@R@oimnOtIujSKX_|!f4 zWY}SP5Mme$(MGhzr!V~zYwn|09#cGVrg#vg^dI6Sl&si7{Dij1OUWMc5UCqEQcm(6 z)f6xSc2=QEeAD>Bl<&n6M9Kz_rTgX90!T1?6Cbu`? zL1rzgCB~7d3;GiPSv|Xn(35mbGI5$b9=4V^LjF6JO?pxbdGSVlAxTEab=MT9~IojnXHH*GWzD7Ul zP;?U(sG9(avH98;Vt-7pJ;L3O9o1rtaO{J2LSdOARC7OPjzXZZq_0w}(-8C7itn13 zS`>JQO6JWp*2y@{i0^B4>MLcPGJ2QgXow9ielw`#4+ZQ`hEBzX)SYdVV3XrUg)s2G~|+v1G=Hzt;a=c z&?u`1cN&UW(6Z&|R`aKVztC^yyzFJzEYtZk3sz?mBn4rA8LhM8uoK2!QRkgDztYgh zPNQ_pm@36ZeYj6A#e3bHK05rFw%sK~u|@MA$wc~B&PInL=@mJ$vxvi?aL#02FJI01 zh7PhnD$7Sx?coI&+F~QKQ_!c@J8A9MQ0uUySS-adb5;t*H@8Lg!!Db;LcJ6|Cg$k9 ziYd<1BS8v<{%!9%oU8Y7aUnA`sbnq5*H{0Wm0l*&-)Bn;VEqwJZFZ2Jj$y(MgF><91drr?gf`o=Ak>qV;A`An)uwTdpblQUASJ?TUa*FS2s>X z%q01X_Z+Xs&K(&{!q$9kU9{f3>_WL+@Um&nu=aJrDOIv4H( z;GOn9@2Idt`<}mwGgEs_pf6pib&e0`pVU^0W@S0Fw0QJFhjy^|`@HU2Kgp}O_nHsV ze10{dv+%0s=B+Kq6r$6oDI)JdDyj#EV8>ouFC{0`Ei!A~FSP|6m-#>~06SARs6C*(#2c!Ia<{k(s$KGpQ&E*2W`~H>eUXBI zKk8Me-Md&5fF1DItw~mVb2c)Q;4Ap5+aM;-QSAX=NT;jxP`;p_N(WtKolv#N=atM? zRmk_`*;EcVB$HH?!AnyesuaX8vA3!lQXh9vc>wjDDp2l5*M~5jtH!i|Zz`=K#Jj-h zl8*M+rDhP}&aT`<^08gYzY!I*Ub!1NC23G<(7t>~Nu$}UD5V4KC_bv(fxgQdtkhvs zGW#h>%$f=*<=DNspOtJy?2Ok+H-%+tv9iD7XGp%%8_x~+sC3{Ty_YDT5X(JYsAiH} zXNo^ngnp!3@$KMe=j$2q8@dI*!@Ek?5pK*@x|$eOJcX_&rY^D3s|iL%1>Hi}Q?}5} z#NN3N>6OIu8Fy(1=`l6aX^~C{$)rJ28#sWjCog($rf*OYy#&e_C$%X=SxZNw^QdF= zT`-+GLZ26>Q3vTx-gIg|z0)~Au%F&gw1GN6w=Wq;9jBW!0;qFzT}nUdqLUewKs}@z zX3TTCr|YJir9RP)kR8rXRtCa!PkQUHL3AR$tJiUQ4gJ_frW{3oM1GOawJxA5d0lfz z^pkw6Sbx0|RD?Qc z%3dl_b$`MoYLV(jAfM8x+J|{ijVe~JgS3lkhKq>$SNRgTL(VYHko`?gH#`-cBWD?` z+)ZSnA($Z}7wE4Rbt9wn{M*i7l2{T%6c#-o%I` z4w@qiJBd@KJ-Gvjd#1?6{fIZl>&YbPVO*SWgLE;Roh~4L7~&(2ld<}D6SQQ3UKB8d zoTl601(G7|6^~!!KU$~9ih8bQAyrPFGAz>(W6Rfw#6+0=HaCHYw>zggiBub1h!IPy zH*<~!zkMbWmQetrV z{Pa(Rr@d~$Vq%i*azeb*wmd%e2vKUC8{t9FmTlwr5^K$lQE`OUw8;x0ZWtpyc9A0u zL!2qj)eDk<&#yZs3&!(nFNwP0lA5cWGdNX!qfCICt3DMxz?WBz%JISXRwkqi@dFj2 z1w8zb!xGj{areep;aPlO!(@&X_pKjW7Kl%-ODJ&Q$+d~uNAaARm~LpE zOtVcX3KmbWJ}*{sUt60?PB4_#@n!uBA6fopWaYY8b}~(i+RaZ`XBRv$n>jx5U(8du z(%4K>8*h6!ZsrNj2S=H|iq?&6F{ewg!LQBE6H+}MnQlSORgzH&pG00-nI%lwEvtK} zU9`_~vg`;~Z7F6vVE9`Cm|qL+=GE+xIXlf+ocQ#+rcK;}1w&2EJY)Q0Q!xKx>>T3( z!MkvY$tZdr9A?@psT^5vQUPUyUzj|=DLozzQOz^N-`T* z-?_ugW7yxy4w^1=eiufX>bbvi1g07Mp6LgToB4m{|1>rW`@~-~&J{&W6C3V`$AvSE ztED}H#~IfErjeCK9Go@yso@uNtH(pbUigQzE9KY}_>So%M+@9FnYpFHOQuX-3TM5k z8$YBh-`FYWUvS3=3SQ;-7$*pCr!O@e5N(+MkD*e$EPkJ%Skf}BSpP?=49_(b$a)5k zG{k~SMp_L9d7$T6gBNV+@j(9!Epm3HT2Txq7&`<3K)R7DcrHvcMhLfXrW#&~WTkHm z&Ek*(g@Gl0mwnU_BH5Ep>5obI^H1p;fVlWodbVunv^3pm*@^H}-QVD^;Gw#g^01Kx zy&Znxc}kC=89nan$6){Gjkn{pe6_(OzAZguNRuc9rwl&Q(d<3?Tll2S0kWkZbv)v;Z2BW!9FWsx(CN3%!?g zMzbHAHG8ARgxRP0tM6g&Cojvp~%zpveo>rNrK#&*~)S)V545ATiI+>hn}YPv^)oRpso;E;=6UrrAj~il=BW zs)?7Q$)g&XOieJ=Qanfff?Ad5qP|6K&Qz<{Q+reYsHN25BzN^9>g=o^>T%S)7`5tO z>cixA)nVEts8+Rs4)7nWs-&Zb)T$oPRXx9}_R~%hkqT7~meEd^^m5T=^)WTgy`rvG z7cd^E+3J|0Q|cM&;Y+I2{Z(C=0jkHU>kCJ!I#v7TnN`iIrLznwN@b1lRu!rElao|4 zRq@Uz{9{xj{YzDYRIWp}sk$lu>tj=XQU2p}KYdbOm8Gg<^an)>^&q{F+o*b?_hYoG z_UP^uDO6Ru+9i`!d|h(JYE^=+cWSySPZqvmo@ zg|bc~^dF(rY66^|L{h!e!$*mzdCnArs#Z2ewak1>v`~ecXK=ZyEYo2ITQ$j)Q8ZoE z+xR&5rSh>6$|zKJ8vRp_Dwi7$B(^E_hSZsCCEwr?wO5&=*M+Td8l`6i`6wsqYDZQn z2kJ%*2~v7!**#m8Zkp%Ll*#HM;D^e~zC+}%?6Q63PF4PGTgaH8++*ET_)S@B9h%#& zlv%1W`YIP$Mx@+ThMOxA-IT-3J~LtZr)gQ#N&3CJo<`Z30zUGu4D?|Da{pD&TpmIvA68Ca#8u`!tKh?@>#jr%HH-B zi}yH<(!Ens>5I0KL@#=u^=#Zpy45-{%7d=97{d0^D)Ys0skG8`azq_nV01 z>*T$(+%bJFOtZ?BaYN{N_T5pQPM7qj(06o*t@l`jo@#kIqKWQrUOo6W?Ph9szf3(b zj&r6=)7Aq@x~m~t=%BCF&*wPkEp@qN0=lZ!R2WEuHEVNr(2J^%Ebc{zSG`ESLI+e0 zo^zQVTp1HLn)+0c6PZapcgRD#s0ZcK#uC(f+Z(_A)G;eC_%5}@vcQ>AZuWC_A6GI3Upq1BczdS++tcPI6Zj{I3kD z=vFdRRPSgkP2#R}^eq!GjO8T5QZ%F7ow+Udn7xK|JHzdN9GzuYRmRodxx%0CFoKy>Ue|pDfV`BsV)kCH1vS>3b8t1oVJxbe`2=QMdiAG(6rL~ zfUH!pd?_Brhfp;>%1DB_P-%v1@HS|;p%u~D{78QU>8zvcOHiE^`*nxVtC!8y6=T-s zoYlU-o=$Djf^hfaMOsh7?C2b=l=voem*zdWBw&oDlR9}~24JjS3^=){j9)-jD%ijo zcf$^(CvLC75BUOlRc}X~f*jQcqc=8db!#z>x=>vrmS4ftZpPu4d1))~CE0&99|>it zg&GbqJD#G6BB7#}X!zuhpNfGL`j7RS8RTfq)kd-D5iabhB#AtCT zx-ZyTBt*x>&44uM#^LTaM`+FX{@T;pc?3#Dq-GN_spyTSfaI3_O#O>=FEvSRAg_*x ztIH`HqZg@jsV_sc!)jcXPHB6o+*UTRQYXz({@_dnC~lnBKvv%2$%(JEW#r4)IL%#3BLb~yq+D+$ zssB(Ln<~_O)W@}B)N^RGvOlVGv@1npRatam_8#Q}#)y;(rI^8rFHjt1&WWC>xWxP% z+NC(g_6oSBC}m%o6s&l}B>=XpQM@pK63%B48#E|-4`xvPoc;rTPfcgkfnTW;85f8$6BB)cPevivIhZ>U2N#GMv! zN#4pOO$w5~o##Z|wY7xjQd3)F@Bqy$v`kTf>i= zG)wkd5biNW7B3(JS@}~qo8YH%@Ih#zDv6H=dP}|M3tNsV4gB>DyOm-5!_`W~BmO_7 ze-$|X5u}i`7Izdfb9pI-{&ySKh1l&-yOe{DP@KBa495aa_8z;ng zbW59rz@CzXDSUu8C_jsnP>+;OaVTuAas&q~umJ*6bce*scyvShE%2+1)Cd~}o~RSa~}6R#G- zarYE!)Pbl3MY(DNlqP?rLW5ND9_5dEuDncXr~>XKD;rCu$<`@;=Z}%KC|nuc(q9UF z@^$GYMR?3UDN%kWszzEZH_zE5`7ZkwFkf<5);nplBvTgb@l-9TB=vPSV~1rOP9W11A9zT`G}$pAD}PCD}oksKxYs!fX-Az7_C zH%~7ysDFf9l_aSH{9j2tRN)h2#RjF)!&mID2nHx?q&8fpyuvmbsg`}WCPFsJmRpF; zTV*xoy>)ik2=my=Lg{)FxOlu2X*`){k@_10Gkhc$4Wi^ki9>%O#!FJIpFhtkS*)8G za!dSLJKf(~yiI*?V!Sv*9X{%b=#UC{Dn$*leB5N&-?n^YrL5mUgOFtn_O;D;=~vsg zx?JgUTV%yYX^pkHc&>E3WlQb{$zk)0bht!p4o?zD(oK}b*%CKnN8~~AEdw%Sq$Iyot6(&M{iCxZqM_2tLb~X9y)m zkFy%alFyC&t@}d*x-@G#7(ekd?7LeIqUg4_;Aha4wndPMp!HT7G_GC({AX~Lzoi_m zEZ%Rvff&vsn}d;e(*HFrLyt*1XN<;7Su8jFz@ChpVK|642IuQv5$E}`^l@Z2Z;?KN zY8d%j`-8p~$VwwC47b^4fd-;{tXE+X&;siecoe9{0zxF#FE(F7W>j7@N1^hH`%PqY zMqa(~A*L>Uhj9^>nsmTWj>9kJ82kxdkuUXEh}(iQbnnSi{2Fv*$~SL;u9}_zIJrhK zJ^@*&V;ADetvG}Vxy3SwBtTx9KcecI|Cr;@33c~OW=v3}#`qaKu2^maW54HR7;fR- zr4Jb98;)7ZdbLNd=J)b@#{zgO}(6snh*xwLIEJZ=P1mxCz*@B3U^=R@ymm z>@7gH~fAFifOWfbD)R4y?*$G{YJwRvy*U~yaFYM4dM+F2YpvM8rC{sL^YL87 zX%n6>2QuBbm2kE>(da=G)y*^DNeLBC^jAptmi6eH$@;uWx^tAmbYM0pf08WPRn&pS zHQJ@LbCKsYJL#u_7ic)ld4478>CAoJEH#9^(e0}W&p8BS<8 zkE^xJ{lO9HBkUNzB2^h%G9Ic@a-#vml#2Vmj-Ye-HJD0cE^ROTyn#peZ4J}EqKliV z_09AzwKckp3{pj~E}3y+8CtuW8JD|2^MpB&=F)6pzD}a6MJ(##G_{x=AMr#*X1522 zs7G=={qmHTxYgrZl}a8HurjUX`vO_15rm`n8ERQ#c!qurYX{h+4`+R8JgXD4b8D_@ zZ?P@qE42#t;i3V}HBMv>OvB=^($ty>oE?e1>IvLAi(smcTzkY_j-IdY01v4f{7R*7Q#QWbTg25*DhS2V(% zC#w~{1t?=hv8aW*dg)&1R_%76ljLOWWU02HSfiA}sv9*Uq>IaZ)pTia;T-j3$K|M4&%})?m{Z~V+x2qNEn^oPacj}&!M=F-Out2Q}RISa5R*F3!FENI@>uw9`3L2K*$MKw3aww7?7hOvnu3V|znzckZMay0)QC!x%iceK6(O4Gj zk)P58hDXRX>YCY0WEWI2-$t2B`Hwe6Y6o1L36iIB1(3*_r8&qEnifkQgsT3>+}?ax z9cB7j_f9on0#&|N`5TXxpp@&3v+|cI;|zx~O^O|c*rj>}QLl>6mtWOSi+(HD=y>7L z@)=r65J=XpQTu{qi_~Oqsx(`Lcl#n~R{{nQF&(PgT`NEz|LSuq_qH@x;vTf$bVhvmS$*qI|M8PZF6E;*VaF2<3g@hU3P4OjV?`f zjDauC|7}Mg2w8J%>yWFLW?0)$yW?(Iuo$;!g!w6Ub=XF84!(TWVN(|2)zrgAD(UIi z1IAn`(4AZNm);L#rIr4h9F;@h0x8Q=C&r-aJ|iehM%lb+XoTXOM`6Rk)QG( z*^1B}Sre`G=s!yrS`slY;?4j@>CEUR(_8$7u$3k#5k70bF^_a>>H))E$|cWD2H-v$ zV9R1N9spUXWM?2loFVWpkX3DQi2pzn94W{P^||(H)bXk`n+v_Cq|SN(WL#rUSU112rO6;gr4tX$-(r4DNXm>fCldXa zPB7&YAIEJq4v^iW^Ni&bVpzL@O%(+?^#9UFP3za?(EoU@*G*wf2Rv3s*>E5$S8`q5sVLuNxE4RkE(@LZ?)$YubP5u8pSE3fg&;8$(`k%C(B z*7u~tO^+-e$hCC~ErH};mFZ?UWvIBuG)#@oJ8SZxUdbF`Y@jigyfNr#Z(~pC&(oho z$LNy2}N8uc|oS5g;p7{Ex5$)^a8TjI^{e4>!Ix3s^z5 zd8YL&Rt3!D!MeYUZA7x;a^D*U*~W|zLnQm?5^w!X&a~L=Ix=SL`vRmvZ{FMwGxm%j_h%AbNESfXW+8`E;%yp`M7h%sH}T567%Jh%tT z9~ojDO2#bCdbGYhLn9UoD&8X ztu;&&*5(H3B|>6)vF?UYlpL$e6aEu(O?yyyZhpGvyl_<*P17L!cUHT4w$L%nthy$e z>bX+!LkQeTkUtZ$0E)Np7Bt07mQ;eCnlzHC28wa51YdpDa8J@$_S`UA5>>Q9FPHe| zz;)jwGt)G>97%2xO1n>@jv1w$EvW~L)oMw6s9pU=e0i2r?I|WqU8RDFC7wfyeWHzS zA#$z=*zJ*S6;U8JOlK6+K@#H|`F{;w#_95{)d)ka99x#Jx5>eU-}EEolX4I`p==;c zqdh1)22iqP2V+KQY_b(mNcCIUVSs{{!Dh9oiw`Y9;F*rXQKuQS-yqpRKZmsHzI*XXCKq=l6_t!iBMHtk2{vDCBLH06cF0nJUo zB|S#trYw&_s|CtAp$;HV2WGV^zba-ky{b8*(I&3eAT?7Kr>I5h zPxI{RI5lt19u-b?bmk7_K2`A4)r!wbB0$kA7P*a+S!ICHMzT%vrS+l_XXnfk?w3e_EllG2zc|o8y%RD;^sTnjGQ&5^|rcnv+)B>Y;5leO0@G|naifizjb4b~# zdlqm*nX3Id<%nXU=Aq|G*%Nif$e+^R%0B?bOO^vo>Yd$q5p<^eQ*(GjYS%-MpqkaW zqGeNAY{z==&%zb1yO6BxORi~9W9t6)1lX-aj57;SzUW399u*du>B*$ zlx?=Wx(9fhd}v!HyBUTVBSv4<4=}F*qj=0t z0kU!hH?0-t8o-Th_GxF}F4ftcb$CnV<2Im|Oi6*Gia4o&X0IY{&suNek$5S~t!v56 z3AL7P%7}%hEt9BWkx$G;w3d+H#@F=O0b`80jCP;Dh8fHQV@~SY*`dHFesLf`R$k^s zg1@&bi49Fd&RXL4xf6hBb{7d5^X0^#^tZF87S+sT-JVSc8b+W#{|b}LO(GS#+;J{GY4U7;H@Pg~;|lT&1tP=+cY#q7&?y>O?gp1CFB zm$8VI6!H%6dye)0t50H|@%f?K!FfOCxTcMZ1m>!a2b}IuDEPxIpPe2IS>tBMLB^@t zW%ixSgo>?pFXsN@O6yNnVBTr#0v024uSLSTvUH1C$BvFinpU%23pW`rasnd07)YGt zkn#H4+(7@IIuuvr^G(;p+dAgB`Y2BbjAA7pcwXe+1+6XgwknQqW2|F3r?}?4J)hH2 zk!%Cb1{SAV>$s_Tdo8QEtc(xlx7_ZfD^1V1C*zwBX^Mf*#@7;{WLm%kGj#UDW;K;a60fxbGn^4l7Q?Hl>KYbtF!`47t@t(OGp z%M_OTf`r^(mPkQk#xpZmKv{CrWD~54(-`jvPAxcR_$D|K@k;+ea4f__pD#@I|E#?v zjPd!TtrK1vb5ylQunwT`1i*J>`FNomByp6A-5N6O*`hzycWg=GQRTC(#o~;j3l^F< zAor7bNIW(p%=AVK^Z_uXi8sc{jXZJBf>VYS;%vZJ{ahRx;-}E`6ArY7Unk zNQaw1(oajgjU6)o*zJaIvQg1r3_;S<5fk(nsW>=9CzHDQPt=+vyL`MfJjpJAQZD*D z;*Vm#2v{k}$B1q=?*fb`@OqlfSNXNt%Nn3OQhLRbtArJWnu*GZIU3V;#lCc;@xJ0v za)EK7;%Mx!fu&dz{Z%hlYzdFkeN^az=V))rfBR3={FLi_yws=UHNCbK z8GY-9tV<1>tCm_43>l@_=2HE^LbZvdFUx*l+^u&@+h%yB+nJ;_6zW=H-T_AGS6myex@zDVA5} zW0jZ8*UeQWYfb-|UKczud79YSvBomfxYXYUi&2(j(O)pmikYi-H=LVar)x2U&4p;U z>04(5-(qyRGZv~pYWb7qs2Vk^MqgAsR>=X%ZpB7`;x2s&;tVu5_BD)JKDX&{HLia; zD83Bd7v9oVc&>K>_*BmJ?(41J(w28!hb~Q?*|`JejA`rGk0_kK#kB<$9d@dHKl)AJ zUgtr-9s-r-63WxAl9s+^_cN{n)PB)V|8dl$4Kr>AQ-%#>#C{1?7Id3?tz!*#W&#%J1F` zdso=iwHD!(eY$fWazUD~V+SfbDb}?e&59AW_hNp`?{((kyuz2N*OzVlo7GRnqj?{VJGT-^=yP6s3LdU5vNMQT`Q313#Fai zsGHe4I@Y1Dr{P_zF}IRJ+c#tX#!#JQ_;K?cjtBVbVcYG)#PYy3HYVweufXa>K}~43 zjHf|Iy)x}#0ADNgam>3w$|yOJ&C`0$qulB?b`PNbs|xE9p-+`&bs8~y3x+$EV|QjR zcXi?%X^rhFJS%CsvxQI-gK*p={EgabKTH}Cw%&G|YzQ2*iYYz59Lr4FoC%F)G$VA> zOQV7btiyHD?2$nI@8t$H?eD&VT?VvH@5W82ywWMg-7n#GDDejidR$UMd$yq+O)N@P zIP;00lE$=!lP<(G*#9F-qgLBaQQ%=i*8ixRf+Q9ttq-^i5J$f|q0S^`_5&WP1{QFB zK%2q=vP^Z3H?pa=>l(pX`=fIOF|(4_VI++zX>|!nM+#K!b>zCN{Z0`1cPhQjLOGcD z-BC(~#FW}+(?&-1*{0BLg!NdC&>cZM^AW~wU#h8u**2lp*vpV-az4SYw?v~Ys~ z%1qv|#z&pMD5Gl49akupN?+GbYII3K`$_7N{C}MNw8E_QZM$g~Qjw15bWGxFyPp1d zG0k?D(G%5fl{2%#+AUSgBS9>4F6)Lb(YTh4n^0|>$ms-3M%A2~z+5foO$Jg%!e=(( zI*aJG+5p#Y`rV3^t_2K2v8sIu<4yj$w)adxo7q;%+?nEVEMH}DVR2IgC(E;AauPsOE4=? zYw{L!`=Sgxgd-=E>8peuqn>Hj34oJh%6|n$fZ`=w&@kF{Qh2d?SNmb%y|Qi2Z6e=g z?rn!f3-baUXGAkHJ?vLRQTRxK>9XpA_7rJI*+%C)>4Ks^ZCTQZ zxd=y{bi4-VqMwTwlxY@QHA5Y!ZR0Pza!tC zkz>0r-v>};%2xuEWO?PnO_nq{2cY2OtU0GlEP3LrL1VJ)z;vNuw5$`LB*{WO?x-J0 zvVpmJDpmoM>EhD5?d`c5NEOBzrO7Qtw8g0(77jV;)QLHp?d9sF>1rEXJz+_dbx5^8 zuG6wprCV^RyrJ@&HE3`sIn#xDt8)5ys8*(^1Sn)#FF;XBZUb4l zQ<79y=zOZ*U-`4`gq~4KbR5>bFI;cGtRv^_w4K+Lrk%C^(oIc9TZ6SPVlPkm< zZKu=pw9?QFBrFd8sdMmu8=sQ(7`_`20As$k-(h=F)`b;+~-Z-}CT zKYdq`DcSdWH=~l%j6EuJO!D~dR!mYXs0)L2MoT*}_$d(}*FS{mA?eOdM7Mz1j_;(F zDGz|GS?e`ueNFx2_TAFX@CD|I$pqeEwI+^tUF(1!l2Q3!`Ak$&$?3j_=%odZdrx74 zv;Xbs#d@akyD8YmNq@S^aZh8*JEI6Aqgk$xM9+xo_7vjFkmNQt>0v;C{Us%F${p(; zD&1?qI+N~l`)+Px)&p~eWnBg8{}AVM-PGkt7=ER*-vbLP+1z^zc*{H6vj;yVd$`+- zf0~Bxf)KVOz3p5=TppY4I!!8z#A1L88F=*M9Z6U)5@c>yp~%k znUQYaO;cIGezlIzz6I3(%iNQ-+xr*bPgJJ$ejzL`e%bS$*ibO4`y6R}_KGeO=~!BG zC!4HDdeBitNr_Er-$^k?!1Kc$(U>z6A1uirc-j$xn(e^~|NH^51s*QfFpe?YvGMNY!?Hq%BIi z?CPbRij8icO%IE1az@e5heI6}1}$W+eKoT#V1o4)D{aaZ%PY3otJl1MljZi!5XQj) zbM=zD52*h_etgZ7UI}e|1*1nnPbuEtZKdzezuSdi%+5O2nZ}@`@;jJJuf)IYPnd+5 zD(83RyXdO6ZdQGGlVc2Pdq{{qfvpYjvM%GyopR9<#2x9?W72c?xqZ{qxKeZJt3^_;t}2BSTFKtbnar$&-~NzlZ{VV?|RBUnfRr>jx#f+*g4E` z&1ba*bFPKg+P`wA1*h8naE}L!v3%p@O*wDY@d#es#(#N_+`j7G@f^Th_3?qXOC?iK zU*pkZ;u_0-c5mXIUY6BG=6U6NcfxpynIAe<@-QhYTzNco;+*yZ-shNHX9n--e2QZ| z?`3$U{WHHgc!_N{|9yamHA+x5<(#=%5arcnjN(6Yf3GX&1No))69B6VS+Nb-j_(xf0 z*In_mB6;T<@yT3E$8Yhy46N&k_`%Z4?YAY-38l^#lGw%gwteEQQ8vdFF>5ZxzEMn^ zU2DULjR7MqkHlxEoHnP6djZM;5eJ|=5dz7gY8DOvlw#qtDnoazoK^}5X5>|c7dz?l z5xJ%go=l&Cc7bIZmyU1e%HAcEIH|IIi`v@?WQ|c)2VHtB%xodMJw?JDF28K12abZ-(SUBQE%1su6ydSrLNAastbjeIzFhfb8d9FD{rT- zY5z~<4e^VfKsKL08qT- z|I2-z6xivN*Gt5e`CW^Q^peWXAmgk8UWcFINzT0XFNQT~ADpiZjmb)h zAp`2a?2|NUePZjS2GrnHXlB#69u2Iod9!mNVoFPyX(N&dex>@1x&zUQo?{|mQ`lZu z9(+4_GOiC9fI5M{huYV2hVTh9v-$&RCU&@RKRF#AnOaK;C2UymhB|^YBRGk=oici2 zIwO&~e;ughMoa48*w#ky%I>3(TabV@3ak~XG=71fgn3{)|$4{L8}!tcTTQ*9#rB!m>YlR}93DSEOO$-LkNWfu9{>>A1rs$@bHT}k`C zR?_?%7PKO{#R6~b9?=?w&^fk4_9M3#vtY|mFO+TYYv|v?wa7y3T;?;>K3p+rEXEU` zi@btG5lEmnxL%^9IvRh4)KT!A=uUQ}D2eYWmlr%Gy`{dLT}|$yqb5|*>KPl?^fxU* zdktWkA7I9HrL^cUzwEzS=V3n?Y>-R1w~F^LIR1z*0REY9hKWGJh&@Cz>MTiwL}3<@ z>p^NPgOXcy0k?~qT#$`_MDtp@pKy*oW5Fci9)|nuDw2)aG#*7=#A2;}+8`&)UB0s^ zgYcl!()^R?u-CP$C)Mjew>FW-DnQVY{VZ>0Rk$Ms7j`~FtfTA#^ z^pq+twu}C$APsk!;aIvCf1F8KFp;p06&^$(YT1e7QIu)yTdSAU-J>=xpVlx$z0&cu ziA*cA9RbDBuIRBX&*>%do2?!6qk_56Ohyqy1^btAgm4s5&dfxtMIK@9Y`%t`%gU~b z#K>9u^0nB5Y`>*@aOc>E7EHjO^s2*ccQ<5A?r+*zv>waZwFeoFlu*2xZN!%y}& z+mfa;Y=rJP$i(iFw}MO9_xZb9e{kk8LZKE8nSh0da)uGLh+fX`W(8_8x1jPmTEtc4 z>oHfj153VQFLFOcFTp+FO%I|HM)MAiN0AIX_R4G3C;0dJF4ykhKXqNG@8!R@ZfleY zM(ME4RRTBJla@e%7rz*MSulgX55g3L5jtP>m$;XIb7f3bxfI!#UIX;~cNNx^NWNR+8`33bwC|dN zBx)JC`Lm>mcdunw5=O6Sg^NGn2chG|N8nCauh`Xe8j&c*SB4?ii09?qM^6#mUy^{y z6m5%6#UeyafmYm3QK~nWuu*6qT3zv1b-j0g^#m2a{aoz?)hNq>dM{;XglBH;|bkTea z#wk$;+Hi9vzTQ0iadF{LX8CDDZf{-X6aB_^RP{^!NK0MqYh8_YYW)jsm(3gJy0asa?2-twE}Xu*;BBs_Z6jm_WIu0t5e{bk9pc(i8>B9MmCs-+Ti4lWcRK z9or>+22dI#!-J2?PFnkVzE>Q#bhVGI`fR>o{#NtebW5|f-p%+?8rSg2=*HdEbjILE ziw6zpb8sqfg{}a`g1Bqhjhmp`HSOhkc&hqBt{JgKbu(FnTBLk7pNvK*b_BLzXURLg zdAN_ViG#e-yUwFM9p&HKY|c$pvmJDEf6aV*vIbO_X&WcG-LTMdm5XSaV%|@E(EQdk zjEid-G+JQ6tq_B%u@O364=?Y4?boq$U5H3+cCrGwRpU0FhPtbA2DW21C^NkIIIEm6 zIJtCT*V~@7@}f?Jv#Ap5dTTDPmb6nf-nHDe7ZP>7$+3bvt&wG~r5c+nZBwuhT6`>r zp{Kx?%z{RDh{hCG?gkrWxSaD6-l=a(I*6R3i;n6=fi$>TJ1{A#(*Pw#v1Y|TCFK5H z-S5lR_I_|quGrU;Z600qtV^dpP;;;2k|d#igsXvbyy3RsEiUqQoKj<=OH zG__Lf%gZ>>(bj`G`7nd|L6SS7+|(R31Q-eq&DxFTX^#PvBGt1M1;yoSYPy-FqE&=8 zNBQz0g-KSqe?^Bnruy`9gSfBuYM+i1Q-7gnJ7uVGUDs{Q=VoEYXULV7R9Ae1XX^-O zS?PU<(vh800rR!~O&SNkZYiI40rA?Xnzaw@r;qa%Vy>ybtXu)-H;DSW5c``JxopVa z&4;acba6|IZZbv+{vu;z4?uLhbGTcuarDK6G4KIAiRg_0ZYq;zqLfWUav)}8#ac=< zR+5)UjmP~-rc*x?9P=@>%cKRfHqqIXfN^?O8O>+tG}O^DulFqMBiPw~7EuHlW!Zr2 zhZ3~Ss4uXi($knFh&jAuEDq=tvl`crI)nR+A4Y$I-6Pz`mNj`1pX0hKs>mbock`ks zV~AnN1j;26Yd)O%iTrTZdfIC0`f)m@l`bFr-MRz%vUe;r5}j2GrBEdra2eTx&}xcJF18h#Zapm7ahCo!?SiFkz+nu{mhBc~*z z$OkEgC@tkYb?2;g)LnGmI2~h{={=a#@(s1Mr?8cU9&pw`USm4V*)Sy5q!|f+j#Eig z$aFlN8-iL(z);ttpAzG76S1+RnJ{l0p7g1)7;hlIEH5YQqa4gd5KmDzCv}qAXq%%{ zb5*7XLIPxWgX*Yb$E(&X97qeZE%L&wmTi)ksQld}rHk+mkNjbLVHM=6Ls>`OC`la6up zUR~65Zrng>wlm#_}fZ<`#${2htsaF6->=54^g;P0GyoQUIp@aiI0@-_Yc)n!Oh zJLfe_kW92g8t;i;8EDPD;=RhMmU6LFG!FbljAp%ru*I3=<^E?5(>n_UR}hQq7QwqtFPp89-tJ|T|o>fo|YU( zepjStFGGXmD-$PSHp*T_-o~Dn%@25nUnk{yt|u&(l=tbX{xyGh-L3h@oM@X>XE2rO z7dCK=3dO{xB150xYV$Y!UgqAGA>CgRr!`d+#2>>-(ZBAGNu;l-2j_->t`MkJ&EkZq;wM z?vw9olv}Iaxg(nEfjEfET!JiX8w2F8AoYQaQiV(-m7C?pkGK*ZQ?5=nQqw+jh%w4G$eu zL1NROJ(=kbs5ZAA-V*Q%SFK9$+<+gc$n7;(7<)IgzpLW(OtZ$;h`VsQ{JQdv?ed_8N>?ubU6YUV zD&t)9t~M%B)q-_QLyZUjv8@ODLG;#!S{5wC{IPf?{IH3i`3>o7_z`bFY4o+v+~g4Y+nCqqm`A)WFa7U6t>aXIs8jf9YfEM%SL|StYw!zrE`Ze|4j{6T(n8 z$Gff(Q$a7<3y=rF+_rTsCm=qK_}Vb&8k@7&6CPkWmzjfDVV)OnLoGB~Bd4Ha^dkb^ zVu@OJ&y9f3l+_zqE?ec_uBs#rm0J!~gI5T&r)u+-+hwl$$i87dvf)q9dq#HCw(fYs zXAq*3jAXTZb?s{5w>sL#*Ib6iwJj<3gRQftW|ktNto3oHku)cXZCpMby||&Pvlg?oDZ#G5qMA<_G&oudPFan2fximh5_%va)>fhu`h#3avcrk! z!(3{M2GFrKz@2%7GJEVE&SM&` zzYtm163}5q>A)h}N%XeX*ZQlNyU={aYTPu~u&@q41OAm2Nmzs|CS4^iM6uD;q!{#R z;H_gmc1+z6WhxF<@(*F#aUghI&DGirWkRL41PW zTUSB4OL$f?hWv^6G)qi*LUv0?rtYF-0{thI)T91?=_l#VvAbFSF~z;-p$AZ@u3-2q zbh)(^(S(W7v5<$b>GCGjM_jld7L$MvVTNI`_&>x~I3eK*N{U}W90qq0hDoN{U&LXu zy!aRC0;M){ANdfqARbLI(W)c9QKj^D|39=V%!OljGq1Cr_fCZb;zzaa1N$u+r*^KN5G) zzGvokv^WQIM8t&{oPjZ7S?6 zl`4xv%%geo7bCeeGb0tXl|F?SfPPJPB9CC_GJIQJ;j$TmFw0xNGpU-5P%3k;bRFy_ zD~Pun5ziviFCb~GlZ2h9o$TpIH2OOm)zXFyVxO%Yk89wB6ldc#oVtv!gdxtlcqnlN z=Wj#+Nyx;7vSlO&BDWH&Yr8-N4J^Wy5`V zU^*00%QFzlkt=y!$T6rlyn`)Cm=NBZn(bI3f8sJ1&d&Ru@eO~19~S2zoZ$yY1Q1X2 zoBjRBa=vlQ3Ce4JSeK(ITI}8C-F#8>#e{1ih%T!8z<)*kk|BsjWaXZLeh@Kf@8LNj zDE>c0pC|{>gM1@e47!Mp70s!s!Kg$Nmi1uw2+wAu<1P!g#I+MVg|vuS#1J9Le+KEf z5IyDuIaYA7Yf{5IMY==Z)F=OHd<)8wFIUfPxe1(=o8L;2z2-JU?#rAsDJ)Hvi)X`! zq@NM-h@aAxAS|j>imZNzUMZbi^c{0cGMJHr^N`fUb>hp#HzI-vd~t`rKM5=b&c=}Z zMDotw`s>;jM_{8)bKfX!j?;itJ3;5v!{WVQnmU#9vGu8H4J`m#tO~;Wzz!+9;b#%E zl)j)*NUoxzT93M+@LA@LnJK5I>#+-Ezhf`r#Il9q`S`8U)&2p*v(m|9P7wLx_nm2V zPfaBIfd;kFWt;;f*IQM6^LxW|aYKtvU&yhyPSQE3`yfK?4cu1f8|?%*5nivsG!G&U zs5e$eqk>dFiXdpJYF@exqf-iFuVNo41mT7F+43&`nZ#wX9ROv#w4mcj?H_xi9bdoK zR%AHesIXS6JeucOBE%k`i{=nc4!GWwOU1UHH8$bE&fVa4JxCkcbHwhm`)rYprhyMDCuwn5#n zr%j>U-K2KR73~Ej*gmkowj8iNpw4Vfwj9Gvgt*K$*kPELN!j!h&NX7IM2MdTRN+Y! zLcb{OKeSXgF4hNgQgduB4d<`Uow1Zqr|bqO8S?8L^);sMgSH2C_^x?|{D!a&my+4| z!{sC5H1{~yvNyK`wKY&Kf;TzBv4)0evx9ciJ4_?&H#&TQrZBK#QOYRP7Qv-}^pHO)de%necj72gn9%-?tc2il* zGW2Rg-&_thU$=Tj8oo#qHs)V^gKCWHPj%v^&1zFE-6H)ew#~A5)8nds}KHWYRGB1y@Q| zL9jGEr2%>q52r4GXCd6F9}wF?zO)^vS=DrUFM3B2hEb3ClI~)HaqD8eSThL)b4jdS zqzJz%&J@aGkNdnovOVp-G*Wzv7znd*evu}^FVZHE zejz8~K9KLB3gF$8J!n(&KU5LsaCHQYgqv5?KwpM$O*_k&OjsN1&e%hWoQq?M$T$2d zSRZL#9`|_O3|Z$S^fD;e-hsivHW)`^>F^_}Oq>nrCC{s|iV876xun=#-`4STeMA&uG zLSlZi4|zH%y2?WFAo~`YsUs+()AmxYQNF}{qd95ebK&%Iy1}o6@r0S|agY6kwav8( z{v9W@B_qS};rjI`B4LE$Bic$hESiG(hbU)buvbY*ltJ8i(nstD{4=r>#wNU>lsD}m z-loo|$|2pRo+(t4uhI-@!;};B=9oED3d3(Mgx1Pz@GGYOW*zak%W|*>+keBLpFnDrLJr9Pn~VC6JmcL3K%`vjYb-$WNRp$UVGS(QJC zTN(WY7f7p_Gg9A^mos<7%%(6{bzyR9GwYMzGP(~Z!{aWqn6the1-VPVYaI!j&q&n? z;ZjD2{2byGT2V5ATUH6&x;nV7}k#6rR??p&r7`vVAbDaD`wJ z{E*PX)FFNfHKb>#a3L2n3Ed(@LEd8oLUdypwoeGI_=Gzl%r2OXzblMPT|~GnoVd80 z_(AY9OiuO`eDy_A-U_~X+@+QXq-}daKV`4YP%v4xTJyB^o~%Na0WFtJ<9ER}OD{5K zz~4%BBsMZ!T7+JX!b(R%WN4%0P{Sk4A&H^Fh$_0^uphVfBzO{XM@Wt%km0~b4QGs z2dPj=_%YC7)hNa$*bk+gG!c=moPu70R4VL{8K_N)um(Q*vwUYoBzC4eE?*AT6l+44~jm6BU2x}Cf$ zkUP3Dj7nITwvM<7PSUMMB7-YsZ_APaS@`opL`?LavU{f}mM-aYI%^@XJevENm178&sY%3q3Lh2S`HH1k4mnBy=^95N-J=*g1#N{(8425 zgiSX$qT=B=6RLFrVz-e}Pee^H(8|uBQ}yt?cnnFGx%33KLpyJg6*r*%5W0mhNwwS; zN8&1@0SZHoaZGR6(|Oc*xT&P0Qk~oUuKkF_-okJqxj(?K+5+e#NQ3($ZM#Cl8P5To>#NzwhD)XkvpcvP9V=&Hk6b$(&G*|SD4f2F3p_EXn*e1E-Q z?nWrA;mRBiBh>Sn^@h8c-Oy>Tmva_(s~C# z2H!OlbzPJsHO`$kkz2)jG-q}F7*5FS_;RvI-?==0Gj~JB)U;Q;Pi?~ZQbBsljj(UR zAjhiFcSP6ie-5!oep#n5DW2xMS=VKIE0?sW<)-RrtE-~1=1*M#Fr+SD)dk$IzoP5` znj2GP1<*9s4t_24h%>m+2VT+CSy`Z*$h}_(Ai;cF`U_;A;7-B`begCk{7=kV5;poO zrjiAFFTyV?P`7tv8TGtlcMfu==Zf6lLs2*ai&Y%#+>( z?lfKE{RejP+#B{ljr^I6=i;qES6BxE6pR9M&al#>c!o3H3%6Ic#OSuX&{}s;$ zR0w-D)Pf^L-W40c$Ktqx<!;#R%gQ9xMsMC6d9CPf$6Yal7~eDvRNh0@b|Hqskub$g4gLszsn@-W7RG?s6d zy)gbQSSHu1rwTVIMqp1x>lN!khj<<^P2wf71N*s^QYDyGpDmjSJ}KWT4~5A5%ZhNg z$BZ<^Pk2`R4Pd`g6uts9A)bLxAvV(GJxdvcK6iVIf|#xCZ>~G|+}_Odhw6-x{29<5 zdZVBg4#IfC2G|Y`6;;7+#mmK|%A}@`l1yb+-M`Whq<=YI=7GTZXXU++Q!|p~FVNBP z*A#ouV`1k32bK`{1XN-3y=TFBc)r_PBnvmSMQ~Q2TWsH&uA#3DOL#poZ#tMi5z9cI z3z9GjSS(D!wu+-g$=JCjyEqB&R%ew=z*EXSq{DD5e~io%pF1N-?u!2%KR|wv=pS}Y zv5$}kJ_fcD3%zGSbBT9uZ{f$pvDRhmYI2oL%2`M*G7M|FO14sKdG4egmGZ}u^MGK% zM6yf7+-Z_zlb>iBN!9s_BT3`Tm6AxZIX_4`o`lmZvf(5UKT!6Lw1u6QeqAX$dZT%O{NN4MY**~&kJD&G#ex`3 z3}=%tOVh1(vnX4AYi5GDT)igGD9Kf`Qm;zW)j{#zvN-y8*hP5?eKzo^qLiLLcq8MO zmb$$Kc2S30_BW0*DXdnO!1!4o$=+(5MV{q+Foq+Y+~J0Uieg@@fiHZ)FVer_qzkxu zO>Kq{(1(^?6xnru4TCoUS2ZDSZxw8H zQVXjg)}Ck$YlLi|?lSAL^(_f;Mp*NZ_@)%g8u_EEQf@Md79_JrG8`9_VtWl%k< z<)!6L!)E6Lot`z&=|PTTH#sILUvN5`+45ygf7;gzW4U*2m)UmS2iuSuyI`=jy3AV` zW0{{jQ#8T6Gc{H`!`x%a9Erbaa9D3?x=|e1Uq%?P!CMp$^vO(0ckMyv$+|x}?Uq>$ zJvyf7;u}lad#P@+&b0MVF5qYB*7VLp%4)W%qgCFQqiq)o<~AK{D`h?9o^Oq;zR&b9g_m*#u}=S-W5S5$ zdnv7=c>9&uTjD^QK6I+Y*E%?Gu(ZW|Wbk(RN7G2Rcd|hS$~m*vzF@D#zwYz=NbTN+ z(s_TW>KfP2tyK#W^%*B`dS(`s~u`B;Jr&JxUJnCwH{xBnYOqdrW^V zENfN928dwivk;khO7opj@sbVpb zT{ji^N9kX`MWI!$Z5%3cMP{({oOomh`)=(3#?;+d#zT*DH|L_*Jbq%zY&=nLIQAaS z5=Dmy@z3Iq0p&!K%-8Fe>ZC&N`h%{4rnj(x-nD0~Q-OW;LAuvqZG(<@hz zU-5q-vFi^KRGxACls;_wZN4j8!*kazkjwaPDwZOc|4EswxF>kQ4759irv)3p8KO1p zULezhT;mSSlgLUnFh>gHv?~Y6YEs0?RdPY>5yVF^H)JL<2iPBwjZT3)yuM&R;Y*A^ z3qejgawYM?$>s>@DA7R82kC#J+r%l^0r66JtDKP3%N&Y0=>S2Z;)C=QYbmf?W~p8a zO67&6(a;D*K#m^ztavef6>J4o#qL$QfR{pwln`_!AOjf%|2gn3dK2L?M$0IyPxE5Y zdg&6ARXkf3sVSBavLi&iG+$l=hsg%XKg$Nm9xGJ*GxGI757tx#7g$wY1cZSFB~O5t z;L98oya07fUjXI8gJNDnXO+God2pBVcEAj!3@ILX2MtD7GDb@ycGdn{m<{$az7{2c zV)ZU@FnAT;EdBwNK<`R$!w_#HT-bSji_l(h!gKxJt@qZwzjb(zL$VK%S;d^ud z9wNGimOzuldr_-&tfT|o%zGo{q0bv7vP5iRRlDpr)>INNzl1HzKA>2GeV*O{u<-Dh z$G~@76Osu&BEkYvpj||?*9T=M!L)6lCB$~yHQo-D!f=v*NF}5f3l6EI*hb+_6$S1T zEmP?vo5ea6$fG3xPS6Nes&|4}!r5w@BvnMI zb9n>BJoUJSHc7VHy>h=aguYWOmi4FCWqZpW&~4Lh%D2&Y%m@XW=1zowVmdn@4a}s+ zczuMcXm`eFIf+tPe>Uk1Z}hLZ9R@RXinrVlj^5(`p}!A27A)0U#dm}b{YdV5kw$m0 zAy~}Sp_OHl5}jYMyEH|+D@!IDq-~luM)p`6784*}qj@+{saU328ZZM`qM79N5jwBl z$5Aq<5{qGT=1FO_VcfJ3S-gJV>Nj1I}*x8JyRSWGaZ;zgdJ%=8d+m1>*VR?LEX{0$o?F?yji-#TZ)6B=s;J>=W+_A|Ji0T6^MkMJ7*NL+AZ$UHb*^ke^?KO6CD z>erQ^7|ivU`$YJG*KdxIvzqTcyRgnv&}-I^@(IGB9h(Ybgjd^3(!Yo6>GoN+&@8rPA{#25hGcY7^(fj0onNyN^TYbo z1%g8`s@^I7j1Opd#a)SuS-JIJ@MG-#74L{qO#ww5!oi)LSxvm=y-&KMf&?p~*Q@>z z1x)Bct`xr>xtY8n^Xxy3@rHnmKkEmSW$jdssGY5EQ?9D}L8*{o4OM6}GPiL7(1Loh zu8B!>0mqjchyG}at)GRpaN8>8W4C#miiYFu0>8|3{D&|t=`6t&U5{Q)ER}?h|E&U~ zS4RG!@{{lBpG2+(9=rUeKfr4&k0ER0BHd$n2AJCp=gF-LZp|t~%LX&D4a8{JAyaaN@0rIclPw_t@Plk*p&cAS&w6IPJZz)JZvVH;Q|zs{k+dkSS;AIJjqC{KlhfWrkm z_%IloQKA$>;}Y*EcSCEVwjn<7`ti?^)yi`t7olcE)PD-z1Le8=CO%=OO%tUnfkB!C z84s`!klYjWgm=mJf~f4h0tT-NeE?r5o6{HA3hk|31~$RbGoL{&@b-dAs9iZUBNHxE zo=Cg~A4Ec49$_*@d_fJ^II>%LvTn7amPW3~$5(iSg>*;+I4=-YD5ilrkJP5FF_b zSsK9=gvveQ0EdY0={Ar@yie>0xvMgw)<7B+Z~Q|z zS+!|omoiHAqkjzYo(ysMh4NK546yKD`WW3QIzw;6m~|?>0LqX+v|8$v#?r<7L(-4* zRMvjkCVE6ogIq|vm2FdurhepiQ#_|$&G-kfQAZQ|gYnea$Xnnx>hk!9PzSYdWEV_O zdzqAK>Ih@DxhB`zVHKZqAsf*r#ckS`0`ub&+>LH%;RB$KX?q`^W{*IrYN+vC4z$ zN|#@73B5*to&U@HjCwD)Yp%sog_lginw?K7ekUiz%56^g^UkCB6cBHhyQ{lSU4sF4ey$=VJkB}VJx@(bcK z-Gq96Y;!)fOb}+bqaTEWZMVT`B3E0oL?b$3Rq_51&$ZlYoG;;8N~`0g6U`e+&C;)C zuiVSBEhbT#SR5H@HFDp`XZ` z+q#k}<~O&jKvxQI=T_joQ0aImi4dhVf8dpi-I_xilf~EVMAZ$+I@`9=aZ-`>U2dGL zr`0{JLw4Izov=c#Fl!?H6{)5dK4}{xWYj|!VQw!MeL|o`}RFRS($Ci>~k`K)Xb91Ci?WBl+pR+|8g%Xr+RZ<=7m#!+=jFv6wEuE;6Z5w{7LiZ zqoR;;ka~+`%4o9lXo>uhhfN&|GP75jczCn!BYL^9oSK47WjWC0*j)A+&<(rK*(b?k+LvzgaC{l} za$_UWm-o5qU!qXpUK+?4$cN^ZsYZ#Wr@mL&#X$TU)kewoNoF!r<~NQ<9+DsR{Y9<< zj2>3%62!Dds6nKsE(2+7nnKP+?sC)7e&{$}0Dz)S{si$EtcSpp*BxUCo-};Mt_VL= zwc^7?M@lZ>2gE0GlZbJW?Wy;Oh0=@hBZhbESQUla#m-3>jC0?}nE7ekPpoc28LK1)0utP~XSeH8&u$ zBuG^Ny_B3+-hla1LQx6dl6i}cGEDq~J4ks_Ufke|M9E)No<)u-+DpcxlY!hEA-WxS zl-dgm0e8grz$zi`BpGIar;eM6_fdBGe!yQNLQhCljxzm?6pAm_goA;K8N^m_h2pp} z9U2MDke`8;0GmY9;ZQKDX)fFauCHIK916Kt@|E+Uy~XF0zu};qN~9IunDPVpu8fTP zi54p#OyZ+zq4|oo8wO9iGLcV7&R<1-XDesY<=$g1s2!YL<#75R( zmT?6r3(xg^i`C$tJOTV1!A!xEX(|hSN4^U8!1an0JP4kkcz`F!4gm;WFH8ly;dag{ z(2jT1{Rs`ik5)88^YFLD-QYolXZBq92*FE zN~-=~Qu;7G8}7JWwS#8K>=c1}$ipc)v{inV;>hw9LaI@45pbolI8y)zl~7j%j-rw( z{K0ipP|+-CG8LZn61qT*N{NK?$lq}j;j2{taHnzvl|GjF{!rn*Z;-uIi6?*+Qg#C@5BE{Gg}1_(dg)kDIaeQakbJsfI~$b$W5}0M@Kd10GutQ&zl@Au^ z&QCi5p3rHgNT3>>U)W*jnr^^YC2Y`IeBUVnt&1m!j?gS-QYNTziV|O9HYJBkR#|_5 zJEi4Tjx=5R$+De)NanPRU=NeWn^)F`%CDM-&-|zmnkdvXYK?`yUT~RVf+vIw(KC%Mc(t|{`C9Zx`)<@KM%s3RV8qG)+dC~32(k@~c?Wb^$Ata_Yb^z1 zmVr0S1APZUmyC-&VP%_Pr^|0>nf^O@Ks05x7%dj}oy7qjh);FSl&~e7jtJg4sc-u< z)&S|Dw)C2Tvf9?Fva|9YE&TlV^4ZSD3``N`P$d(JL(MHQ-+&amI?N4pvE`0g1;Un> zzTOaE&h{`XN1A*Y?%y}IlU>4px;)T8(UQ5ZftBKlIX5LE#NTG`V)iRG&YIh}OIp_1 zS-oBQsl!+%mMv^=%xB4mw9 zSblq0;I(F&Nts~wr$g0^l{)N-T2kE&QmP#_u=Kb3eC=}qPkp;SmAzE`xS_Mwsp;1E zynLLdFT1$liTV%DADKe+9j^B@ktUY6C^kToB8Umi*8VH}82DTJP5f=BLYF8T+H1e= zo?-(tr}hY%O?6QBYV)xa+Ouu}_?!-@zb{qMHI2jgZ)pvyfgMXPVb80bMDO98oq37r z`t&L|NiXK5WR|F1_$|rv)FTAa$Exq+Q3`AQAsAbgZcXmYYnP{mwF%{j`>TR)R#98QM|)>Hch z4>J>KH<3%Sg`O;qi;1W05=&^Tnj`ZFe6P02H~KIxC17^%b($t<0mI5A$RM(i_?L4E z9jFR!Is%+k@wj^=O4VN8LEdlGE&d`_6*)-IQX?j_g!Y*eNRCMzIpxAo)kk!ri1G zuN$5t8{MeD?XthB=i;~JqB0o~r5KgJlYkT_GjfTGfGxRF)g7D>ag@pR4++o+i z`>F_Kl25woDbmt=H8lo(&Jfufr-*vwZ@EO7hz^#2RSZU13XIw5JEZu^rLh4(Yr`ij z7j&tffUN<^G7s!06qfe|PlD_jarj<%Mv@)>2VNf2m*}tT2pvLrA_D?%5oXlICskF4 zh4)@Xw&GO`E8i3Q@tsNsvpvnn*!rp|$R>P9X)y8)ADb73 zmf^y55qcWmo`hho`2NXJSSc|wBnE3D76e|wU5MpA$@qQMOeV!c^_gMiYf_78APzYO zLZCC`1Q`d8B;$pN@I11J^9BAzPG(MjR+0&ohm?PjB_%tVZ)8f|c!VaQ8C#K?WLe@i zG>jBZo`hORVaQ~3KY20mGL}GY@JYh^Q|`T25NQ-+8zgfnHRcb>HDZYAOwr^^e?h65 zIKgFTi)IMN0S{6?sviKu>fM!P@ME>3Bukm926A=E&Fb12uE=0@P~r{*rQb}BK(5hu znUqNSUEmdT0e!?L8Ec}K^j?W~(eoJ_)CHP_UIp3=8^FJS?}j?*S`ajh7Lecr{V`4y zl&y#AHbYzW;T8YEV|3SwAHznSBj<_IO;GsR?CL*y>``p}1(d395ipOO>=9_``fy5C%q=GuijR6{b6NmCzuQ zxMDUWH$E*kK(CAob3Vc{ zh}^C@jrNx>XuSY@QUtZUlw4FSYH{Vy0sNg)*a^T~2UNQj?AyG({1%8e4=y?kUbN?C z{{`jPpwwf~M(e$VPT0>nIBE;rW>E*vQ%0NVz;4Q76EL&^i82o9y&A;~%qK=n&>E1t z^6*Xxa6rDVLoI1n1h;GXMT)g;vso{Jh}OfkMZmI_7v&__uf?mV4&*ugvO~c;j^b25 zD4|)MuoyaLcSgQ~huHoOUJM_zYDbNO4_hjRvXm!Go0ycI#x{n?$8^caM%n2(PXVJm zfA(idv3%#OSNz_J*v@yXxr(Em-Zh_r@Qz{SBY_3&-i2R*pKUJL(?NZ!e`*+bzomct zGsx=9i2MM3awJbU11H;CM+L(THlHDs61A-Fy#|?UV($5Ij_xal=xBnx<4sHsn{izM)##Jz00A60vf115~PL8K$hk#7Y(3}yjPB^UDE4m*!S-U|}Ji%3m z%ewo6x(xZc!8>#ffT70~-Aah*C(#8XnaFna3Qj(-Sv|WcTe4VviCf55s&DXe*`w9B z`N_4j)lUUcBd@vqhUq*0NOy895PW4j{lr4|HO<$6?MEa?H6>BG)RJQ|<0x~s2q3Xe# zG@bC19+xz05u71%5Z0y?G539^6%^$m){C{&C`lMkLS;){vBD^UbWzP-iYuGJxQ9Ar zp9;TFbLHEzy(p!EmD-!?0^;M(PS%|X7MfF1T zKEp~Yz8$`%B4zXCuT(4L6Gb0YFXacgPspB%{Koa<2*vZ7RB{@?nK_3P1MdrGlM=8w z>njOC<5H5ycIbBeK~e&IG6CzeRNm*%R?g^#v^-e?xT}QbrIju^(~wiLZEF z#3$7VeBt=ZDnDXuz+{z*nC@*S$EpH)ej(qeE-|e9MJ@y{VTr_DnJb17ABCf^&8pE& z6R?-6hz2*@M-^PP3C~y6myN(_m8`%8?^1!89Q=W5?euc|hpH{^A0krME$&% zpUO?Wn;~+$Is`bOTyKt$yhjF@ZU{V(I#U&=FVbp!Tz3=MYcy15t25~fkqa5C=# z+Gd!aF$}$F@J)V;-qdfHQjQJM_n%abmFvBOeqir)NBn1EPj$hASK@QDSkEg2rX9zm z1gqZwtx9ius${#e+NR~7P;Rx}VjofdwlV{^$TZ8f%87`{l3rp(4x69lO+$N|rRf+t z(X=-C7n)|8KBW;|WL!C^4t;C1jCaKd!(RVNtU~`~@M^rD?nlp~_#++Dfuq{1nG4jw zEzTbj9Dd-O$hRmH96~msbTsd)OH^KFPRU)#BzF@6*#gq-$W|+wT!37$ zypCOt=2+sxJJ31iw?QH357Rb36?)Eie()M>o?%Uo3wVuw8Iw}N{OXK_{n}AUGEBCu zQfV2@pH z21Fx=e=rY_gbtWwj**dW@6B;Q8q?_F3P*rk)5V%YQrvXAuBU)7?W<4W)R?FSeSIHO zP2<5ziz$WmvE-0(3uj_pnz6L0F1^qg#a)v0#_*c=E_RIJlb|Z>i>bGW9y{NZC7Ced zqVb*dm)B6^Rrxpf7se+b^S2oOQ8oZ~jSCwRB;Sq88ij%$#%h*{{l=KcUQxH)@Rf70 zGR$zL>2e8RkaJ(MX^H+y+xbDUKu3;J37 zgCzm_9Kq(?I(>KHg7i`P?xL+pCw1?|+hcp`9!Xw>J=E`!m5+7kpUH2HIIcemw0I3P zltD|~pX;yxzcWg=1YM)pp*zgui=XSR@JsosyeJRaM+NnJ%3yt}zJt~g~yRMTc9Ak~TX5jURBf3LSp4S&WSDEYn zO!oz0`n9ynu|E~VwTnbU#1-0Iq8mJocC~m8%cO-QMYXkBO4_@khgK>*R4mdClqqsY zYi`LyXH3=Xl)p+^t}!UK$Gp%a11(`^G@ror}k@5gf0DCBEu?rFq|l=2Quwc>KIyT%P*PYBQ=o|GK_{g{tO)ur%5qmVtk+oj`X@jt#?oYKo`2X$CzllF( zH`G&r&7!gDQg9--RxJUSG|JR4G^nPWc{P@Ms}rGri=65pn4OcP?hgMmqc6kUvc!Wl zsXQCwM)Q%z(8u%=VCYt<-I)L2{ zy-8if$Bh-xJ@8c{Hqjt4&g)-#8Szi|sp?)T=AK{eLUu|eln=gAaEuDaZ#DI$rV(xp z6RAwXw|X>HM`V?~qbdkVp^8c;jM=qRKCw4dNtF_hr{1QLRoy51Q)Q~e(2G=;-Zcb!g=O-P8LA z?xDs}S9N#$G3K$)6Tb|f>z)!{4GSu7tGXMem#$Lz>i^6Cp_-__ zl+{l)O;4xvRi){p66#drb%&$NRcX44kclc(*Eq(kI;{ON{DaD&<#|a|?=-)50Kcn)|mw9zp!MbbRlU3)mKNwct(n!T; z@m5DHe*}K8*~%`$AKM?+)!@JF$(3n@uWfay3lU}u&wEaUTeoK1A*Nb=rXxh6Wqte( zqQNpCY9Zk?cZI|eTTMSlZznF8`VT)({B2C|>ZuxKSmgdhHB+C!q?ojk;<@;^))l<_ zcyY@U_5d7e38@>5w>hzjkN76X<&rh{K1WQxJHD^knDGI>YJWN1jNh}@#2>+b+Bzd| z5Tk7Y6NN;fHEi@A!f1&cewk342YK}-E}LqZlpcm(OiHx&t(c7+?s(5zi9PNJWu3uY z+NHIp@G)&iD^}n$+T2Qt@VwUeJQS~J(PUKMO6SpOcbHd?cu#zVBPa3^e!RJPB7lFk zb4MQ}Cfla_ejuh=o(&j9=q+*XPl;pZXokr5bsMBe^NC6~!M5g#ss_##`;Y3Q^@r?B zYbRA{?YVWPvH^C_`bYVfZMz#2GZ))P){>OL*3azk36Yj-P4Q9T)+pY<;LA2QrvEU+ zdRR1UScbJuGO(}2s*!DW{b~6LKulwp7hEj)ZYOIbf@FJO?FJ6VcD3%``YIdN5M4FS z7Safo{%QTiT9m)ts$ieXG+G`vb)Wv*LU2pstIfB0aMWl^g`hI{q%~QTA2`-x6#p2O z!W=w&+gD^k725kxJ)DW{!v^@BbWrhR{^fR|f-wjJL1M;(d z1?DzzY_~6_UGP|jm7kGK5?9MFb}oOWWj!Z@ZL`ET<u|Y^wQzxG=56n z8PhdEaK<9jCSln0VWu)sMtr>Sl{h-8k4YtY6ui^)p9~NDY=Y%ahsB%5Ge7Gkrq$5R zZl8@el^TYXKhVo!+}y!E%e!XI;%#9MHh1UGt{ZE*D1a+HO*&y&$syBFQFPuP#*?D& z83rRIem>pJSR%O*Kf&->x;gTvQ7?}Q-e7E3V1aLqeSibLzJ@c)fpjHnfMKI7zm_pG%05?w8FJuK>f7qdSBpY zd{3PQToXA+w-9m$&(m#zp}+_FeC1VNFWqlsR=+%bCg#Ujx+dY343Tl-p>T)(hvEn~ zRDTmlZ&d3y0EcVV=_xR+e4{=cJX);L`$5sU%XQ13Khkx&Jb2u+9=Z&8PTVA|Q#m2B zhjyFtesHU{1F;0&)Fq+oeEaG~VwwGNbQ5uz+Z$aB!8Gh?E~^d+`{{1O3!5yu(@NKd z`?{0LvYLT9lX7SI2ptRQU-Vfw9VyGXslA45OBZOjpxu+-YXxX|T!3aTx5x+*g z)xmqwroVK30+X?*K@X`(eARizql^~W+&{Z4(S5LF*j zAJ4X^m#Y7m5u&El9Z6@^jcU`BVd_-1G-9xNyt*{FOWjXBCGar)Nj=n;O+Qe(_CG^k zqd#=tNS~%(GORpL-{oCV?=TEvSF4xkuhi{UJM?oZKdWJVS(%5rOh355qMoFClU<_j zquZ1gM8DDjiEHU|x}w;{^bVcpBpba{`*1=uZP#`LYH3m%J?uH%r1{Xlh_2Ec>7GI7 zYna_0ny;SATdW>te!&h>p6 zwUYLAM%TWldpX)GM^IlJE~S3d{bn$~i`s90o;jOZV&|s3r&?`~61!8VEj^|;CA9t> z?w|^-iR0a1sYv4kw>Q)PV?QRPQG1m8lRDh)!QxUI+L~(D zQwv%zS6rYhtq~=sC`F4Ue;}3Xe37}4n(UmBl1EK+{4uqJ8r1wL#+&NhTo(2h`P{yA zd~foXO&^d7DBO zi)CVGpT@xjv|}-=FuSKChCMH}rFj?Ua-zw;joULuX7A0*3HPx56r=`m?1dr^e}fGX zANO8s%a#7!hjBzIn5TPI2)x6vvX^o(Kf?L2zLvetS%%}M- z`(r^}b7hlP)+_r;Za^w-PvA{WgltN_D5k+SOt3uM#TF~l1y$P`B?11l)hXRIq}eK$ zZ|t+!+66MkF^d?Uz%)|+M*iX*b#$_Jv(p>{*hlLYHqYXmuRPW4*>t(|s2$>-DM+-v z=AF&*v)T9uQ`xp5g6mTkT1~=hF~!yi;$C4Ntf1szP>D5L7U+*zj>-2Au~-6u^gatM zP0(MiKg>n{rOF5^yQowpz0^{8C2B}7VuJ-2kp-Gg#06^d#8pm`^7aEQTl2)x&4zUi~l$MuIv zgPdWAJPdos{oC#?SjH-~IfPK{K3h*wa>Y;UZ1I57p4I^I{rqCfT1i{x6HAellY*N+ z$|g@m&A9B_WS+T7{weIXxdMm@N-?bfBmE_&t$l24;-R!>G67V;Fzdn*Y06w9i$$n);d#LyH>LnJL)4 zX1v)K7MH6`v*5eM3r#i3$h<7$ccmjE%E(pznob%ONLm7In2YS1Jk?N&=7v7gzef)R zP0(+`bpF-)>v+QurJ)az>S54rCQOW{>6psQq?D0*j@a}sI1HU^?Pw%G@H5Rz0f=Q7-HzS41FYVz`soQ zOcgZ*)DKhL@6)NvQ2p1fMEjUz+Rinns5JK9rf8LCz0i1BRao`b*q~C+%rl0otVO8d zq-t5tpN0(8U+Kk$DAn<4UHZGKPx15h3UUTxmrf(0P#2wpWR07qGn0S$*X!KLn?tth z{HRh7Kdqh0=yqE>i0WcevZ-(E2&0tVQFp^QfWA=0F&v>^mmM=m>2HPq8NBFIIh*t+ z>5Vhm^d!A3`LcdAy)5pm?yK4@s$I82?HW2xC#CO=bLjfgC;a}^E~FO>9joQie|WTM z{-FuCY>k|@GpxKqFJ%2?*sC+uNexXppz^CBKo?zBr9Y|bUC11U)c(rpt)HUZJEKGQ zNb5|#qBCpf$DPx$v_O}`$)~`$3O)V1eDW3gzv$Ywu-$Y_G1LK4VJTzYR;1r?d%J{il3tRkC@JNNZJ7Y4FpQ z#k}sLzd4invq!i(&I?x!^lbhjzR>e*^PjR)Oq;p|Sk3g1kAz#;rJY^Xrn>(+F4tVI zl6CZ`3n<&gTv<|>-*&cPNA^E$C5<0vjB35bo|-(iwTja+7VP#L{Cf2hKu zzieJ6K0W-CT_cqYd}I44Cwrc@2ZO21^?$)J%!=U~|JRl11J@Tb-u5H#?y zWenWaYp*#PNn)=56n)RIk~z81IM%sHTvqeY@klbHf^x)4ZkIf3c1XMOf7*Y_3bW?g z0l9nXTiX-)&LkHbU(pbI+IkTf5K&N3-mCO?~roxwE2MbB-dq1hjW4&gaM2{ehCqv$jRR!_-kW zH&B=O+PV^QiQRA6134mcEfQE2ywv0LrwqJno{V9=b{QM6S1!Me z0YoIj%6qEz2AAe-U}yE|W?yJTxzdh8i;7>{PQqS!G8+%ZGq+hc!;exutdo?}5`8Tf zmCl$)=GDlch-v0+$W_KRcoi)leb96VLxx{Bd14<2-ZIT0@_Oww94G1-8&s?+k4ecP z%j&uI=SXyQjC}?&Z{{PL4*6YNZ|j3*=M`HQpj{cSEKf1_lqAamEIP5bWe`TjFl%<~ z(Ik#J3a^~7+vJL?NAETbA%w#(8~!5<18*4Rs#U$1wTfyE<7wiO%;^s85vot!S$jNw zuWG3cA|}l&w%#EmMHj7NLX+EW*-0GuA4g~57gh82VNp>O3{(^ZK?w;(8WfNQr5hHu zySux)cj>abySux^*2l!a?tIVh{Rj5?%-P-f&Y8LAz7|9RX3w8_vBogTjLdXHmIRhM z)G$f1D;}b6k=%&hsyin66nay~k%9s*Ysu2O3H@53Y`E7B4MBR%qf*@}O9Lo{vMsP& zGh5ct9ALUH+gpFZR3^JwU1{7a+gf_pI7_y3!BoR>*|}VT0V4aCLDRpHV^V(VTjb<8 zjqaN~A$o(ZPYw#bs{JLO8+bumBcDCtrsjbhG0H=IK|ac3lj^5@IFOY$Bw2DKdR(s01b$hD^q6cek=Rplsdd} zHQ=yLR~`txthFj@0?%tB6$2B8Xk?1>qf*sGMWV-ZAU&@Gl(X_TP`W8xd%Y>oxI`gy?)0^~CmKac9AL&wowG&zSW_8wUAsr) zH{+A$pL+e&mzslW>x4JzpX%YGqE%m1RUS`O3Ca@yB}#D{I?1@qm=Aht3^$ypa~kXh zb7jB&hXGmAs$ZqQTzFKUq}OKu(k<0@r*GBK^s|$R+Ckk1z%I?uU5fmxU8qCN_^Od< zPfmTUsnzm)->OlXOCx`&I@E(6Ta{ncz)r8?l2QxVZp^Y^L0ZFR%c#0UgRl8?=6>G%2Os&?kCuxJ`pQR%9y=J%hK-4HrooQf3i29IeGO#z&W{mUw zpxS7FjXbV=r61)nN|~nn0Z?YBZo@WrQ$e21xUQ{D+WIe@-<<~*D@8slb+sDN(+VHAyu_4czilbN zQqmRfLR@$9L{~Qc_MAMYj@TGUa~vZ*m`=BUqe%QQ_93*)aSLq?47TSJ>qR!O`)Z!g zjRI2UBmWFk-o68=YAS3yj_R#@+B$&VSC!g26LYoXL<w8A(xI*i47TEJK;82zViSNgQ04eiD za1$ETX2+ZdJ#X#9UaK>;oW)(Mq_j-L-!570P9t0@-0Z3+_GO=Nc93-QS{+JqL((_< zdrEI?yx_KJAoHi=yrR_aECAP%Y z!Z1YESuZn}O$S@2v)%>Fx8OJx<8sXJxOY4s8L#tz{4xXxjsaPgC^`knZ}BHfL6hAJ zDW$c?T@>n+%4lZ`^+E9=$8B17!6`>59hP<1PG?M<$F_Aa_9fl5`7rm#W?MJ1enysB zKC%G=g{6!e88FwJ$s0F5+B}oL-1DK)R{-oK>eGe5I$6sTF9jcSkE4BU>~*EmSJYNH zc??_y%CVg}ta!O&2yGRPZkF6XzXlrKfgFK)1>5YpI&U*ESwS$ zZ}=w69G7m~D0=1jK)+h_9vH<&i3dQ*mxeSia!6$ddpDp&cyNNPKnp-B9lP;b7r0KwS+J|$5<;F@GmgLNk@)L)$fsR z04TZAVt`U31?DkzwhY~5b0kPp>IHVD6keTS+acXt=4~4${k|aIijz&rE3iD2&742Z z5+<9Uw!*wt#!Vb$o-K3D&NUUu@H4B7hh>Hkh~b3H?w@DylU*5?qMt7J@Vuv8DO)ha zS0e*%4Gd9TmA!77Y+s^SQ9r~sptx9d+!n3aQ8vsfQd|X;821$Say6Dx#pTR}=AFth zsr}|OWm!V1sYe++3vZgDco)Gkwkh_9G#ji6q<^mduR=a9S$AGh0Z{Vg=pnV5N&l-< zQ3>Qi&}!gjb$p%ER;HO)33x3u!%6|~g!*`q(SlZMay8~tYHa2LbD~<4>SH>qmM5f` zhN`8r@J6}X4p1hmYXHh9)c`zreZI^KhV3%$; zShUB3MS88a44{N)$BawXF49l|idKv(2Nn%<{^5u;Q{M+{!DNVU}A% z3bRblEVpvPOevP$^y|i}7XOrL<2>_&xJ3r7*&Kb$P-prR-ltcY(t_o>{YKUlxURr( zXl$uwK!3;cp1NOGGi0dh18{3#h;ph*)AVf7iN=HVuD&x(v#QSbK4@NE`mHw%5?l14 zXB3p2ySwWNtSz&mD**mE+ly{>Erx&2 zUDkOGnU=xr9FD@K-0TQPKZ*Bgufr~lnc8+9FAVo=`A&EgH0VA<5>C3{@}Wd~w>u%U zmZ3i#BqmU6XWhgG_Qp*>iZyxnHb6hro#;t``&2&aE#p2(_jBkYz`C&kxXwr$G0ap*DY0??TALQrsM?m|#pCZ223A^5-{3;K2O+(KsdDoebX)1i%!NRI?_Ovg?sWNKX zj^TS!_Oxy$oQ(V2a)dNFW~BQXSswP)wVLuc=&=(?EAiWCe@wsWqqOr`vxa@L&E$}P ztSsXd0`vc%;1membsQ5{7v6apn^D=@@fTN6aJfNv82$H zwJjOsX>kK?76ljm$F-Y!H|(u*2Yq_beMbXhf!|tt1oM%P+_shdd)PexddR9tQUKtz>zw)>J$g$LS3lB05Lt)Ap(>9`gT%If3`?#a|8 zarayV+WY8F&XbIqu;&gXGd8H-zKo^!TWJ%sKln(k54byqeKfD;0pGrHfuIFQnHbTE z#?B5I=~OMceJy!$#kRI{6h-l?)>G8^1%oYDsV}nkxxdo3q}RKB>89jEuHlT7IlrAc zCNBDw<0)%n*h9OVogZ}7_KdT|ZZ4~<#=WFyVh_t^BB%P&d21Hjx}87oEP^0co9+mY=gXAp`WbZ z`O2WvR(}D(4^VFi9{UK)3q`enK?x=@0;6~=UItJyB@Y|?S~qfos^7MTa`%>xY02Sb zF3fQwd5iPExsLOFvQ9Y%`P6x6$5;Noq>GMug7i6~?HIv|s2{d)!JW{zR;I8o=(t5H zLIAeGnWCFMJX5;3b=Z5uA@Np#k}3gae$5W4tiG)^QP5jWZ?Osk%6_;lLjJ-zt}DXx z`Ejmz(eU{{on@k|xekX|BuzSQUnx2~XPE7oczl$<^|iPz^rbae{3z(K`H*CqUz_Q^ zWSb?7;gbdiL|4D2M=q`ffNFK?#Me=_B zd&eoM=Uk&>rnEfqogFKM#QwFZq?J*Vtox+Q(2>@O(&s@3&3ftUNgGWcWCkCOalCBq zuy=Z)bU#4xmjVAu^#z%`uC*maUQre6mdjnGM%MwkaRJ`>L9WR|In(9V`B4ss{94*x z`*X$2#Mkyn#n9LY8(w}p$`^2PUIQG~H|2K&Z=2W44^CQdI;WWEtuySDR{|7I8L$r5 z?2w%TC}g=`-DLM-)s9NF>p#`WQk^SKMK9XoWU1h}D;&F2b(v_#P*qpjZhNlkNn(I) zxoSylnDvXiwOKy`=evf@hZ9M?*twc>^I zg)zS*#TjpSQCR6<8jk0@v#&F>XRNRfF(^|bZ4d)0q0Fi?)XlE4oHc~bgjgE&{WE5o z^Yq}UUrk8e^NA;n5M3KU;cBY^ikD{g|Gp7bB|xDl9W_T>1(szMB~F#4qS)csZJANn zXdf`Y%$aA8HXlrXZnK(&sj=3p=G=Ih<&QaZmej&DZJ!A@-!#pe5oOwIG*10){A2hu z@ucCZfeBCo^dpD8)85o}02H1&2B3^r#n*=~d)dgUs$Vu0^t_a_w7j`;LEsV+_W!mXZLNa(EB-LKGj5qG=8uror|buPjW4M28O5&rX?-8PxD zYvi>SE#;=i5Z6gMuo824vNi!JlfhkAw`A$IW@J_JlE>gvrHxC%p`k^^i}PU2+*6B2 z!nbCEd(R?Hr5@@zg$j!Q)_oj(VAg+KXR$32SG#=h^&u-d77|_rK-#@X8-1f&Y?PT^ zC)}AdjmHq@YbF@T%6se&K+5>>*4Gs*kwM9ogvF;|J4!PaorFIxitpQr^vLnrCoa(0G4`sl*$b=m! z$2%>=@9|eV=92?sUbcIa-$(3ibpz*m&XyM1`hW`8EBdzyqn$0xjb29_>)A&<{@83> zU~Vxp`DFmbOW0Ej>fMjqS^??#hu=_=)H9Q?t#Eet0^+9Zk6q0qOGa5|1=*IeqhmHj z6Mv#@BQ+)FVXKrjGGa@Mm97pkyXzRe0maT&%z+6bflg+-y$(4>aOECu4j@7 zkU+}BiljB~dsY+QSIqBzOuA8w@Af4hEu7pHO<9@!pfj7w&dBH(L5oXS)pmmRU;Lg{ zDZM-9Mhlstj9BAd$9x!~bzxbH0}7o(*f%E(cf@l!UI*=U{FxrVEnE1&uC;N=|GbuZ zns{N2yn8LRqGCqZLt0pIW!G@plY#}E6X=HQ^Bt2IDe0K@9LB$tuGa5NTil&iUsgrT zdG}@Z*odXBRK0qx> zTPK-wY$3e^#+{#!Z$HRAlJ&ZM25~mar9HyBL2DXJ zWeCr4NeBr@w?_!i`u?^)6;ZwRS;mMid;B(;#q)t@#gW7VbLIwVN|mN_D}Q&Hw&Mi< z?}GL1M+M+~P&-I)HfvfNTNp8q-x?uAB|T_q7hak3#!VJ^M?Y|dh&;o$IfbHQAuLCe z7!{Ca+aosl`dS}LKwf*zWJ&DMZ^oOFT3{6SB)LGCDIgt0+#|eQZ*-KnU#k-cFE?J`nLa*!HfQDN5~Ypm)Z<6 z;{1}K$_X9WN_gq$cDE8&6xsGf*_eB_ZGv)orn(iP zbff{ZpVE;u#O+efo8#l!uJnld>HMO&6?V}%PVo{jWxrJp_b;=pQYQM2wdN`(c26dh~lK2Eug!zx0gYyOd zqtxSNHTl%d#Q6(`({7S3rI*n+QKYfOjO(=I5Fyh>e>{fB>0q8;g@=TLd5ihbvyh*i zYB&Stbdiyh5&q_>s9nfqY7Hg_JzabkdkACaWZ`GvhEwDODZUzmCiW99f~{nKQdac? z3YhFHDx|KUyiG5pIcSvGbovVV@?b!Y&6N6x*_YT=EA`D&5V?z%gZq(-JN7}lQ9qpP z;fcbrs=V0a%BH3H8?ZkNUUwjlvf__DylO5m?;$6yMwS+v4`lDz* zg-TCM&!IvYme~{g$`m@_=s6V&e|1QK*8AHzPnA|aK-Jvz}N6F$7kp=g2=cH zjwEVTaY#RsU9<_cg}jCxi%Fn#k~P><)KzF9ekRS-yo=zbw^s>>4;kh{B{`94oyVoj zV4a&yqGq$t1h>$VxwSrG#xLIJ<=-1}foP2a#giwtmp4x%zjBmTRWJ#TS;& zt)$3Db+=b9laFb6T6m0c9ng73&qF#bSS%k~mQaI|bK z(tz}r^?)9uj>~!~<1iqZsK5_fwO?C^shNb>(Rn)Ce@6{n2j;Jy6v7nRciTp>+>s4nNHi$+gCys$ts#1_ahzZKK zAR+RUa(9ItjZnh!k(dk0QE9JmXvOiEL->=5%%Cg8CGvmXHZoRTvLqQe0XxyvUGdo1 zRB;I@ncokpsU1D%1hoh(m}mo}}O_Sw@R$OPZ_{)+SR+-EBKA`0$H~nC<&UlTG10fp6A`?8OssMkas+VY=OAjO zVP`4|6Qo}i{SJFxR~dAJ5TZ@>wiCaq6^lm}TRZbQIi)Y!ue**`z}kAu_p27S#%p%h z{O2B!3hKwY&hcs+eVj|_!<)PvM!dfHo1F-ELay2>8$Q9dTGPu8A|U23d81JyO}|s| z=(Wa6(I2s8hM=IE_?@~l0HsCKzi9KqUA>PxLrN2R%3X|dch@PizVdZvk_KLr)P7hR zQip6Ka(^_STF27XfspPq_&Lo@E+ISxGS8XR&;g6KzbQKle`;goO+ZquKB*M+9P`=e zFPIm`$3eI7b%uoiB||r55oY1YrO6#vN=%E@uF$eKefQ0&m4&@g8t-arw^4GY_F(5v z?y~x09ei4CTTg_No3UG^jPTY%XktyD}GtD08N#tVIRe7e`M=-?!od zzu}zb9B(I~#!#}%6M3)ULQffbE~uwfh1uE+wHa{ZAOZRcJP~?Deuc0T&JiexuaRSz z6pfy032TDady_fJUAIsQDa-~gY>M4!U&sk#Htf0$m7Gvh< zKHf4`GxcI5QuDyN`d(7)cmUxJ}R!6{JFh zjnqv}Yi_L(~wJ_q;LQiZrkh|(sY(8OrzNAyclE$ zRFjs|3(L=vUov9zTvQ)sQfeXfBdaRHgSCdK-QnZVv+mKTa6OOlpiFVp)^Q zr;;|ZuI5?EFWFmC^C=fNz0pIdH@KX@7j!Uh*QoU@A3k@HKPZ{z>@YNsrq6di0$-(n zHG4xl8J+6C@G@qmh`ywucO@>$-KXb;L!zAo?u4JT+HwVv@_aK4Y(pk`az2S7y5Fz15iY>vSsfcSAf zshQB*T(kra7jW-zsmP(cY+518$y@^uYL+#vsZ8HG?ISde>}xI%C= zg-g0942$+4-xYEKhf#Y)eqQ_N`-O*lPuD#atZgrDxF>k-*bF)?OftS`-Xesmo)3{*jfx$w5cH!@VUG%qv9JyzjFNuo#KoX9`Uod zDS9Y5RLlmtId15GPCbMn#iK zq|U(Mlrrf$ul-b(G_=Q2eOsN|7Fs)~_O`dy|5M#H+yHLxwJL8neNfejK7#kErgOZY zB;_5-d-w#U8@m*-T?vC;LV=Y@^_A!W#lO-SSiE9s4g$AbK~COC2vJOmnnheJKN09j z-Y@U)+D{?M?)C&!*$nj7^)+h@!|lO!NA=wXaKklym=Xp$quVHwH}~l>*zFLo_Ao^W z8?DX3Cc$@WE<#aAjD}SA4DF%uD2>5%s|`6g>{IpRabnb7qs`ZR2TEzsRp?nP~<)9!xYUDN~^#hU1uX zu-k@k$S;IOFR4?Y5_RuN^yo9X-0Y3msoLJ80h~i~JSv`0qka}RlC)Mm3ZTqUUh1x@ zsCE%so>cWZyKOZ!=N+r{=DOGREecb^ciRc!F3?%)4c3+BKI?e$ehAbOf+4}CnQI~4 z@IxkYT_}=i>?(1h;|QY+aW5+D-Bs^~SbB;iyJJ%Wqa#Q;Pd4*$3?Da-w%ZE;t*(&tO)^oLVxX+V;M9 z0dj}clsz1+u^^LXVxF6)L_%C%}(7jm0&{So?#l z64ZY-LeeaZ)jBZ}j*B$io*G5?W}M)4kl3fY))iISxjfCiul&N&Zfj8G&&7kff|^;2 zGUapY;(E;jLc^@?e@sQ=yDk<9-?XRW2-+LGsJ#}P0;RNGs@V$C^iVl6N*m%YwSc+=rNrK%EywslpVJp(m)GYoR^baulbBlxM{-E4Yb09IbM|UV zY-A0GL<B|VdoDEN)mm8iSOc)K5_7$rAUVh^ETDNFEUut6dep%F)9Gl(+0 zjZ#BeM_7gVO@2ar51meVLH4S1&_+?}N=WoDpifj6BbH`Qy2A{nUx+MajbTCq=CMz( zV@KTJ{pC*XhQiGlT+1MGELLuVqHMSw`fkhw{4K>IESYdcco}z%G=}vK|A};-{D4?O z{)BOnq?9|5!{n9Jhjrm)Jo9-iuM!u*Rf__5TFIh#_Z$KM_lJ*@iCnP z;2+d}w-MS)>$Q%8XVZ&yjfng7PjW5_#aJ&mi$2aQW(~voF@Yz9}rL zj^{&*my^)^tyyQuHbHul2j#F}(M%Hcs4yxZm3~@ydBk<*4&mC4b)a4RO|H6TrC^l# zE~HS9txbRp6-Z=6_)@`T{#B%((33e0r4;6o644)pLi8|fx$p?M61Ps|Rr7){Qk1vw zGZ7-H$r?>kiIycMkdKP?0+iw6N&eMze=%jmbw-5PzhhWqjO3nkN7E?D2XjI5Bgs3> zV#o%`SLst&vvd@{27X&Qma!CBBOOOPfI2VrLpd-Iq?8NuiWVL;P!KbZN$j8?-^mxpqeV3FQxGWaCEVJ=2~hmvWWHA52j4 zq!{RQWj*gEY`rpw5rv3XJSG+*w&E-c6Efz0uoTy=Iyk=$KcGS-5Qu@w zV-arB1!aSOEwxbbe8dgPYlUwcP(o||=_sg2m3kQg$3D~2Gu!dunr8_V0$&pn(MpO} z*ZbE|_N(jwMWCd%#nmtzkL)k%81~;rP6O8-tU3q6*a{>wnPsJrVa2Hd9ek8YZGkbCE`gsWq{GhHpqJy|z8}HvhDb@r5 zl(DK~tq-fst?TTqwaylfF}S|dov30rcDrVXFEufpQ@MHI7)LU#1+v8+j^75Wv?U`{ z@R!zwpu0%ErK<8Ldc0Xu)PPZ&xET+ze~sk$rT7#>bNGJZQ2k{8M)F0id&CX$8jVM5 zd)3~q$@Y?(|2noAF4w(jPghNA_|*DaEC-!wIn4RcOm%nAav|egLOcw*-PwSMhF3e% zKuW|bdw7)>s>%AW2!R=Hxs~x8Yc=cQSK@A(n!^tf+656p&I1D>cfu^W}9SF*rbbwvJp2){W@gp)XEi0BW)+E z4=?UE6x8nR`>oX0FYYCa1{$s1uQ`oP@m*r-^5&Zz-*NjP>UJ?)4vTJ!Y`g*A+On~t z9~t8wUw8y{$XS~ahgofZ8+Q+T*A@}>9M85yP2rG=j4J^OM8CZQOdQcT&NYmb06Jt= zlIJ(GHK!=ekO*KcNr0Z`XVDOFID<-uA_j_j_HkUKY4LsB)fBJry*vrcZ?aJk#|R$&N-Sqbx0~=&z@^T7LM`N`=_-)}!)qc* zYv3=XNb+GMl(&a+8nu`Hj(P^;N%%rLiA5u~&=24aHVraX5CW^I%uPhqf>o@wq%WCn z_IyfOd^;zC`XYQM=L>z=WF7AY^VIN{qA47G+i7ehyvt$4kr2O4q4*uBI<=DU4ZUA- zi8LAWl9xnYh?_^}QnK)~2s&yrfr~7sL5QxVR(dJve02z;lKidc1v7^_GtcGBjLF2H!(`!Nn|h>?Nw#$i=g@fCQ2B#B@mSh=f- zdx_n&U!(&hG$DZemaIj*qP(LZn&wk~Q6*JK`WRYM(S3#wy&>Z;;|YTiZ)JXA+QP1~ z_OgdeR&y?M?hSv*|Br`lMIc!Oojn5eoTxM$1H3hK)okoWa)Wp&-iK1n4JXW^{G(}! z_0-$=%_J3V3Bp8n)6t-3lx2*`OfK@RvLxzp7;m{2(>fCPC&=KNx19_R!o)7bcVr7f;1X7+#z;xUGy8G(Y?^W+A?W z7{q*zh$YQs*+EWn9lM}%4MoQ8FAAh~akv>fX>RVkxb5^M+`D0y80&ajC#zUT_;-g7 zaHk2HTBgCqGGACTFVxTS}|kUpEz;Yat@lh0%zsi#67{E=N7>) z5Z-gofYM1*dBv40$d$ZZMN=p;esKCbDwV%9ZX2yr5E6EgzE5yyvWodYC>lP%z9`hW zWe_#*w6zo#!Jnx62tUfNP!N#~Ku5!Is3ZIj>@Aovf)r{TwpqZ&ad3Ttop22PvT#IW zKhaxQU-5@DUuY{tl7+(4>F+5bkx$$<>JCvs*hSi)=m{78NiF-9_6XhO;*6WIxh#|Qpvh)w+K8Yo6JLRTCA9jhBB-t`q#jr`<3?E=vBs^D6(^18CO8{7)Xw#m7 zMk!eGD%fQOPUwXwQxveaA~!4iDQRdw`8%uzLzG{H39x(R8yi03C&)W1;Dl;9xL^yh zU7nFXhjdUrcFq&>UD?sF%T%fCnqLonP_}jWOM02i$9V!|*Dg2rG?!_sw51Ra%}-fB zbd%f=08THi~`wgcqWJd z_6(uS%kXXb>*NOHG<_y!DN3)~4Q)q%*5x-$!PaXJmY>CK(#98f;lFE^%mZXbnrU-h zk~nHc=ogBQ>a1TctzQ*0{3Ug_(%U(_fohL2^@FNyc#WcYhP7Mf3qEf-#y<^ZTSA#E z*gG?c^bJvAa$$0j`;D)lspw2&dHqgIuVF>`Y}`=&^L!9KK|g&SkFZXMpW{zFrL}~9 zBO5jA{1(xW>Ky>3Sk>g%U2k&tn+%OS*B(u5Q-yQ6^dNYWqmR#l9JZfe2EkHnH%N=% zz1FAb=g5iHF_7CRy(PT988gHzEVE$=CS^Vnx7cW&yPYt`pq~>&%+{Mje~`!Ocz%nh z&$WTWUsCR?9S%y}&5l@8X2bTj)9MGH<*jW}b~CL7!=DKm6sAQIn@=eL9^(pdqD zSaZoSy>3CzEaS(9sa;#tE|5&aKFT^Wwt|pqo*x=Q{AYYOX)ooZ{?hPQluaBe`by*{JpytEU*lWAz zYpi9HqnqC60sh&2vc!h;y0` z;|d@5@x*^ytu zR#0BfUCxQ1<;7-l2IujB%HoM4^$GlwxWn`XsfOHU)BsmPmV5pGlui-&qCCoDs*~$Pr9D%YDHMANozu!LPEXq0do(2JYBUYJ%z=4o3Yf*@a(B+r?`o9HQ4V zki_HkKcu0gentx#L4LwahoC9fnV;%jQ?Id>049is?36qn{UZC}To+> zMl1{HGdJ-p=P6$`^c%leFxS?Fie&sXn9(MtM%93MzzUYk!bP!G@h0Hw+2Qm70)f4Z z=pt%4V^QBptsEo7lkDdD)wNRkxUFSH)Ga*EJQi&SPnmX-uH<{g&S9+K@0u|iFlK@$ zo?(9yMhyMN^AP4)er5%(-g7bL{>OtYmw39TqkT)AdZxvpiF`IEu1P3U|qW43; zaY97btWEH_BD=l{u|u>$8H$P*-4ahiyF?$kqcE4nezaFO4{;1(2|htQ7d4(xAqIn^ ziA1ri_7uq}UQ{wjUL@|w-AUOmK9hEmdO-Z&Y!Gdi_!mI&mn@ukhSegm4E@GhA_=#g zhYrfIy5n%Byh_oE_$)6MIZ;jWOpXD)TpmTEU>?dx5N6^g$zCB_@maE4&07dG*}2*p zVux%?$p_LtnK5?{`L;}x`j2v32A$nZ{UR%v5lhF)iY7c}ev+jD?x%OsofZ&epC&<9 z1}jiMQ~1NLt9Oe6keO;8#}{Q&&!K)n-%$OB-;5ovav_6p`Kq$!47^4)uI2#&=owW~ zO1z__7nD*9%`(T*yR0EM9N0^CpS@@n&E@M^;g?NeyF0VCf8 zyKWdJ+>9vD8`-OnJ^Jxf6KYV`hA+U((gh$kW6|2RO&4%Gw3#(b{3FeQ;+4eln)IAf z(lqs>)C_X7x^R|(!ckqBUPb+^qD*+gc%<9`m?a^KUFNRl?N+T;4{5fXl-EJuS-gd{ zaE_VCE=Kg5j!|c!f=t73PtYhM1yPP!W4PZ$#Qrs;*NnqQ>syOA69l@KIkiN&4w0He zx}??4vXBQfkEYj9X_|!-o-)Yl;$iP;FO`4HB~5o+-r8ia#_>%)6gtcPQ|JNPW1q?T zjF@gKpNn~c3ovHgKL~; zD00L_$01xi%HMtk?m}1CZh$soR$D(-S7Co!M=iwR^DN%k%L!=ngcLdPnDJB0UeX~$ z`}78iK)-atbGk`a0Z=Au)|xhg=v_lK9nB>j*JX6@tM+yQ5lU=Buo_{5tqUn}h?th~ z*tpgrP>Y(#Js1`HUajdErOR%qB$i^MD*|G-lH?8H#yNL5FvttgD;!GtWeDXtM z&V(1VKYG`&4^+D@#)N77)O%V}(X_wEEQMQ zWlJo4+8c18j!r)XIi<*8tbrAZl9*!nX28-h2MKtKS)Wm5cn5nC+J<<*L1I2O^>cpV zB5EqQFYxP&5AleE8`+EaB_u(LgnyF!H)fBZhN=WwS+1aS$484EGuHtAN+)N)yq6jV zFW2s)1tJ#9+iCxhUxkb5chRZrcE(x^hRSBru{^+|G6&~C2w8vdhni&U4#KS(4~~-* zy6_)2i##v8h5Mb-n!@3A(mZ0e@$=}^U_1XFGiZF2XbgL_$8X6I?km$zG9F!^9YPUc z6tXu|7}hNuN-M>!W{;rH!Ed8{V0aK#;5IPM5&IAmm@ZORQy2?MUR%ADJ&Uq>;d}N! z>fUT4$4NVpLg9wef5&X(er2+QExcY<|M*D3T@K&lw;0R2XUZpT#H%&sq~ipYteCum z2ojJehe-`AF?BULio&L;$ggpk^hU}~_ESEW6lem;-9$D!X3Q%_;}$<291y_8c)&_%SC}0Dyc%0 z3;jjT6>%EV0k3pVrG}a$S`R2sB1PBdzoQQkk50DI2gKm$vy5Bf)?h7bu4K&kaE@7$ z=JAU=QsSczL(P>Z}) zcEfRUzVv3re}GqdbJ1>Unsnp*_cVX$#-zh^Z|TA4a}0m!xnLbrF8ww>oc&IE2k=o{(r1B%N4Iimohn5f$loY5BF<+V0P)UL+ zhgA%an-sf?4pPVp^ZX=gkph%-j8?A*i~3A2SBwqTGp@-`0~ENt#p4(Ap?ruA1plC4 zsK6kRx=*5Is4Y4p=RfotT`1ibJ576(7=z2xR-(t^t2ECby#$iRRR54@)6A$SBk9!} zi+sr|)fMySQf#W9iQlPi6*mg75UDDHjf_-f|M&>ND2?*?#aO3+Xn( zQ4#WiNzYNEXBgkmwqU@ zaGcpFs?|*cp;uV;5Ga@{7GG2m_N5sOzKS1XT2w!g5NrHgevp`9j4q^;hz4nz6Xr@$y}PlYw`SFIP>Mr2OQPU=P!%5BH* zLASeHNCRf0Qw<)6edN&9mExz{d&~5MM4P_gBC*KI%(z5qwlpMWk~7Q`qoOEAzF_sFh6pLgkmKJc>626h%=b4L*sj_hyyi^rn=wVp-J z#!PM5)Vv8>>z35M!YN$UrLXa=&eVc1!bhJ3b9A{&oQq0W*-4(Cq4m6Xvw|N4{NctzfFH}w(;GiUhdU*^{mcpO{ zcdIe%F5Pz4S8%A}BD)*1OgxrT4jacU<$Q!Q>2z)%;s}w=3q=h>XYdxI3n6Rx<(Q@Q zNBFz2cgpVw7UMyMI$;2zC*!hELJCNnFY+aSkE|5Ur0N1+ir&y2WAKv8OrRfyJb`mu zH-uRL_frJ3Mk9zKCTkaR6=xS4jJiX6!STa*5q@yiWBpL)xOKQZNFnzbzOfz<+!3th z$N6Q%LxmjvZ?b2`F+mC?F)>;2nR+3zSeQVs4}2!P$V?f76F*=BH@T!=xfit6bQkK6 zoXL)=fj#tZ}e?*EwmWGwPEOG!xv%h(ar(RKUSkEtKZzHmBedkdCwOX-_3 zHgPX9wkOo{l9;z5vw08LrGbz5&pGxnXweYvUtlaDg5&C^lq_DlroZ5(I+e7Mo+~wzSJ7_?JSk?z|8aEIaZ#*q z8z%%56BHB>r6iOFX^`&jhJ{_Wdv|tsc6MqzNkPRt_A!qgn8)rqb{@OiWAo1M{bxS> zH5d2p&U~M^pX*`^glszRC+i$1hcBRx4cv|7ON=JUaaIi2;;3*2s zGz;#E_9xsD3dFjIe9>sYDIiYtKoaP23&@nZj%|}RNIzN)gK4rsTt_F8OQAQ6Owm{g zpBX2D_|I7WqFby&_BgRuM-Tg+Sk&6bSxIC`=WsiK#EO-?Xkcz(0DlzlGvhq}fTS|v zs(>!FkH`^vNSOgK!tb(?9=F8Jvj4`CBx>*vt6`Z7Xu$4MPfMeq8roB72k?;oLAspZ z#&{vU!92~pA^qIp%GxgTY4u<$WwmveI3+Sw#cXb>x>^f}`;{SR~_C5X|7 zDq4b>0<@;~5sQS*D$i#ZBX9CAar}^D>CK#BWNm^o_Y|@^T*6y~bo)2*)riRBme2}G z7)uh@AbCWNRRFvL)sfrOhvlOvU22xNt7Dzohc}wKU3HEjpzTr7$ouG0l2Ue)B~x1Ghp=n$jp<}g0bUkPi&dh$NKSsY2Ztoz1Y*YRG5F`THswXexFw0Bx=a~1uT=4b6_#!U@S z#$_H+zsu9JR;W2?f3XMDw((UQs!AEQo|~`A^e6Gklsi1`@}DTj5Gt8C;SwVlj|q@d zZHFw)U`6{6vq^NF;%hp}E$zrRO`>n5Rv1g$Khn|-8=KzHV+<}ee=y?otg;bI58d9p zewLHYA@w)wo>mpt$KI#e7q*#mP*Xia%iX5#a{tLEsrHO*7oJo8v>N6&D6=XLrS%MhQJedLDKe}~ zb6_zI4zU;6k$UIQ2gE)>{0sv(UR&q>i}zSlFqSN=Ra2~nd23ZykPK3L|0vmkwj*=L zh~nE{_1O{~4_|s-(tdUL_t>_FQ(d|zGvcWC-@8~0I>-Z|A&+Jc_slG$`}w`*=IL)`f*mBg&EyojC5 zN-!S`oy_`Rn(eoZGt2Pa-IM!3pD;$oS86X4DopJfOf20~<1POvOQ<~vC}b<@i+OLr z$qoN9=YVsY3Mo|ZYxDk=BzZ%tf9*l}VbWmPbNTId=e#>mIC*K>M`&fothga)6ZL$k zpW+q$sPBFF0&}n19K@gfa?~ai;9K@?+^ti;c{+GN;GPjW?ej=UHbdA?0UC|QC z7?MqFRgmAxB&4C{3$o|!qiacE3c0+jA6!S-mUluvgX)krL%xZI#Xgm9px+PmfH=$z zzBd&aY^htKLdNZLTm-)sj3rokTs#W(6pNcLf_s2*tuEqnpo>()<2qoSuY>VF(IgeZI z7AJqoFLGR{xF!S$R&D_b5V7!4+eXk`L~q|I+9mo$KEllqZ=l?vZv$p_{N4Tw*g(D9 z^i498cA;j4WFh@PX{D6Gc%J)4dX;%D&0eNtPm8@ETfqql9V2_lmHKS}ZTP8f(cm+| zE5||Tfrub7tdkVLrv-@}N|{t}kIEEH7iwv3ob93jdMrI$w3RWkT`ewTd~C9aw=;Lw zWC1O#wWY}tdp44LTQZ+RO8qJg;MT_;lj?abp(CV=`MrK?WK)DQ+##!$p3xh7b%5P^z33UP+<`+(o;7`^v+9qKUo7_HDc!lHKslcfPt$NBOs$x(up%Voca z?`Z}{AWh)j;yMbx@J!sL>?Qmno3`35wJZ~HBkOadObg-DswB*%FmN!CWFOawne7ug#H8zqAr zP&iFM;${og?1{Vy!rRn8c{@dgZ7%#;(fY|+DR}V?kOP)v&HvwZG|rYRq9j` z6F437m{-;QS*wL=EH`q&B2}Uc5B9;Q;TBR8h5%k4d){?+_%)Y;p<&*JP1%YK2mnD&~Rk zhwN3z2hl=M=DS~91)AMv0f*&%jsw!OP#lpoaS{3~A7qY|3jl!mMc&IVU|p1NW;wD~ z$v1VZLwP9uD{>cbVEl_*;$3I%Lk=?gS#y!a9WiVY z($)G0XBxt)Kgan8$*JvDx$acJPms1^k2X&zQ-A8u-7EX~OAztNu0m&k|I7A#qG0R@{+Jq$@h3)vBk4X`TdwExIjR`%6tzAV=)J}V9^?M zi{m_CHA*8?>X9d)nC7J;i}UC{ItQK$V}#biILmmd9Yvut_iDOY&ahBTQr#zZj{11T z1DOrNgUk5^>Ww~61@@{BQ}Tt2Rj!Wn#T%4F>Yr>8wpez# zV^5b*^apiI=R$4=ZMWqVV+wtZ`78NPhSVI}V#Uldnd+vq+>Ce2zp!2yXB6hJO@^Gz zC7e?Ip5$n*gYJ8DFL#-4$}EKUmv)oS3x2m|Xi9<5Q$0wiOi-O7R3h;z+58T-zDY!j z!nEE%t{ZJqPa^#=ePVYLIiLQq3uzu?40W!p-N7_lo|NlZ`R4x$Hn4`xqcS(J|25Sn zK^&>EIqD48%Rri?;(6*-KI3@zb+jo(g2h@V#|5I<|4U|(o>kh)Iw;10E|FJ&vobY}0_<~T-h&*R!+rhWID@~O--T~`VwvXIVenYQdY%csP{94qtF zs4JY)rhPO2;v&W#A7`G-kUFJA5Tje=I4A_PgiDN+tUMrJs&J^W0|+6@oOIn^+X2GvWjYSpkgSKl~E8v&p`eYswNBJ7{9{EPy zH}g0)m%hr)hEZTlTn7R6A<1|*mbcx$dp}g7K)3&^p@>BZ*X(#E5>d4gti$JfxNao zv*oyaGa0PAMfk?ftC$bDc5E&Lpib)V%%jjPx_ffDB9t*d>ZYQBa68-y`>^@mF|dK# z;Iafh&#!YhiF_BXB~*L>rtFBcsx3^kNxHo~j%$|9BsVb1W&M;k@(0$zJL;`bp_j+H$f`s;Boi- zN3u(-{DMKympv+T3Aml(mLvc_az{lSll$@lX08E$@de(&au>mKR|J|QI^%F$@g0B( zBD+hsN+seb`ZggT-pm-_(11v0Aw3OP!*XchBCscR`f@PxBt_nE}=p2qI@}>WC&prW;m)sF< zWv>uC5G8Rk**8QdIX7s##g$yNT`1nob8Gqx1oJl5#saJPHRS=4@%)bkd`YK3mvKV! zU09q{FHIEQkJ={f6y2CPD6=B=-TY)4faxj+_egyljzWDh;ynh>mb*)01UdXIf(*fZ zK?plT*ew{O%@R2ar?Fq6DB%SRlll?2(IdZ4G4?IS&aQ6Qy_J_G>=T3Xy^&YNXXO`zb^}bP z(c2RkhEiN5(k$o@p<=CQAc(A1{417lj=&sVKUVN+g|d z+Q=QUtpB+IL!;mW;)(2w_;Ox22gYU0WbRBnpNIy(hlUP_%>fLvxyYiM>%nXWZp9slQOpb0?}-wOY8_)s+oXd3mbORZQMp)x45gKBlVA zT`O=_zD_d>{!lJUuoh-2b0d<4`;;>SABkZ6s#mJm1{b;X07C55$emJmOhc%Ip$kN< zta!sUF35Ush+?SN>-A=ekVDl^Y7OB!>iX+KJh2Q^kk=G5&oJW@+FKdn#7!IS;7rs zRhlm`!q_7Sukk7D!>0C@(;R{EQhh1c(KxHJpF3daDGug&=r8BA@%HIcQ=jn5bz9@} z`7gDf!U4g2?dCvdVTQo?n5la1a15wZg%K(ge2VZA<7y7kkk!b=snC=y*J1# z*6v=HmhtSyp5i(+$EurIagZ~=3n;SXPU`H*KF>X1IXXwlt1*9zJ;Qrq_6z%quQg2x z^bpha zG3ZLtv??AHx5@$I%Z4)ZkB<{hmAV^K7c~ z3uOZLsNDmlR6r0}*(z=nU4?ng6S*(pg)RAvH}IoY4dp80Ogh-AL}s@=uK$JPkSA9~ zBF&VfVkI)AqbX-CBBJ)Dl8}Y8YjGP9JtHCP2sWL0GoS=Bvo)T7VJ2>=^Hb~`zsl}D zo+l)HNbqRDD2j(JlU8!;6me~b7&L{t{TZcRF-U&eI!Ya)wx^D+yaIdE?29|# zK)Pp64@_lvr(O|=pYtPBDx6?<4;wEgobIqx z$syqac_k%}b3}f)ql$i8{vS1&d=-kN`L+x|INh&45K_|LSBjx?jGM&;iYd&0b09?+ zYbZ5Yv6X!{u1B$kb2)4?9Krn&kP2_-3#RGe#e#Rv|01JAJM8WvpMf5NmEF=Wf*-OY z^zoc9FophzUIt=}W#kI*22znrO!`7tT@fMk5#BG{Bol~IvQ4sQqTxBEvR~r7*i~RO;2SmcvDylIfUQS!_A7L+0@vAd(#0TQ1Wd=+x$j};GzPP1x(6mcxI3s_I=6|a;`6u)lL zNkqV$+7!tJ;8gi$=?LI+;X)}%Qj&F2dRlURPNwv=Q#i1%wlWBoJK&*SCDEV#KcIXY%MTF zQCAQH7!_!iJ@7zLk-SHepy0-wl59`_q1%ac>An7WQi0-(=UnMq#YgAsvZspccDLp6 za4Vq_4{zj(1S>E<#!+D?Hl|~Qa2@)d#D~$= z%nHC04N4vY^vI7GYv2R&BII95KJwi^U-B5aPpA|iy9kvUWVzjKup606u<|g{$93f& zQ%_@11&ykklof)9s?OFO!d6vf!$RR5)u`%nQL^%2=`qoA<&ymQVs~Xu#!4{-f0%47 zzK(B=fs$L58q@;3@NxLE%5`EWZhD;T& z*W8&Q1^m??rtSuGYEP$+lD(>KyF1b-Wf!4h#BOs!xIFR0TgD}q%TjC#%Yh2|LjsC%#mVGfO} z*IZaF<~N@`&k9n`!hpSV1uHgRE0IZAk9&!n$sJtRGlrI)fSbe^;+pB zRbrc4-Yeyu_WBH&@-%rUX_InmhimlTs!ZyaU{Ecg@AiGDu3{S91JwcSyzv=C;?-i? z59-wd6T!;Q;>nY`IQe(q_GO!J-4#&T>uPAwjw~+Ve zUBpF{cNt`yPK`>ufwO3&=pFbw`j23~Dw!$sy`%DCvpq&CKXX0Cr>VB_=i9zlk%dnP zR-OPZa4uu(Nr8;%*p{|Alxm_IuD-PfJ3*E-gkZZVXw?Pmua3@=<5&{akhcRXqAkuS z#)R~vi6^ik#@*<(*n3u1FjeWy9`e1awBc5G*y0a(Z^ox6w+QQO-z%wNB4-3&Cwa$7 zM?J|0=>2Fq;b?LhZSUCAatGy5*VYfALfVolCaR_zN>-z;3|ihGTFNA+!)PI^I&mkO z!tRJ3L|=231UF&pxX*phV|)0;?!U2%g2UsJ@Kn(*+jqDaAQE%1RWf__dUy|wK_3Ml zrpJ&A;ZF?bmKMa1@ufZw31J?rvPEXFu;K?uJi9#i5)#4jOs65uoZpEnktXibsDlW| zdm3DcjuTAqJ%P3gGu*$U|B4ojPeA_#ylvlM(Gp@!3B3inv&SpKnZq=Zg2vj@eo(QR zjWpj?{Kbi?yQH|uxmVc-+i*?Ae=8pIqH=dBe(~OHZlJ19ju$kV}%awr^1n8L0D_%){PVjrp6%G<>DwL9et z#j*;ce82cZ(L?zuATsABG!fX7CW2;5CM0Zwf+eD;#ZbL;#w;=vCDr=wfCgj*?r#)6 zvYQj8DF#3hk;*hr{x_jg1N~%9lyM{nJB+gZ(m>K&*P-Qt&wg<{8Dkku5Sv?GeGQV+zRE5l~ah9G&jAd`6ZxOWMh|C9(W!K242xHDe znHtH7cadE~W<|W0{X`;WVqh*(?{fj1k0cT*Hc0sRCiyNTaRdk{kphC1&k+{A57?<( zNO6+5C>vTC5{5FcVUgq*{;7JcbPT?yOesyo7Zse7%5g>3I_XY4Wlpg48a^VfLKcK= zi})mK#TL#~%1&a-d@h0$u?ly4kd2KQA1Mz&-;XGkzeAT2Dg?&qS>ktE68Qj7qxshA z3~bjdZAh1l(u7s#Ny^mc%RD8E)q4t}C6Cp-EIa8K)$=*A(qL78T)mW`njA4px<)lA zXtS(9`QH1BOrUIXA1S+npBNtn4&w7h*vUs>#27{~1+%985GNbHkp<#)21&~Sz)An5 z{w1K)gH^A9)B16xyCsu#C-WCc5_Dc!E|My(Aw?sBwc)V~CD%27MMO$tHLHVmNY84< zcz>7PR%g09$}&{K@zLN|RefmMH$_z+OLULc8_YADjwGb*3S_GolmM}i?4K& zN(6wFWp}XnjQp0;kvZLfRyTLZ4R$pbNo(}i>!^(z^z-U(*5G=iF|~ZEzOrdU zVVFL!WkS{^eQ;~n98cZ#wvlm@bXM(k;rn$9D60c=^^Md%FP1)>zT9=L?kDrc=pMa{ zvyMo~bmGq=eA{|NM;P6@8+AjJ&${gmKBQ3H;zqnNO($zQSL3atwD^}D*Lk*z3dq`D zq}^G&wVT@~%yHB_C%4AgX*N+dg>TUgQr`v6(NX9tygIaZn5!pCwEwXG9^IuI%bR5V zOM6m4>{)0)@n?pEc3zVQMWQ7Zds~OJRV}L;u4~g15bUq{e5+t+CUjus!>nsAUqA!wp5qQORAH! z@Dv}_L3(=ZQ`HKFIedY79;-5Fq`pK?jrK+Fe)^bIq@9?butrAh)tG=rWXk$y;mFs9P3dShc((h#MQFbxzq>NMY znWti}DvMa3!Y!&SPHy0^Y6EwdSA|N;ubRwMYweum3cH{ON}y$&Z;*m!x%YLoyw`q8KtSXlKCt@4R2@d%3Oxi*h`YXPNz~i8Gc&23j{RWS zHlM@oSSRbpV#91p)l}SylUpLeRGioOzE}r$b;c#EfY+A%6tn01$GkyL^M8je$M{0K zz=_xak(F0A-X_kPl7};Zj4@(dBH3v31y{+^2qJUk!PGePB zTDyxa$O^GvUJ^nVcc#BYLI8(kE5rrBV(j2y$)qqd;wu>lv_Z(~p;8CEp*)Alz%mj7&y(0Teu9@t%4@UWJ(6t|ba;n!bkT8mximXh z2(ObaN&g4#l72}Ffs}H10U8bQ*4n3m7i7|kXIG0Q|y$t<*ZlyEnk#A zN^wShG-;Y*m;A+SZ^dQEBXqT5BNQ9puGkGN^vYH|g#Ma52&XCh$9#qVRA_B)!QWwP zLPY_a$vsdzoYWc#b;3CfTcN!$vt~SW9~PD;L$6?A;Ya8#jAg%rzQK4}G4uuIB~la) zFg80;5d`C*BNWc?`~Zp~0p8}dNKp>2p8Q6!1U@!4OYs|iF~S|LK|Bc+JEV{tC|`_^ zX*ni8js4ewf1By>Y0Y+Q5`q(UEu zyoZeF1^eYhhF5l(e6K1qbx^)iWs|T_KB&}3*UFD6Q$zlde^HYB zQ=vTNr0JDV2)=5v0pj6)qyHlku7BI^RwQA1LM0eI(LNuH(l@t+fGpkT`sLt|&QSFU z{GjtMbCo-4?-g8=Pt#&qTZ!w0Id8!)nuEmX#aWXPHCJAv85c5AZcyL%&z7H8M^CSk zFIBB_70BNz-;Opwh067|RnSs=4Z+F-n4rBxcEGf&`JU{nX-<6w7-u|GwFG1vt4eo+ zD#NFOY~os-bpkZ$@26UWz4`_5AHdW4(NRmmuex0!PI3=jnt!3ZM4L5DDo@mGcNNQ* zs&|i8$=|CXsRaE^9r5vCh4EPAZBS}#2+jce44N4L_(uQ7GzfgIJK`#lmunY|R?EA! zBM22gjhaxg$L~@8?u@AVPRg{HYM{nH%;)Q@YA%??^+5SSb4bJc!XKvFO|?07rj5;4 z(?lj?DE#^_RztJcZ`9|!y^wFVa^%C4|V~c z7+dP*iC$3B%?)+At*1=C>UTDVn=FmaH8fLR6Hrbz**D)Pyk-2m)h}m`v6}>>E;VMg z{hc5*%xHfW?O^bw^aUFYx2U`P&KvFMvZ;3s)y#^CHw~lM>qZ_ll=DP{A8fvGJi$s7 zm_$Bk8sE6EwaoZe(}#w`#@3eT8hhikRV6TwQpB%YjP z*wOO2<(7d=3T&t|B(`N#uQ6D)Q_8mMv&m4wd)-M2l3lGMx_VQ^IuGi>gfi`R+ECO~ zT`GeYEY>|?`S}s&H`uE^cjzLyRg?B=@9=|1?$hoQvWc`Ty_i_(Rdc0P?T7S?_J$U! zehQgg|5CSyBCRGk-hq@=Yp+nn1sAm|X>Hj_+GcuFYO8hvgB>r`EM~?>jnmF%Jq~7Q zw{ti%`m_M|q~~UB7{7YbPEEhy#HjTew&*UAmbFDftdOWs8Kpf<`?%xZ=GWS7)C={1 zwvD#0>XO!tzM-_9U^-ARLL+77XE8ONtnfKT^&7Tpe1}@kej8<_@#eM%w`yv5f6g#z z?D^L`|I~yEt0!$!VZL4NCgVfxiAu;3XzG}i*OgVEq|K_kK;@`ERZ7XP`AGJ^Z~U3poXyPC!s(R4+f#@ScLR!`;vl{Zu$cwQw; z6^*wt|EMaMUyv26e8GP>r&W1HAd1gWdI|j_A1VDrw!!(zHqkS`CCW|W-=0g9za$Qm zHmC&BicxEnYh)2t!^+hlF&@Jspdb>cs^IT$idD@JRMq~Y{3>`>nWcOuN2VLH_@3C6Mjsb8pptAiqA(n;8_4Xi;kyAX7~-@r=;PY3-C+QE0g|I60r=U zR^zY1JnFvRZFpm?-(Gnp4EcQk+J={u$ACU&pzx7xNg!aJV~w@wF)0E|7Kl? zy@ehVD%OgTb*$!Y2KT%}_Wv&ca+2wbl=@DT*%ykwI9} z(t_nc_Zz~oT*ZXyM_9HZvz&#^Qjm+5qhAzIZY%m&p-I1n9#ZrtJD^>PjWHk50>yy{ z9vZH=IrB9dq4@2Wh)#s3dA6a6aPg$2XdEncJVyMz+J=FKz|RPk(MV+Td9({P)^9`A zsIFRy%20Lrc$9&nMgC|tD$RL~#-Oxx4mugFPFjNuqj|Bd$O|+f!X3GS2Fzk2htX+% zyO9m(7|$C>5Axe(0-{4cIHn?68ATqbD(cISTPlxgE98puQ5lMy zRt^;wA-k15*&mQaN>ch5M5&BRibPn-DKRcc1O6b~j1=HUXF4FsxWz9FiN-TL8xR-V zY0^SuBzADrWq24{U|R+M{-4`C5{vdXCm^}n^Y!*fmKLbmjHGF2l+8qvG|vhSA+t1# za_o>vn!L1H_@~Avkpw?gUz`0FzO4qrAo!S?9JCrfq$Zk zqblIV%3_;`un9*9R^Gz$n_j|)jW%`L;RA+0sv6+EhN{v_@OFcJ!CiQjesMMv?$&3f zZiXd#+eALh)ahnNz!Y70mz6`ty7i)8UF2fC)T^{G)Y>kuiH8@v|jl2LSs@~b~ z;F&5pp@L$+n&!b-7MHqKIL&;#Y6P5Q7MDDQgU$X0C9scaZPo>NqA5G|Zz9e4a>9_} zt#Qum6vbP^?a+OS8wO$E9mT);CEh<2|LJ}bs}|37j?Om}ueGfscPOrCjMl#t{TgCT z37&y(Y9;k*t2Q^f_S)Cv)~xEWsa;V1tGls&QW23(($JY3($(AeUwUCzRkJuLsPpfZ zhcSJf#iaRRzdOP9KZBwz8cMr&z4<$Jnp?TKjvh84*7TCO!|su}gmaVdPQS)~NKj^x z*tO+d_mkSo4QmM(y{MX$t}P9NDQkY++0ar_ zf7o)f)mY89RFe*uJu%;G`>$}wOecHg_?!Nrc%_drMRr6aelXTjV`tAdy3s^ocE$`w zX3%7#ka^o@hT$-~!9B>(#&vRXGbHfG*k99ggv4lHH&wiuU?o>dY35p-+BVb|nLFFh zR@*QT7`{B;#D#RQ)05fkZofBnz4i>YdpeLSO0Ua%TkD>ce@pK9hAq zey{s-Vv| z3O49DtT)-y^sel^Y4*DH98JPUT`)IhHdE)zD-6A_%jRnWziHPCG(Howfx>O>?wSjt z^-fM&Eil*qil#uany4a1Mj=@FSYFa(Yj9&vsePf}!hTq}PM^V9R~oMSn_HIuKquvm z$hOr@;cZHd)L!NnC*0Sj^Y0Kp-UEWYp;t6}g$Dy&H6fxtpHZ3?@hf*1^+`bQG){9) z+F^fLt&)u+s$l#tIYpTY`8Kxe$MBBUmg+X}d6jOuJb`mbhxUu0H~)}UC-ljR(mDv& zq)yd*61gRe)QlJPMI-7Sact-b^?AT6aI88AIN|N1UN7}pLM0ES*V(J;G#*tBsqQyvEB_LqwKzo~qA(4)$9W?p21(SKf02Fpg3)VGH_8 z8RxJAU5;wlSJ+DyJp!)pEm7J%QWUl*M74z>%myA={L97_M-_BOd_fPMIb>LhNQ9XN$-m0 z^;MU9N3?t>o7dA$iY>g~eY=gB^P{_|eL?#Du6yJ|N#{C$PrZ9f+h?j=n#9WM+SBkd~l%YzQb(|h9>3bEW z`*gcs>8b7v8C#cJ@gPT64vxG`X+GEM2rN_pYSJSH_lx8O5p*sl#5>>=ViP4360oYu-sPhDEW92|+AbnZM4U2|h zEVxK~4$|y*W;&}n?Uv~rduHNUlOOwfOrCKw=VbU?B1_s~=1!vzKiO})p;-_)ty}*| zXqn8?Z4gzAB@$#Nvl(#V^-f){Q3)dSK3Kq@WU+!^kyCRN`=w%gQqEeVUqC!@?%Xl{PWrNM!7(m z`oZWVv`buVun57JApJIxZ#bmy7CX#buD1~%^INHF2G&he>oCb@S6Evj%^TaMEtU9Px#O zi+T+(b9RYtH?SjYvF^1*J9B|{SZYmV3tp8DPeZgy*}tx!<~1l8+p5WsM~rx?-XdR2 zsMtWnXk6*2fT~N3>wvfA4aPJ{W3i3lk>qS%nIT46naR}Om9CgmtluNO9RE|dSmqKf z*0sy@VZFKl*)JkX+79&b-J?yHPxYLysh0~}B^nk~JGNOJ1gS?nQSF0n6ZfxF5GuF^ z4yf90xDPg#9W!);D~i$#_Hw&ivtBQ!W!CCT<;&-!=-$dN$G_2qL1UwtIxna(beC2P z@q@N%&OwWPw`o=>JU!>CH41lEk$QpR=GZ1xp5la^z4Eq#=uX5p!wtlBG<>`&z|g3; zT9&MTt$0(Y)3?EHxv<_E&d%u9?S$nifX){_7(ZS68nKHS(DuQfL$_#N!dC+S(VRit zd^c-mBley>>PGmTt3W*l$sKD_zC~Q@7|IfaNM^yqktqZ#7a~=atMo%yP3b;;CKgwS z>h59DIVW^F%qRVZ&IR*M{!6<7D~tEgHe+a1ujVb*5pqk@iJ?T6bTbC}TvVUM+^23* zSD`0ewW`0+6Jrl3x1sHJOY!&UOsgq)67oMHtC8=O5&8g?uH?9Gjp~nr+qyiJIA^N% zhbkofwiZ=+C2!NZsEXohh%9N3$m5z0<&Tg%>TjwE0YB8YmCuP>>}2J@)J>{WO31~i zic$uTIiL)|yY0Gh6!)>3g#E?_30A&9FIODa4QS7lgzKhjFBZ(vp4LJ+0a~7RR(go$ zskSlcr)IG>Dz;zKuDKC;R()1;B4n(3zGefFCEctk@cBozQGI#p7FDZyz{RXQp|%7xddB?bLbtc_9LN~+XlFCJUaMBuOoaW6KJ}%bG8u0-; zr4A+H&jQp0#n33+yPCAnz48s=#gA8Oi&zVu)=tY6F4$jpJ=1P}QiD^G1&r zSxu{>1pP=0J*0Q;J5p;v=iKi025&|mmon2mvv(bJhV$?4@ARpoc6M`_cddVSRdel$ z)5o>TR7(7M*HjJ}|%PE%AkzrX%xZp}PHV_Zhq0KbWsGQa;y^MiQT{?V<` zQRKOqq#Gf+zANnq0yKRE6oGep&o`=_hhL97?WOaNu0I&(NA2j!V$Zk!-5JkYNU-ve zu$-U_3}jUy^OrTalx5BX8;c8}fpblRxrzP1n?Gbs?kBaTB_Eu-jU>3CfV9O_lrm zmyrrf%jW)WlNBb;Eo|SAQ`onjd?Woy?>EZl<1(|G;J(dQqgq5zQeO)YqdqN5R##aux1Bst z8r=6cWnBTQ&#U8HPIxbqdMn+zr<=Al=|Fcc9gef_(lI(B>$*lWHwNRKi&)C z_1@W*nOv>MICCTK$Hae3Oab7y)RZn-VDr^r68|8oXqNmUC}RP$Dqi;mP@j~v_G)Mk z3yONS(9dMIbbn>6Nc-NM%bcIIuB(yN5IeJT5qoxIL8mjPBN(u3=dKN?v5epy@lG-O z^6z+zGOZS7PrPJ|5d}IfHnMmB<{ntxXrXF<}uPLNv=>ux#08y%TqiQ_v5GtHj_nE}P-Afdl^oJlUq@~|~t z5;G>AGthx4jtdQek|CS_bl;@Qi7M8D|0A-G{6YD@-9wy9#r$qxt}&m~)y*r+5_LK7 ztGB8cR^KO!nkM}X#8n8_2k=-+0zEt%~-%kWT&xqsJhl#Oz}uU`cwJI>c$ z0T&UeOrV_Tg-{1WkpyLaDKg54lx^|DB2(uoK#+U3(-ZiZ*=^BC+Earqu9BgI+h&sF zON^h1BdraWnnI-yXGx7kvT*-K11{U>m2J?1L+;MHmEbbxJ9-yrwBtO@J^3@6FB%TS zAgVwW(+L$&@vtnhlP4=JJZH^2kq8BS0?xk=ygi_=?%K2_)0f_ zofrOx(>tv_ju0w5Y@w~YN{A8fG5ij?lAz2hlvBLP9HyZXDbfctso7no8jV$Ymhr5{ zDcQ-GuStyK8XjvVM6ET{sXv7t(y!Fm1ntpN)c5@6>E5XOr#EUpsT17-w2Rb%PVY4} zs-aP!mmL0{Zd*OGE}5xM?seELF4 z!`JABL4K1V6j?xRi3zHkzn7Hdn=tS1_Q{?P2O22;u7CC?QX|G!_BGQZ?CA>FesOAP))LPexNyqiyxPw>#}}Qd@6RY-_`KnB%C5n6P1EDU7i?`# zk1m@3wdHlFaDF0bLr}@Q1?>xbBL}*Px!#?*!&IBe=Dx+W&*RH`J}`#tZ}(KN7g<|% zZQ>E#iIyq9A19`T>Ail06oYkeNY zo_D;B7TPh-fgBK&HGojw`_AegORe?1+P8}KXtKWN9iw-AN%s|&&i+>SWbPbmtIiby zYodyqA{{{)o+Q8I*TS%-c?H)8>CKmNx)!W#osdpmU`xtMJ~e-Kn=pRdyrTC1qv$NW zqFUcJj3R=Cg&=|jB3LM(q?B~`Fx|0d=X7@m9b$_fyN=y@jGuk%u4DI6v5)=j?=QI4 zyVlIy^X~mT_q8N}-sR@?jU~^0%-=96ZEg?{85!BpEToUV&|VIn53#jwfO&&UTlS+w z|MSfsMelq0G>N77hJ|;P9RI$!`Kr>2n+r}c|18O+0`YOIs4Asn?PLh zxXuH@Q3+Wc2fhrsSC%=K8!lS&0r%baZqWsxBmSA-8sjeeHYTo&-DZh5F*pUEj zPKa-p!v%c%Zm8uJuEKEJ)V zG6g@yivPD*I!u`R&=O!rxOn zuc`5W{*H0lKg+&$zCoTB{nJ^GZpwYqaR?)3$vei2#-!bAH;MjDn$YGEE8`1VEfSw; z#jWAeX)!Ze@}+Mgel#;=$3|~$S|NWKf;LW8tQwrza9=gl|CHyP>M%~}quzj1hHLQm zFLs-bSvI3{q?lPWtivK1n0vPUhh$F{tNjmYZCby!|4Hv87PnrK<;MwILuD_g3R{xp z=9rk~Vnt}g=O&Hf(ddnhdz6<#poU(m*@M#?ved8qPr5g2{CoXy57Xe!`5jd46Fg-; z>wU^bbWmic3ya&&$;r9B+6D3_nXPS~6oS-mZ9^275;IzJlreFPmJa2isjTMJs)5n@ zO&e9Th@nm4>ZtJJjWz1m!DjDqP2u1)?-Fgh{|VQ5ZGNxsu6XSi{1kh1hjEHn-&I=M zzEhQ2nAjetT9wn&_EhyV<7ivCx;{0!RjQslOVDyw{bGi_WsoLgilsSTBaO~#qG&Ee z3~D^0C5InvSgqX^Z1i^N3I?Zm9_YjUkGtY@j$YrLLfwBjB}(6n|Neh6M3>BKpQzOr zDB2!skK{~g6Ka2C>}frwOGq8p8n1(Av0C)HH8YIO`*mBU7@Dr@mP}+e73qFP^lx0G z=Y=0?AnUckdauVYcyO|ZWPtpSIdAKoy}mnc>#yUd*k(w4b60L|W5KWA6Bv##SRM>~@30 z$O_hZTa5mI*Vx%||VP(^oYQGrykP-Q+TFo=9zcVqP?UVq=;a2)or#Yo0Tr!^<<9 z154e@&Cz`?I=hVra7vO9zlGVZ8DHQOw((5ywALs1EQ(L7+{Vpb*YeGVq)%-z+pNjX z<{sOMgoDi$wnMR(np$m7CNFF3vE7I=HEM0U#!qbc!zK^AyLUwtn*&e^u% zl%>{-IE8MRic`jzEq|P+^wdT#4x+WyeVv=nNUeX^T+F;h+UFX`MkviDC+7=|s-D2( zGlIoy`G=Y3K|f$TTgCb;gt)UvGr@zrZxu|qx4@a3hzNzsq;F^mM4EgOlOkhB?-ngW zpATr3z7)M!ev{Nfys~H-g+aDsb#xTu|yQm*n&m3O*PELg}iivSoE1$7`@qUP$9E%_h_{NI{ z)-r$crNY06l|X-pR^Bb7!=@Y}tK4mn=G4 z_KJU4ynW7Ez*n*)X}mB~dTi1&kR;n0ei1sSNa?>6bttNruCJO5SI?hNQ;JM%v(xxt{ZJx9W75{3zQ?pdk;T=+U zS{iBVU4KWq5pPktD4Q<7Kv^ThTWn}D`4qt{##H$hMlJJ+Viw^z+oCv9mc>m}W@NwS zbt(@h*70Af=#!oSb5y%23Hy+sI-d{sP4&r%Wc#Y&hqf? zXgVeZ3d%L>!`=wXwMjT-j^@ty_L>Uv!hT}GGd*3|VdQ*_5ldRWhON3&;fz81YVr_F6I6`EsO29#em_jeapO)=fJ6x1k9d$px?T_%^T zx_+*SgT#`WO>w+y6oY9X?I*3u_`J@*m}opv@*nesaand6+icX#YUU0x)=e75J7pXg z)&m3?zV}}WMi`P7$qGdd)!f0wJ&uXZ_A-I}mg`8x-*(h|uzHd`SaYtHU|TP}L7>|T z;ax1YFg5^X>HRGN|oMmNMEKxI$aR!@r$E0wtn^qA$(m&ZlvpmcbwmmaXvkA6dF|)aotW(1R`5*9Zjc#C{ zxoBZq-usrdol^@v&8M4|l#FWn;Ji|{sA+)dOy!WqH1)sLnGGz-k6NO)9r}l$_MGB6 zhz3spbqiVP&aF+Q>RsmI-Sh(IADI`K{?5RJ->ij>te6Z=mwkBHV7|dN2B%b7x);Xf ze(zk_@pr-84x}l$czS!O6D-}^7G|PVM7H#(tyTOMUrA?;uIVSFuG`o&h^SL0Y} z329$LM9o#oMQ?iXP@2;-BJ&1=VSTgUT5jlq z869Z;)_MPHyiruzMR)csdD!{W_^G_EW1Bjnva7vGl2E;;jSL0XK5m`N*-QA{GKTV; zwk!^T3jQ3`TA7yhP?B!qnC_ zHb@-k*}=I%dTedy)!}ooZ2mHuP@XM#%NT-u6HaGt<=a3h8>TlvUT)vIr|@Oo$C6*j zFM%{W1IrO+%{(F+35|?N6vrUpVf`guY-pJi9wHSqaE|c(fv-FXeH1vDzoX6w$pB+X4!lVCC`*AH zf{JGDM=u~lV-iIDQRAoy;uWGX0S9E?B^8Ulkzdi^xi0E*`km$vbTV^}JDS;twaF63 zTFnX4hH(z}pYM<@e_Q<~0l00ExB^SS@U;8wC!4dY7a?T(~3)gCz)g=5Ay) z+7mMaJs_GjDpEudUkcbST`N^BnohjS9@cr7ypdDZ97mOLtuBZj&-=>^FrM?DYfLN^ zFhbhGekL3bi*cP`HE$H}5|l^F6qLg5+Fd{=;x4v;ebCjJ>!3*NNWv4iK=dxU24P8N zj*7rir7QhUOGe2=3qis){@u<##QlN?OgIEN3 z8}yCcgDl{#=Q2#eB*{#81O-adqO0KuS;eRbG(f({ z|FmeA{KSGywfCT%9aV&Du%qz;@i+ny()XDwf%UR82@i#j<&Ah_r;ma@DgyqgH29y!+LieW zd}?}&mb9;{^B1Q!POlFXUvn5q{Uk!;Jn~D)Pt|kkQmI=qie4Za0EIE0%G{hgEUi3* z8p;_WUtP1B+pd^Zbecawu`J_>08tht32kR{c*isal+W2geu3Y*p!({wRkc0nersKg5Ieb$N`xtDv+{?byan| zCWj+Z#}tKeKd7rRp7I6iBMCu*!y4a-3gJXeMJT>Qs9E8E8UZ!Ld4E(Onx}2_sz0;? zy*)M8wK4XRy06+2gTDT&R;av1x}?RYX(+AQZZMiwqFv8^%jm7WOi5v`)PAhK%&ygi z7mek-(oM*S=GEv*5=QW^>8KNxK$T7wnhx&KwfLWglXYS9`j=fa1-G883^w)g(yFVB zPi%W?ZN?k=PlPVxUL}EOFm{XPkqeA!;Tmd?k-(PF?ilBg|6(|d6RH`k9Ajvqlzr9k zIb$l9XLt}_$U9|NGf^#AZ19F=fH4M+|7mEF{!Ev%lxCmWnpeKj_R4d;vd1>p7FRRP zR;8ELmD_qLuGgnq*NDPMBdq1ZY2^2o53DcLBbHU<0=mgUsd~lCGXE|($+~1dl#X$f zW@UT{_p>=*qL%-_v@tYOxZ0HDe;SN3y1Is!4Dv*@oG%l)w|M9kyWL~0+pB$DD|O#% zV_hQ^EJB3SiLEC3Ier8CNN*en%SJhB|3tb*Yqq0RItI`7q~HQ8z@|=@u@6{(#_73% zR$5dy?>GM3EK4}T{M`Svu-)`(?%JYP%|lzbrPWPU?w<0^jcRL7RsV+ly5^cx?|b>@ zx_EC2mR>*BV*sj21KfLAqsR~4K_ryA+f`F}gx=^}RuIKZalD+pi}le#jx%#i_O7Vq zJjiAa%?1`&t^Q|((U#P?Wra`LAGP?F(AsXeJ!Pj`w_C1Pj&5n!MOU+$H_2OTYn%3< zHwmSUHw15pc@3YL+sUIF!bkyBU+=8S7~0>Sih}73x4Urm0T$WS7H4OFbgqh8$$jA{ z8A$`e?fv}E0B5YO&QFDY^Xi+|7VEnF-BU{6b#AptE9yHC?TMtgBb`3o&U<%{PfYK4{O=02BQs=n0OjfT~p?4Szb z2wU3286Sw7TfY;*zf}Rf%`8%DkuS*}{+7!Iixh$YqM^k@H6&hiV6OE#P0S zdp&moqqF{3$GiGx1o+rsni(GIkT(()P8n*N&c7jCb%O*qG?^y)`4`Cr`K z{(lQP*v;V1;w&!p&3514&yt%}Q_MiM$q+dpVAdT{uDqYB=am57C!SDgIpta1U z?C0<~HjOd_Il*~XZAIVkb`)Ag;ext!sW<}o8K;#*f^VXhN`50@BT2FySg79vRkYYU z?=NOI@luE-l!NR6)PeIVgWP{sQiuXWM3NoibAI;~y`Qa7Ew`_G2voCV>Bt9tb^fT$L2ujRY0^ z2@gi`j}797iQ^EoK-4i|4;CxF5rT@9QjXt!nMZbQE{|;G6I&)wXA9Wwy|f+xu#RFZ z6(TwnYX+#0_pm#l8eCQ73nu~`9tRo2isx@fdyy=_Nc3B!MOcrW%>M-bDcUl-6FMqB zGh-h7TC#h>F0`NYdI%~ikq`5`FAY`7$qeX;dnk1hyvw4Yy+Z8TR}4K$ zl4Hzo*Z}kbTaN7!{KvT?QZdi-GQ=}UG5poyFO@pMZ^_2|cS5d|F}n@)%8F-nLA&Js z6L!MSOuKt8bI3V)-#Ek6wiQS?bWE38uNn=uz$qO6**6Z)X? z3qeqpYP;WktVgxAgHiiMcC0Cu@LHbWyi9y7Z!zbQkI3I?u25A9stlwLR%}J?GP)FB z1tVDD%6i6E_9Ep$B9l8^{WsBS*aoV z-A5m2Mz(LLnWMhiXsavJ_&ek337RRUwWIq;W_eQg>+9Aw=BSaF9QjaYOTj zp<@Bsq4h7>FSSJ#@mx$x$#e1UX*Fs61!>yV(~kkmv|A!?g449$gPrgbU8vuEWRLb( zdtlXe!@Y)=HD?Xi9P{h$7%rNI*Z*TUr9ME~VmKltQ@Dn;@MT&bgPlK|zQ$mte`n?x z6!i$}qCrvK!l4)n^BTEN4B2Uce3~I``U$~yL!Zc7Ld-BU*aa8qpZVQ~|IiED+?5s9 z>kXW0)Vk0ywANVqR8?}a+5s8(hXNrZdi(Voiwk-kFH}(Gao0sWBzS! zET6}gnG167aGsier*rsJ`n`A1E4k#~T-rU}7r=%8`G-+gGlaa>za#UR%n@4rrgzl2h+F$U-P#f$R-JhOd??W&!ZrNOAUs)Zt z_}s}HKkK^GO70eGLF`w)zva)!e}F(sc(4bWYzF-9gAYvYtsBc;HafhP%J7DR_L%Cr zh7iO0TA7!jA`-Zs&Eo%v(>y_tl=ReH!`((%@7hdTOsjJZC5&JMIDs-UbC2U%ZUVaw z?}=*Sys|Hg{mE0>{)y}*D7N(p_JV6H@BJQtO7nx(s?x`8sowDwlUgU)j#SZFf(>~! zi<*;_pXxkKRpO3%a-#$cGAJ^5t<#y59ej+wQ{wKb)k zQ}4VIJB53~@gUNdf64wRxB<+xNpVVl>x`D~rM`3Bo|ENeojEpcWoJh}{fp}BZ9kQB zYjH!0IFZoQatqu-q&Gj|{7W9+^pbjwa=r22x(eE=M*q?ij7sn091knN^ELSbd$Xs1 z>~wCi>tduIpW<8{+$cO`f9Cfq`g=T0F9{?)mYyffe_=?(18sht6H5OsnnP z$ratMyWBAwB-F2H592hGpw=F$l9JGJx3-5iu=#LlG<{8ze9jGKL1S$4b@qUU!LbRP z>z;j)0leq#iV>@Y1x}OS1K^Q;c?-Sx@DiCPvh?@j{?=U;GZ(JVr&Z-HNLM_nDVg_H zWUd?CwOIIraCvSDCxN)9(}x;LZfieR+eXFOG^I1?QLRJgJY=kB29k&2+hAeSn4GeP zKO!dc(mY>BtO2gM{rnyRG0yQF5T8}MylJ{%JppkZ1&$FD%wxb|WFJij6hhf5>j%fs z>Jbu>NPoqbpjk{eBOaT@j;dFSLO3_dw}{8`(A?LOe8G&=ddX+tYV1$hbnsMUFZoC$ zFlfJ$jQRP#*IbhHZ*S#R;LdOXua%5C1Nrx;Atn_tjkZ{?LLTowT)N=D^WK(ORo z@){VE?v4#Y_Q`Y+!RUQ?YtT-yS0VI$Cw-&zYw1lEVk6wIsFSg+)^)T8qIx}*p%wR5 zPGf!)&lUa4CP~JFR?aTTO3qDQKj}>Bb$*TXd>uv5E<;PDz$4kKoHB5R+?2cuDppLI zmJXX08zM#^&y|aVc8WSwmA>yJp(;x=nYdN@#GOxe$qKCg)LPj#-3i(+d5of);gq{X z0Q0;2G1!;QP^59#oP&zRQ~__4(y#VCpQlumN(BE?9-Gr9^i@qwUIk{T=1t3hT&kBi zrLTHn&`#`(W~A>s5nKJa>5uwQ)k)V};&0V^3!QvTJxw>A+N6dRL+BIL3$eS5Rq8`R zT(G5n!oJ5gYeFe6Ij=OOwOU@j##?fdzfRLRrvvcO{FC&LFk9<0EdyMr^@|9GGqjC3 zWvupq?>lU|R@C^rZl(SY=coE+{aW*1Bv`*qyND96|4VM6UeO=Gs_7K{THq_=ntmyp zgnOmuQ3UMM`tF(^+(iA#l5xCly(RlL|Bb#i={~^LH%!YCuGLqLPl9sw3x}UaQN0p3 zOa08W2tZU3<^C0q5 z)?U-|8UtshX=_O|_mD}HeV;$Xl%5nS5Se_Z!oYdsn(@hCn-QlV*Nw%v`^nd^v*BCK zQ0HvNtGfP<+osKgZw|d?A?`8^m08FR`(Ct;I?|pa_(fZ1yU3z3(rp+SWgfKkuj$7w zvTi9hb1qxUvnTVWSe_=0;cu~Mr((cJ^Xu`c!e?gt@C!(h+1K|SJi(OE&{_4y``BTr z`R*++<%?%aNCq8Ll&ecIq)#4D%hG=3GwpWjt{DRWD^}91TTx zIKGZvSyC>^E=nZup4*O3l?WWRA>(HYDOTC=i|{dv*Y_RdGOHRgDqppz9Z}Wan)Swq zbt9WrsxR03G@g|1Bpz)HK%8V&L%d)H<+~SSdT3pqgQP`_DEEiz6y_%P%%Z1knyV{I z%Q@`4ok-=?I=)R=%)jLrFpdUnvDXj31QTrjzVE>2mP6jBmHr*;?0;8JY_}S{wH0kn zwT2Maib$#T-&zdF6jFDylYfzt(X^BqL%r6xkCaA78*WrxX9jxT6=ky)c>Obva>jX* z6It9_?!+m}_;t>naSR~U88-YfblKj3nO84Xx>|Sk-=zMTDX9Dx)IJi!)|X-%$m=g z;3ZA|oA*EW<#8;*RoCA*MeH2f=Oy^BUG0r4KeC8r53fAGaIE1`_0jpaRF`Y_&Fhxz zBxt(iuz@&w?gaiA(%p_{447hUKS|Wmrnb$md`Q3AT3xt`IkEX!=5W@Hrunm$a&|Ww zCZFdKz1zld1Tv3d*uUT+R}D@{bB=HB2L{#day^Hl2xiNCcqXw_SB&J50~CR10%a3+ z0GmjgBg_)@qhDvML{FJQ3Q4?~^|NN5M9*1VyjY6yCS_;HoC3c@hI|mPWlFcA4=5O` zS8RY^4C|$qV8d}I7gIuON(BZG&$uQF$CBmd*C3NRT)PJn(w55Iu!0eeWg=Rp3OI!# ztXu2=jK(P^e-b5e_ttR5G5ndujgsMl*6es`sBl_hz4RaO?vw?xb#U%jjT}W;Lw~EH zMJC+IC6Tfk#k~KhkDQ1gn@%_5y%dbI+Ofiwtm(2J;0bmsdH_1YSqD_XN4ZZ~3z2jD zIC49>TL9MtV%vfJ#RkzbFdz#MH$kHJSJ;Id+Xo@S1yC#uWzhzM+ZWDE@81Eocr9$(jKt3I~&CBDr8u z^=|x)A-U)HVS zKXYt+vGF4Jfq<>P&7T7-mtGN&ghFIH-~MR z#gd9yQq(COIe8m)K>Bp7SoBVwJ@mVDlVTL^ugq2!dKS>7Xo77O<2QQWu!v>CmZ7Rw)pfp)>W(!;)dofFl zkmaVyThRH6F=Iv8GiBV+@3?F12kx(YuA;c#Qcp>4T7S`n(h5Uw#y9B|yuAsOWk}N5 z$7E~a6z&MQA3uOsB4;yeINt@{RhtJByO6Tx6}LzEIxRbLke<3}q|hw@M)1$O2W%p-t?= zsxv$bw-0Vl{K+d;rxT~)JyQBgw&0L@b78))zxravIdGgNdgdPxLz6do3*4nqjuoM? z+KEGliet3w-e1HrZKi7l>7H{(9Z!f-azsZcD~R;dI@VnJYoD?(C#n&{Ey5u_6T2M-3e& zvg?Q8{>t0BGp-1t)EsJ=MxJW=rYoY{HvOwm(NNQAQ5XG%X#uzx_euj?32VJ6nf9JD z)D%=7&E*?ER-EMRGVUx`EEr{M%?JWAjgo|$LW?nR(rxgGarl@Pc)4-l&|%n1!~Nc0 z(7z3I=cf7;`vLQI;%_^oy+qz^kC6YS*4xgBM$@j@WMCvC+ct#rg}K_gfu>}KT0z1_ zj?5ZTF@<}^vLnBTKf^-L7$s0!!V~TShs`S|{R5_&%g40BzUIK8!!f35LGLffHRD5P zK4HA4#9Uba*}X#xlJ>YG-aDPf-)>u!@BkQReL2ZrXti?3v_mpW&(PuMT}wJnA(|&Tp43G(9WXtv z4{Us-9YDO&=r5m1Zf_uA71S_qyRe4V?77An%kcFiQx%1P*?Uf@t2V!k zd%!t=b~oSO@iSqBfa#bysUPs&P8`z#ez9#I8icZ~H+z49Z&>&acWq(YDU*{hyY+9) zR$^ew1sVRNy7?3Ofl}Y}1^7U{&@`UCpI+EVpr$akHpuHR)RqX8zm-rYUueo$VTGJ+VVA zbCP+Qm$&C(1HdCn^~7sT`;vRDPBFE?uVB%Ks~2yfOpW81&4f_JIB;W3ykZAj8GcbI zK$8ROG_m;Z^Kac9=~ict@GbSMr4KZmo~nz1M==&D2*_~eClQSLv1fry^aY2*9gUsf zE~Z&U^LQ5t3&kQqNO_Bd3gqSIN%Mp|(&^G$;FpA9vV-uFn5l9PS{!~(LBu`=)~d6` z|Khf+@3KFg(LgZspgBvJ!S>Orz;aHUd@BTV<=9o2z=MUikzBr+vl>kkETx5EBY;(e zbnK(>bh$xv7rd08EdC$tKf6^@k4#AzAdN*kV`8K)v3KETG+PZ%s}CgZGc-63`~-BOfW;4)ntc!8O29AqCniyv>P+H-I~++mHrmZrwvv12>h6 zFbomoPZJ5y%Gqx5bgV9Zmc(CF5fdeOE&eO~lx&I=3anP_l`X?=9 z>k@v~B=MhspJXn;)sb5Uy`l~KOs$*=LA;Ce<{Lzeyhrr@wQ*=RS3bD$oYznQ!nKu zp$Dbk`CN>OW(nNb2S5OHi>&NQ;Yx8ZdQub4M6L)eAQH9#dS$^Vch7L9?VX6Ca_DQZvLGEX1 zKC+8HNxEI|L{KUVVO<7@G8;uK6v%$m9t2f#sLT(t$^XvX2)8QI({>?R#ijTu=mKS7 z%owawc{Ti)xLkD_rwmiC!);lw)qAb07!rj=|AqNNaaDz~^OPeb*Euap5;BQAkg4dVpcj7AWsApJ?flB6gZq0Wan>Xbt?!+&$Vx<}u!V z?IJQL7^2-&a|lS${;!lQBy0EPd;$&HhpF!&v94Df1zx5LjLt<4>(axIVQY0*V5MY* z?*Bbj>vS5+eCkHyHr;D_yzx&Zo$-fpk@$ZsU!w&MVb>cu{4!39F^ieWJ!%|7ddd$l z_N&2tpoXU<+W@*@S5B5tXZSPq6ZnT=MH~$}W6(tBA^i-R@Z;z^gDJ2|{M~>XrbNT_ zUo2B7DqE7yKdfj2^29TEr@_j^$ludo5QPM>)qWjUQ zUou#rG5?X1k9(z&)CA!r^Q;+7;6u~p=mMB$+8Xv4#n-iP%4B@g>NnQkXg41qL#`FN zAnHI@tinX=aBdLYXY_Z*L(`cI$9f)@waPJ#QN`}DUn73urrH_R6+DUUWiiO#VN=d& z7d)^9rHF+?t?OsBf$7$%6A@^WWq;Tc{y(l z1dI;f=|_bYyKtfiin6T^dxoUj#s^koe_4e%1+k1Uy(4aDd8@4j(0~`p~q1*sEiQ)`zsdeY+f>6to?|j!+M`9tZo= zC$(JWPGuOI-_Qp$&oukiZ)T5Zim6=5DQ(Ovn#RR*GkX^As@I*oPr&oE$9@5r?nP1a z!7(mvSYJ5JSr%A}o^#M}3f;cWR8-HISEt!R^6ko#2a_A;W@8X_L}#vW6V22yi*t&8 zxqU2cH8ZAdLVW>?)jGOzFx%VwtSF5e)EtnV$*XAmFX;>aWW&$c?}7*31yPHH?Vf_L z0I1vb7fwlZF6#9I_Be-H_lmdFzR-V_7zxW&6lnsnNU~1)nDiE(mRm%D`LE?^)Njl! z^6PXNd4fX6_)?Rl3}o3$YL(|WQF9Kdc--A7ZK@M|X!>=v3s@huOydKxM@4HMz#9fM z>sMd^zFWOpa?jcedrQ2iuNG}3GnI$LI?8=Xm}CyE0wziQ=5#piwtcbOrAr z;Ypuaicvr6THSi|Ej>^fiJfNf#ZJ*;<_743Sit&+_fax}Gmvpca-Ew;s*~z?aJ5SY z^3N6*=fgtE3N)aW4gZP7FI8|N34hMnOB@-@4QKOX&;_d-n4Q|Eq@zEO;H%#ZjLatC-303k? z`3;!OcZ({KYylnIj7|W`x$n?l!c6*O>=-zP=oUGl(5kCq2JBl*mn0)T+2Z0s7-qlG_O)v!kGC-hNFI*}FET=2b1SEnOjRhc|tBlxNbXC!E(GiT9{XcP=C@Xn^Bwd^x`#?Hg z5*6i<-jyyM6)G3V3kJAVgB4!9tMi&V*a-74z}=cbf*%M`wod4SJjN2izG$0pJ=7PA z=KO?uupRV1@KI59{a?ru(YLBdv{URU>cWR?#$@}5(jXw6#v6{}lbNs72p_Tm63$A zP>NDmF(00!TwZt=8LNDom4^DM%9DKm#wj#<-7@``&vGZXRfK4=Po!Ghr$CK~{TY4lWsaIj_`p$HtU*-Ns?`6i3cdu^Ua(FyvSxpf6VggK_1AdE6y%RdihnO0<4 zp*bdfVj`Sj%A1yeq?jf~o<;Shk)uY678r4gZCn;V+>7EgSQ2|rmUY^BDd0#I{Bgz^o1P;a314cyM@<_HL-0TV>Uapbu}r6 z!*AJM%wI{6$G&vsEVQF=~P8C4ENS zZ8ep#ulz6dFIH|rght4|n(3>#%UwEap5_sM+*G~x1Hg=k(p>}PA#%M0-W_nzcpAgI z0?esWe|?Gc7jc=&Pu57DAlW9LLRpR!$}drW3w9|0x{&p+qKC1Ia$Bip4X9HqpRw8H zYgCIkj|zsU2lJvbKB@2XU(RY!9|bC=Dl|=CM#MO67_1JF=yFjc;D8}TjBi+*M$5Rm z<&romU&)p{p&gNol(sPjz&m7F%u>O0*%_9Ng~_Sx9TZ6Zh;y$l7|+LHWlxo5d?de6 zc~IcVn4pphLuXl3TR`VjR2>8lj|fv+(U=favqdyEV88CI1jSSAwj9-d7lF(;#X<38 z7D22QzhJB2aLEdelfPV=&RxXpk$&RsCx4fT`I~CD%VPwO$}Y;U1Ml*a6c%tuMu@Tx zls`+YTn_J;(xQBd^h5-!GO&#yu=<$zK)^oTKcMF;zNQvOu2*u940qOo(Y%L)=0O4o63S@HIO1dMm_~zk}*&|4{^;BDRR*> zQ#^_-*tdvb%F$v=h){J}x-ej`mMKrgv+}NLvSuOtOmJ4NKu!SrMEPikP!IWFT(FF{ zNE87DGk%NCL$`2;f*jsd^HDM$QJ48jE}{u}T&W2gmeET#4f{HCp=^!l>lC}ZUJ?^A zSbk5+2@xpGa&EvLO`x(Lo|QM$O!W^a6}l(;2aba6*bewD5&|lb8_3_hFmxFTFzi?@ z`jfOxZ<2@=8C7J4J8uen0&EyC^4zQCVG)UsRgs6EK-1nJw;4Pm+9<2xfLl z=Sq{Nm}I3gcEmt=fIK*auQ;rD-EXbBR)y~m>M%`)x<PLH*XM=BjVw>3Lk6rdkCIm!(J-3WH_KQ5!f$_D)EEM#{6fesC{& z8(j+@k-sJGK%9z<>ZK@G;VJQA6BL(o+p!+y>eYUPTV4Pw4(+!U=ONp&V7KpL$c z6T*@GP=D*UR_WAM;*^298r4AlCY49(C)lUDfqVeYsD1&bgy+>UcslP<6KL=8Tb`X5 z3p3R#t4AS)>Jud{G+6y2=O+48Gh(&?TcM#P929XhnWlAqkqI)5+@$LG7h)6I`-y6sWy7m3oABBzjK{Oad^aJbHKm@&iRVSRS z>nXmAgy^2<+(LfodeT;)8+AVu4q~19n8|;L*!rA^0I^*!2;oRUy{g|@`6E5v>7nUu zNKuAzN#>c7Av~Em68^^bn%)V{3c5_E*h;`-T0(szRGC!ulR?;2R22+Wn)((e!tutt zITMiy#_g#Okw?Zw@$b;f#USqCe1BYct z#LKyMdnD}TEw}9z(D0iVlif$K(H2Nm1Iw*P30sAWt!QO7D7FqNYKHihy>lcm)q{jq5)+ zl6&37<$vJ)>%7kD#3#U6)V_jej(3FFz(a?sVvTUC{afKn@K3uY`!A@|K0Y-X*4kFb zPeRPL$&)UkD(jc=)3Ehcd$37-+VZpCI>{>w1<&Z?<{cFZ=`sxni;fD_H zq$|ir`-gEA*Z?~eY!+AB+yQ&U2J3E|A~OFgU&;K{@SHHOz!3%m`oigAF(yS}hWsP(+g9s!nm%2QCN+6~3A;aJz|m|sYe^VGO1 z)Z?5QY!%&f^bOc6`pb@Qcd7y`tK@7Zp`%ii$!cu3K!NPlZ7X@FIM-SqFl)KDTKkiC z@?Nzh)!yTOX_l9J1phWU3N`?j8rNpAgc};3rYOME-kvxBvU)C0>J1-upB`6>d~@9# zu~?Mt>puw73L?7U)Hj1R;FtiSeFKwgt5>->b zO&3dhRe4%>i2)Y<($_M3=A70?v5l!*{V;BIoZTSgCr=t_^aENrXXP_14@-NJsX38074QZ*QZSzo5P#oST( zr?!$kqUeQIz-gSbSG$+{KDA!gi$6b3tJ@CbPa3L^1N)8D8-~H~@GFKVDCGCRR4I{pr{0%~fJ<^XUsPNEfpXv}ooK}KOh~+;fc;j zk8tJCX&IGAN(1 zaD^W&r3nu*?@6D4UJ7593uV-;m%WGM%YVv^@bkiFiqXjV>;sBb=#~_QawygoM^tL? zo#N4|0P*Lskg7u(7PM6}Qa-2eWt~cice&v0>l~R5zeRq<7KwDw25^Ho7UuD~#pjV= zCPTtN{zrZ*`GiXAGNlr1bomQef9zo4MA=f2G3%pztT;KPPQF?)KCV&`B>5UMQn5`I zGZs(^WUqoYtL`aI^}Vc3RN*%oJx8OLUPWWjPv}qd3nm9gV7swTyrCjQWMNzr4-p5F zG4V?A^4jB)S(0hxagx`PC5532I%u#vJnMj>`uwwCl%#FYos z&Jp*LQ_3fZkIK&$P$d*aWL6aJ*0dxWr95Tk44G_zvLj}Q?4*h{mLor;+7h%;$xzSl zdr7@b^8-(0jxI}LgXl^MasWo$etdpSX<1-}EQJS!r!P0vg!&tWLoaTPeMukj^pGCD=myKuT zL4Cf&2aMLXBgv4zb}>MQ9%%P+SnyWuJ$f7hXnTm4&@tLCHSy>z?WfW+m`mG}|A#13 zH#n0edZ$ZI+9tN?>ZdQ2Wa>JjXG(tP){QYsf9ZXKHp=(u1NvT4?$YPslsSgc;s)VR z<39MX@UwA|;4yf^*u;JeZ7@1%TVc$I604CJ#)|4K$OmI&=|ps?v3K5kjA`h}SSboL zyhwZ{I%s${y<6O7=!u>ssWChsW0BSvz6?Jm4>R29dr8r3z<*&h|1*Y)!+>z>6}VCu zV6_Tl!cW#rwirBT?Musm=2=eF--ekMdv!7rVVPNCLC%}M=H;MW=4}~kF}k@a@r`J> znIC&W^wL}$og_YL9y7)&IbwP^{J3m^X=C3@@&FUw7o?$^28y-_(w#S>mBKI9j5@XTbc>hKptlNHlB1_b4 zPaM7S|0p`ku&A~+3D_8igYLqN_Tg|kTVQ3!wl0sduHc!(E|1{j$Oy@?mQ-T zw{mQ3{r2}~UtIinpKI-z+4ov&zw3E;n^gmoP6_^24%=Q42$c9PlWZBflWR#?-|?h9 zlzP4+hnhn>W?oK;Zd+>lUH^e@GBsCYjArBU(n`i3{aSm=Pse$yojGk4FGN!?@u6UUC`|Iy~#HSKNF`?_s8QTw?q5i#K(NIcDn6V-hTBeoRt@p6Zrd~{7OFSHngNVxa}!) zx#kf05bQ|EZ6d>2b(d)VWMWJ9Y%XHmIwLd& zT|%AYdlsA3=HotsWilEkL}1UDuSb1UUgqHQG360~i0`i$tewazP>c}fwh_R%x^LuU zP}oq@_!eB>xUIGweAjGUz6h!z5(;ggd88egpCKCAFF6rHT6fMSL#L@#p)(P4TaNEB zWPst}{sDDkR!#^-m$TtfAFyiP9GsPia3!w;xLUu0xmRA)@RW90zP>54<&gYOv!YR| zC?syJ`J?C|JuOdGn8;HK-Y9moaxxz(9#i_0L%?j>jM+8diZ(;27qpI1?Y97KV7j;u z!(!v+5GMYO4z7vy)_e3R-F8FW(&q-bEQ4arcvW(NE9fd^TS++TtSXNi+D zko{StmF#NBC-1Cl^)iDYfd>Gn>kC;2*cw^ZJGjI=}go-)*+mgQ52YJ6mD+S)Pz zuy4CqFb*iDZ_eBUY-Ais$_L)H-;UwSy_w%b;^k}EX?`8@bDV$OpUN-uC{9j_-vXlb z3+S;3pN+%Eq{W*m`X{Lusp<9azc2`cd4{W^veB~EML6aX)MqxIb!`>(Ims~7-T62SqFG*-dDO8 z--*v{W${<=-!)5Yf`#lDLqLdj$I8~8$GozocEQdyAobNM>p3C_x;iY%5J*H>)ShH;OIY2-^h zx_C>|d%nG7Ea5r7OG2t#$NwWS7jG7*C3kbz3a3b8({qJlX;FenWFvhYEf*1FUbC`9 zPh|&u=i^D!_uQ{bRs(CD#z=1gJFK4r=M}iq4rqbrtah^B0IswzoB^PUoWV^8R83^= zGhioyz+(cJDm{50fRSQrzD_NoC1~fKmo!}T0GLt0~LzO=JL=I3Ct}-5K zbF!C+p(g9+(%TT8FfRWJeQsaEyodSGY+1`NtCnIm5xvsb&KZNQttE2Sq4EkJZYr8z z)XKet`sC#C8j(+_S9rt7vG`GZJ+dV7p1>7ZFq18KfoOg93jw6j{kjN3f}O^QzapO2 z&n08vd$@`ge%c<%wA4(X&Szd$Zzerp%~dxv{9jdDgU||G z#ShuR__sYlzlu7WIbI(?k}_}W4mR|#I&^V0C_7$zpge(nNn2bvlS9xv&-%i7tRbcP za}64=xICVhdTpc&@3NX1yqLdA?cn`Hkf{>6jR@P7S0{LjJ}6gOKNk@(+?5VEg(?|h z#-Q1Oa& z=VobtMHKUr5!WDSNKq~()xv9zIqbQ%D31hRxWn9(zmS>+h28o zycN4%!)RKGy{vslU|=unm=#~J@AYqrI+a$9ML8MDoTj^}%awz8koS7*DCt`yMX7J~ z4f>AQZY;UtbXA;85GtsiT zg-wB|u)(VK8`{%ItLQ^bO&g2&=+owxIkPYuQhsVDDs4F$w-60$EsZ2#@l^ky&&omC z9q<mtpPFq%2@ga%8IL>|Iu0Rb(E{eU-Ctgy{y$>LY19czJ`OWd;18@lxYv_(ii- ztqB=VOsR-Ka!7)rD#WE_Lyj|ik^CuDj)YK(<4o{3>Ym6VG^mXhG>qvOW4*nxkoFHQ zb(o0t!I6R8~L&D zQ+fYdeL;tX88|CHhoQ^d;z_x+(Q0@QCL{5ux|Iuc2~qP z$E2tg-7MGGK?TD457`({8p%9}ae zu?D%A%Z@0OlX16`vvMAv>pe!kQ?SW3TmcF1Iktc*ak|wAG$bYCM5Y35^cfO8Yjo=h zsU__j1Nor^>uJ+smfNsNBo~scZxHSym!o$y<{AH(u&@liIF7vhqzK8}~L*#eF+Z;)XYm&{l$}Aav$6z-()+QED=YAsp5qI&B=4Ocv z?|a=s3C7n{ZIoOVOfUH@wHL7ScSt#ckxZdNO8;>rDqLjVRwG~sfbUyE%M~QrW#MJvTXKV_R8-J(P;_3js?I{3 zFOIL8EFKUWN?wR>idW`+l~hUQWKtzK50lPH&Pz7USt^Z?vcoCTEGZoLKzcyN_L?Ru zlr^~q07U@m*d#9ko>+}2@YFC|B?k1P;ivAUw_1)0Lu5`(fx@jaydhQ;Av;)!ij=Yw zC9_3CfK^_KxE3hOSSjuUW+hpPUjnAtmn3<>)vyi;Uv3w8M>HL;Nhn+~~)@hX`xwf@Y+l0ulU+6c)V_&Os7$yMzmoap}>* zxA3FHR1p(CGuu{l2|gIQR~!mo53~|Lfsf8ekVL@$R|$d_;40bhN<0Pf0^E!fc`dw~ za)x_d#U+*UM5@q+9eiiy%bKtJMapFrj)JL5PEoNysZ7mzDR_XrO-~S}WB(*z!d(~` zlPa2m6@?xUEx@V*ZNwFr?Tke6GW4WNx?}~)b8M0xMqBU{$aTm|TqO(9P-t8y?RVlU z?so0mh9X|2*009Qd#u@CzL-zdAVrP*8=C1kSpskMhBTu4*Mp6XY)in|JT@sc}W1GHD#+=_eTBF+0Mc~ghxOpO&`g=S5yq*9|< zUN^qPQUlhja!+WmhIi>7H0zpr5+`dk#8ENZHA_erLo>DUt^WQ!+9t~V>1(yDwmjz( z+CawN_FHwftWZ2Z>l;^rJGtE#`q7Hjl$ywv18Q>Zo~E(tnmTVnn7X81Tj`-LZ1_?< zTOHk$l)GMC)x0+Sj+#!ImN-harKKrmrD}g`U}&8BBDLDzpsA+4pT0uqi>9l*5gwwfj|8O@kFXls}vA7Ee~b zCpqSJE7!GTr`s#>cj*c5m9DMlqt7U}P`g7zRg2s5{W0}4`kU#CRomLnPF|&2z}jKI zNzLLi@%*d;0p3@jB8gk6aaeofXVPv=*F3w?4f~tOsx8AnlCq*4Gq?OxG=e@OU&=Xx zUT=Mt_7S~AotAJPJwdCA-iMX69S!wSQW;tPfO0p}Zu&f>hBa^Ua^-4{Y0P5fL*83F zKPyd0$5*fM5`eN3xkzG@l8})WO~Z23lDw?u9%|LPv-|~efO4p47GkDu%UO+d(e|X> zMGm&zOt3>V^f%EP(eU=fP!}wpIq1*FTG^AP_hQYQ1(TOxIv!=rLabBJYVimA_WxWn zELf_egu~gCvBW(vh4Q<>4^~mX)-d1!+Hko7Zf(0%G!|yi_vDyiA!BKp159r>#gD*+ z%&zDqh&Ag_$Q)z~`?)_I`O1x+Zbm2b`X(<#vjrYw7N7#*6U(3IT8Rx#}G_0m^89U6={wGuLMyh6-4s)DcL|u8DsNi8(RRJ&>O3 z8xjJK;l=p3zy>~bx(|dd15FeNh zDYi>eC-;EmQfSNo6e}a(DptT1oRv&Pe9K~4ui#`;hU~R4f^Zhd7VfB;37AFIC3k_p zM27r@z+=&H)?|687)Z&K`-vCDg~;P2k&##ArIIbPlH@s3cR!wdkM!|$nIcf;JGoPF z0hm3eA6zN7v-|;>oNVSqSd1<4qLW^70REKOpj>;%&$o8&cM_n6HJKk$_ms`v%r zGbJbi+DE)BR>9L6F!3iipmvg^9`>w|OIE{P#Z1XC9FaFe8V%3PFiGoSyQEFhUg$?` zh4d}-F5Ue*Zv`}i9OI2hYlvsB!NNh|EcZ?KA@i;{ROD_15_V^h*^<7~Q_v`z8?-8iRHnu;nS0;Mge zaAu?Q4hr~o$kNf|X}vN7I@)<95Rcp*GZ*-RT(c?%1`!BXsYfcCe+oZpi1lklJdI6F ztmu+@UHL-sY&BXGCmv7}a<$^ys@Lhy#2-}MiNElk;L_RWB?9Hwh)~HM<+os?v|Rbn zXPb1Pvc_Yp^cD8nd6jGnrnFxPa4@!&gRB=ljd?GUn6B@u&lMikM^s-C1?ldV zM~fEfHWjWC-O)woyb_Po{*|664%XHs=7`yv*RzMjXEbPdtHek1cW{^Fn8wX#mt;gu z^w=TIRBdryBb%nOw_hc@uDodZLuyjS;3`^lU2~#fi)m-wb76>yT+J1BnWmPV7v49n zD?A{IG`i&!ir9wTX+5I3hStPN(Odob*;B-cdUiNVJgD~!Iw%RymH6zJkhC2hyCwTH zD<>V5&Qt%vRVvh_mOmt`)c9Lbg&S7ef*G$@aGItV=T&(S)P`l%3o4Hp2(`W?(+shM z-T7JiclA>;VS{x8o;{?0*%TN%OF!7WCSrvFB5e$IG@Kyc^>NpKpxkoz(4V9Ibn@2A z7&~ll8AL36|4n~}`xJL@og!=@ebpbXKGxWy=hkKt#_B@|t17GYKK1U!KXtxsQ{N_CN>cj3o%+gis2kJ5dkI{7&3hG|#ar|Q)7Zxiiw9H!Cs zrhYtUCSE_EkN;jYUqvPoT~}1+);LKQTu-Q-ud`~{T=AdwcGINd+uCK#wRv^gzle)6 z=4zFsTS+F(KJvIZX`0ejQuuD|OsXX4oAzm2n$KA6A^LZBCvAKChl#e@FxIEh=d?^N zzSh&67c}6kye(c&yssH)ENvLjTxl}Z25F8oU#ie+jHGeJ<23~>v3W`Amt;Z)sK(=a zlceeZ%KSMI>Uip#@b#K0ZTExTX)Z7_eXKPVOlNmTO(<*S#8K+c9Ome=nsEMjygnve zfwS@-$zCE!ttReon5O2F9@p$tr;%+drm1$fx)%+r7!Alz)hE1Xw>tMW^GXWc5 zP7m)mttnx zu6kFD(YCz$FjhkEE{{b&GFXLw(1Y!XIZAXRb9_30E@r+-l%Z{`@As`>DHZbR8L z_!`f$a1z|Zo0~ldv-w_W-7tfHD4`Qh7gW!l0*4E&!sf#|;hvy_@E(!Q`vKxACcAw? z%#sABPsm4U@#y2IpRCH_H*y%*j6vJK}dkxnkd#L?}nRC#(ailLQ9dg^ZFj-Z!DUQjObNc!q3-(|edG z`#Sm<{1v!iX$QZOKf+bKL7#@5AW>XbI~UZ7uUD3Ue@UuK4uJC{EAvrsv*btC7Vw<3 zDdjDAMyiZo3+|NOh$e%FWqF}nz$G$c;5ASO_;E&Rp{p>d z?jx`sEv&u^;3+NT%jKg{|Dsj$nW%4`jl2vUmpMz$LB1q6$XUp}I0tzzvN1|4--8T> zT$Z0kRtESfMk7nS;uIc;#;r_Ih7c#VDb^tsb_#GDQfc`cj70GC0f>h9)OE>jtJYRa zfJrJ&c|1^}A{99UYE@Bg8?ae5IYR{;S6)t94Qx<$#ufwnl>w25fX7OQkWq5nZ8E?^ zuEc(MO_XEUN;ez%Ml8-L8h_5)+C?ao=sn9F3QrV&j)A|C3w4pQZ0+mnDKbdAx$L@Z zhjw03i0qA)l5+vD*ZfJpC;P4WCuyhbv!)`J0QjjtMxF+U>N_E0fVJxV0p7qbb>fUU zz;V?@x6yJJm6wyPyhHh5^lAB5CDpPLc&@}}7hosKBV3UlH9A%8kbX5hE91+24F?MU zk|h}$a-PU4_5Y>E$_n($l5Az5K5Ncm*;(DwNNd?A-LY9ofWI!;zZj_33T7k#@tO~= z{=jCLoZLk(|6cpB^J8USrK&T&I;JG0gIu#C zpKQKDh|a1schv1oDKU#1s^YGi?lqo^BA7#)PlQ0`B`x;;f0@(C=R5~Z=P0|UYD~Vg z1LH`>Va8hPVN(U`Kiso@3J>2K*H066H>~dXTpd%(H!rAJQ|W3>A^4X}GyPY$Hb2YM z*)T3E-qhNtOo=z;Hob{EV%$v3h$=ANYZ)IRFxj^{`41YmP?mZ27!9=ZQ&GbQ`hjsx zMtde;{l++%gU^ul`}ufpk9I_~vBBH4k-)6^ZYr)jSFzsstzk~_H{+7VMfv_la?`6! zsK}07OwFBM-%qX1`Wqxv;mP1_> zdtB>J8;tVO?xGWC0XiDvi$AE#Ws*E0?J2gE3qjk%**rd7yPUVh=AqVJfcKDV#*2M% zmOYiesMG0Ak(;YEy3AHdd4sl>qAYr<4X1YGwrMBQzzm`WkDy5Q(KOJxv0K$Y7?j8~ z&3dNwEVgzwtJPnidBr~DDbdX3PIRf#MDy6=Q#7XpW}64Z)EgkfRin92XVs|kd4K#{ zRKbGjo($EHaG^_~YKkaoe7x#{_@m7|Rk3t5UO!N_5GSTuKCgCI)xjQH`BBAUA1}S6 zJi`$d1}Hai19Iw>AoqNFoidL{P4dES^S;i0jfwfoBd22f1^QWq*jr(fKLL9qs_-N$ zbz;mVR~aMmA0Mmiks>yCmHjdiUcU~&-@c&_6vWz0rJlE{vRIkJ&nx+a{p4ROAYg+6 zZnhDl2|lLbrWHbRVgY(u_+<7iR3u_W#G-$T9cMDoI6vQn9ugnT+J~-?xTJ1H zOC_uXA#zD_GUgAmOBxYghuoAdnn^_V$R_x)P+Qqy&l)roC~!$ZHFB$Qd1#=VZ}SW_ zD&j0esE^_U&dS~3jp{q7PI|5UGg=@^C~ii6$QI=@k>|3HS$`o9fQZyZ$YOw&Ktg5! z^J3n?e}JLzV#HrQEf_{Tlorg3j{HMwhsbb-{)kv-4yR8n9fjmnqgbw1* zF(e)`Rfi$#6vXlpgsT`Rnuo+I&gN}Ef)!sgpCeO2*OZrtH5e7Y1KtMa#+1R!z_zdh z@H`L+z5y=<*ZNt&{orL!d;CAYU8cf9C}n&iEQUyd&~If#Sq1b)>0dYk z-B7ybRzeq)78wfY6!svQ1MS9c#C?EPW2>UI&=L#?eGARUfM8o_EmrIs0IkQSO%H+; z=tmbPNQmwnABtaB+kVBfnCX^fkP@}SRVLbb6x32s7O;**ajtOVsf@aF`BU% z2o$bGhc`mBe-Rjg?P&}db}lx2{Ea;M{A zXp(Z!7KA)-emw>i82%iC=A&tqQt%LdY^Vp^s;3m(0axoYb1r~?>8GYY00(tHl6HYw z-PYK8uuGQ`H3%-$jS0b>v9xCcuY)VJ$=+AN^_pAL`oL}tVTu4;qh9XV2yRy+How4) zs%{IeqF;^k1&T-C);#MIm#?e1(0jhJwxqRJUu~PO>b0(sX7Rd@)gDZp*_~dO8o#aU zX#LR`$F7x)gJFqXADZ_BWp+;>J@yIevLg3;jP3NIES&VLV>+#W%<7JR7^|(mn?JCv z@O+bWUJjlNGE;c42JHD%b*_TmQ&E#vJhyv(?XrAI*M+*NS=C+idSmM3uCPYy_@$lN zrrPM=okF5>SXk%DmKj0uony&2e7riMDKZaR^L85MeAaAFA8_b5bD0vW@20(+LA?GO zK8jQ3yXaz#P1lFoz2)D#u)5GcXnhEN21?$ zxV7|#`IsNKRtANct0>QWTuoPKsE4HqqJz#SO;g&<4t=I{c9PXMBcJDlvvPp|?@-k~ z6hEn!bRMezP!4xyH%*`q zM*M7Ike|u1&BMpZG@X9K{2u|BElhN6{>Ni&JJu^1kYG z2ilxoZr5Q;)EB{K6X`@=wb_k4Ik3h+HEZIL(>XXSnA$123^)3T&& zo@puhd{Mn|xbZaWn9PJe;n9U5SWYu^|&!BEF? z^|92~vl~3F=)E}CocHRk@<R`~w#T!Pw)5PE(i-oEmKL>50?3NEyTIz;b4khz+64udzdAeYBcaBK=h(k{2 zXybW#5&`0V{sJt7Kpzo&jIc6Q#Z-Xy`} zr_|okuu<>SpbS52sk{K3z*+WL(NNK-y~7OL8J27Yu7z(~-{v7zX5Xtwo3bUp-I%t1-e5YEc`uvh6h z6%V2pR;z*_eQvqR9$JzapgaTZPPwGiLuca$m11~C%x`5H91$*1+QX4E`>ZCh1J6)?(;AYnB}|{T?u0jC(zlj*rpNr2zOenLUzFTJQ-oY2TQY+2hi<>Wy<;J zs+_-+&FI<;w$cw>pE4bLgAT<##jc{qVzRNn(6!-OjEt_DxdtPm>;1Q2fvC}I75Wfm zxUWZ-qde!G=xnsW;UaPt4X~jiTI9FIW~30ozXON^!Yo;+bXUDD*oA#noyysS9aQbl zz!z|;Q^`b(thy0*6-!nvi>|^ZsM^A0=zWzeSb<(uCHZmDWlG%P3awHebZbNdmFh`E zy8gm7) zRh_k#!h2QDczpv##IFVDq4JM|XUp@7qX&a3UgUiq_)@hmt9ziOhMi_N_et%Bgub~x zgtsvt`l9QZ;oo|{HPUC=^$DA|_+9P&L|X2-ve%Wo&_&XdOWExd)cK9pIr?%}B4an6 z(pAsKcPz~m9)PdNW(%*CBLgW_<;70@|5m@tyVl=OYswP!Js|8!4eRr+ADw{qo@{_( z9`%lI90`BjJFfY4@b6wji>vQ&&pGlg&n4ZZ6uJw)E1O1e^6%J6r`sLt9L+@Vl&)9~ z{<)jn`FPij!9sMa+!^<~B^LeH_lmGTZ%5z6`bn8fdf|q)RF__l#_REa_b8gj#$4`k zAr6My_e8evgFp66X)W}9(zTG%<2l&5n^xdj(NRMWaq=;*Z>QKDF+X7^TK+P<BIZcfU2<~R z&#t|#w}M}E9;ase-tIU+>-X&L;L#D+VzWnksM8G78W!2^u;~EjgXJ&dF1`|HWv>XI z`D<@VK9!B>>1{F>e(MQrzM6Zido#&3lhGa1LQDzj+D|sb!<~CuHPMeclPL$o{_9ZE zyn^qT|7#ocy==b8nBZwJJ!#+JnrHH4l{!r`zT%YH9W;*cE?NFE{3XEm$8-|$9Go)u zr0+@-y4y(S3In@dk*#yycd=SyGWt62QtDDBc6^~$$G4l$(1N2cn|W=;VGqm!^h?2) zOy}BX`JOhNX7+eu#sSs`*9_wf4(Q}&_{58_J7BaE4B*LDy(0XMX~h!!JEs02yH`4? z%bF5aXw%7|Cg)t~I6+Iz06JpYa*}_T`SirNPSZ|?Mf714r9B|*vdNv<7xcyy&U)y3 z(3r-4;VCyP=jOU58!C8vou(Q_31->t)0Ye1TmIDT5bN+!n56h#pE?rQU6Rp3YJ&=* z%_DRm$HLsp;HIxITelOEeN0SdY@EV)mN^pj$k@%c3Vmq{Q9q#hh5+WV*r}&VFcO zvreZQj3eyDNd-oTlNYWX0%_gUx@gN*kjXvpBkf9ShOKU1*WldX3Xj<`l^ zFN-jz2|Au6({7LEsN_FfWugr4>QM~?t8vOaQzR7^nWl02^V5yTxDDCkjZB_f+782S z-j$>n!y0~LY=^;@|1k2ieo(M3bd+8$L;^qQj*Gl}SL#y5qo!+hJ(4SF|?rxo)eN5b{O0UNRx@mG+PX@Hwn~BsEM&wew_6F4>xP*=i>T%{jTP-A;9k zd=j1pWh;M-*M}6Fah9C~7Zni=ZDK61M*l)GIm=()Au*+%*Q+GA5({6?nRIHn4TNLbF`%~^S@{y%X{g}>*- z>)!>J;@2!FwQzx6E4!21sSf~3GaK~NfWK1y=>7nA5&)gIeERH*+V^s0q=ojp{B+28 z%`f?tKugVWMU3~5=5NL3Y5nRAimfgwcowH%!Zg)m@Uq>0?Kz5RF)=3$IjpgV*;#@tF*=0=by4*Ur||WwSKjG`Z=4nm)~nq&*sf z=2)DsCP}j-nx?*_(S+&Lts3J@qMEL$^3PRIQ~&lFQR&rxd;FtnRU4-WlvmVV<0G+a zsvCB;n4ikiY93BfeD4A|h2d)@Boh@CZCZG?Y+oLAA*+I)`DLM?swS1PU}N>ygw6Ah z)Cyaxy@69n@lKxs!sC)F>_hiKRX5TN$IY*%9H|>Va z^By0@Lko+p>+DoK(Ne?f#VL~~yis^}!OF@#x!nt-)%?tR3#QgarWVgTK!6e!4sNeI z9P@2pQNy_KKmFSqx6ORfU);RLe`o&~Qm+?xZW@{EF{v+)(&T)#w~c0WlyCm$RFLpW-p+^6_&P#>t}lg?s1c)?CNat3wE2rtRRvx-}`nfo=77;tvj3HipE! z=nrdJ8UAjrn3z8E&fKh)aQ}^c56Mrx+Ip8$I3A8YCuxb!|8^&~{c#j^<+fMaJndZ1 zPQph~!oycCCM&@RPMNpjhlPWKOX{BFRu1fLaLIVxf32}PrLn)UX;=KZx%=?Gm!ZBV zr26p3ePJz+XI|>dZQbp^qBjBW;UxBW(u5wPyJym;P2SeIjq%7)&>7CkuzAu!;=IA@ z|KV$KR-O_~#VK=N^0-jgzpwFjZcM*lvscEyb2kuqDcN&zC-%V=^@T%<+(mOpYJCC%_nY_7UKJ$+ww__qF)aJ2y9`8JUmv;Vt zEvElljQ?J=52ZH?md$07#^w ze$Cw7wUb`p-_riMmYYCY_kh5*yfRmFTlxVz(lV2C{{=+ zaLRm_9V&qP#+){Tk3P}4m{}h-)EU6Sf=_o`WAF8cI@~y? zywc1G+#Bw1O*{E9lb0Je3#uLIMo?sL^FY5^>}2sr-z>rBNScQ-F3vJYUYmEPyMXa6 zYkt@J_Gf8tyVT6TPMJH333+?FqS)FjsPh)5 zHqEsY;`$_ZbdKhpnRB-T;<2J`n1Ay#LSLFU3o?Ulo81LJ{5nmE!mTqZOw&Ze?iR)r z@gC=G`e6ydu~k1J9kIEuvysigyRB9NW%wwnR@%cARDVz~)@9j?E%*=G=~4v3+Vpho=}#SZQ7@ zelq*B>7Jx8@~w#_xf$}uG+vq-_|9k}1$|c-Hpynq$T6%2Jl($PeSsk7jk<606i1SF zoBXEDUCkPWrNwXcam6}ZWeix1tHgmnavjW1q?a@B&)q z-S8T83VdQvgVjF!4HWRCXS04TIOH}CC*ZG1M|6$Q(y@FE3h{03sA*7w<#-hz`v0!D z19a^F#DpEQ^`@(^UwWEpC!CAtN;kp6xL>At_*_h;@e}+lBF@+XznYb97>17rjy7oF z^FDhF4)B@j3Oxay?>1I<3;uV~VeKcxek@N@0Pn(8M#0sVWh#3(8L$5j%)=@34(`ah zVyZ?TrYD=GqIZ+d8!uwfc&_w3OdgYNEW$d%sfO*?@>!{dN~}N7)?kCFyl?AQV{@j< z_15SY*8tso%wf_Y?E(yMfmc67H{0A%9YL!tsmgOGzJ9@I2tHFnmmq>HFXKzqxipPY zqn?m7&X}yOkDG1yt>#Bd413gqaGJqZ-8ZvczfV0cV3xj1E%Cmi8&=PrF4v{0zPkqM zMyY;Jyrp@fI_RKM*D62ZD%nb&WtkGj@Yy&v3dL73Xe=7M!}rMdukfJ ztCkxXO=oJznkr8gDs?L3f-d>C_K%e zQ%9v$v~Q<9i&@Nk&4>tcWBW3r-9K?wvYw6kOJL7gx_D(xV0Fu&JHe%PPB*vi4&jEO zqhUk+fRf$BY)n$*60Msqi^56W#8%c@vN!1mRo?nHd1aH1YC)M+^&hQ^=1}O=_Os2H zR=}9Uu!))1zJWO@Xe#qP>!|xUHi1)TznQ1tk`^7TJYVlSkXn79VL{iD+EqJf+4XeX_U_`->9pb99oraldI&EPjo{;9c`f9H&xrV zkC_&Ik};0;IWWGxhC_6BWZvWX*>B>!;B8p=psUNIFNYgu?R_j`r`bo9jGHGNNG581wbN|i&ah?!VWNeyK!NLA9>*juBI zx2@w&4UD6Y@Nkv(K0&zsCbnGAyU@2Zq;>7wUFBtzQ=LJT66!r&bM;)>6SS&!0o@j$ z)hXz|1>A<*b{l3%lU@7w)-TNmnLiq5kl5@omCMN19JlHSkc)inH?c}ZO$!J`Kj?jZUrVDHNC&T+*-q4Mt{h~>AsefAv8Ktc)ox-v z=fA4cvv;*$X=vssT9r)!+~S5E%{O@=mB}Oxe@y;gHNC~t+^0vc%1 zqJFnPdX@N^-2tYb`0)JRf_v<_eM!aPoILY^QVHiH?k~QZJAgP;zTsucvZ_DxZu2R% zclqM>+PdQcht}~8iv;T%@|x;|^%XaW?xN9o??|Ua=9EKZfw(g2bL&aT_<%;LpJcvU zU>hQ(+8t<@NN3EulQ$&T+G{M#5QdwbikAroG!3Ocguh@;MYc#H-C9KuUE$rSsT8L( zh6u&t#pK@l7|H1RD~+QhUP1VO&)NktnVHz>Yz5;RfLQJx7|agSA!!Ef|4)hh5!i=h?; z|Es%F2Y~O&g$)w$N$&inS>Uzg+~(Kdhp3sPzrauaDl!ec>K05@f#>ZGw3UK$20mqy zl~vuVb1amu#?ZV4*pNzA7>dmW*A*|p>LjB|Z)1+!$zbr! z8EZVIr04B0j!~>GoMWgEk1jUqS95YpPwC#ZRhR$JrIV&q&C;GEP;2V7GfF=Z=o&*# zUVVf5O5&wPCv{vzZ?lE!l)sVGq`c*t(|S}n!G1G!FM7D&oJ#NU?~2Wc?w+XMob{v2 z0(+agspF3#vLLp@QPfw|W1h_(D!FE=q&+JiZ&VQft}HZMuKiLC>feE7CWu6$vas&+<|Mdx27XKF$_C|O?!Gt3hb zr`J0h*My&I9Antzx2<`DKFPI!jA?h^D(UKZa~qP5EI!t0ncB68pkrm^EVzL#$@)2; z4!p}X4t^4r=MN9eW%U=u_RpmLU83(h)l4e8+pDbERXMFEwIr;%uIp2_RqfnPVM0?~ ztNCg8zYTy%?zgi!-Z;v&knEt3u;0>pUi07ly^RiKKl_TCR#m(-?;?g*{j0$f8ESUJ z*U8C*1=3%w-Szc+d&=|1ag67*IZfxu+BS@6u3t;v-BMaHy?sh+eqJf=i~A`#j|I_A zN7k`7(TDw&xL583*9_ibw$13X!rz=5^Dfj4RP%ZnxS4XO>2Bk9!oQly&CBXh*qs#L z7%r`BS=V%k$7`KH%x5%GjHKseTk35xSRdUsi!!!+3tdm$nA^+v(FP@_wST6+h^%7e zwBPYZ*lSpwuIb#rI6kA#3chjQ4Zf)@uKU%~RcGD6Fpa6d(fFUbpmA<<3q)&9A>NSu zLp<4%z`fU!K<=d9AYY46Xa6QiRLb{9pbn1t`1JFwrd8v57j&+UNxo?PPRC!boGnyC&Zq{dOTg)x#=V2 z8n=|Frj4dox45@uwm6YH=*@LylyJt)vPV>){bJ6=HcwV)l7L>$h9eXVB4?rBlJ>*A z@h)|&-+avWI`^Jn!a#1th1P_w?^UNLH3qEa7?rBLLfA-Cfm87>xZRBdq>1gksNLl5>{2fU_WRJ?$cQUj#sR=RNgX!k`HPTnNm6 zgt!WaC7joGlXk|_iAT|JlN@k_acnOu0J69kC@#Eh{?69 zoBhOhN^OacB)Qqd7LjCU!t+)~X=eCp3L@R>dzj`S%bqHrf0CiL*O}8~q&`x?X6dMo z@S;D`1ns5Ld?_7`FYl7B2aZ&(mEIQ?R?n49V{NOI$#SSq>*{52^Wg?B*@@b~#)p7? z3AuRz5S~pY)d2E@S1oUW?=D;UV~uWL&$B)<(fUnnx@T!g}7UkXT2UNEI!@XjEu{&Xr2te zOqfNy2M>kaZqdMIpXaR#m_1cQ`vK3i9cnuZaeLNgpVL~HBzXmz`I<=uhcp2&R2-^4 zC-W#Jsq6R~%K_C3rblJB3Zj%%cdI5hMbv7P2dkIWsg(7_K@C;daMtLi*_a`o(R>4I z3cF7_ioW-GN&XjYpDLyak!!X?v0F-Enfx%$e>UI>heo?!dy1RaaNnSat1o#SYZx{GT`T_WHoU z+;e`v@Atk;?xkK0r`0RUOB+IDag}I&3$(V{>Uzq*Luz(07+tlk&Ku+?%3No1DbcX* z*p@?P)H&ReC9H||^^rr_ukF6UuXxeKxtU7%#;WT7xA2UmyyI+6WqVUobKaM>M#uQV z_SOz#Yl(Nu3ib3da`P4Gi3)A=04Tnyr74OJ)~sx7VU&_LH(Vlh*X?f@UhBu=cUNuY^rlw@Eh(X$EeY2A!_#^C{wH*9h_|1?DtI*bH8`4ux~9`H_K%V zEDq?Pst%Vb+J(}Z@+EBuc(!syE5iF(y`#lIe?Yq5yqi>8`=RN6$p=bL=UV?D34>S4Q`t|A4tEvtE3o6x$Xq>2*K!p-_h^J$Jq zd`o}Z_$DHYHKK8Fun)&xzj9QkK~>k~iH z(H5VGe5S7Hdhke&S0iWC96`VOEdwqJ4m!1SjliC!V$2C6n-*~yC9&0B5aP19U zr{E)Hjr^l1gI0^iiUEeZC=FQ6JjD70cC*pCc<3oNwOo%3;eE?Jf^r1!QX(ZyqGOSF zu?@h{;AheW@Q6{Z_yyF<CxmjvV&bl+*|jhEgBU?_ zih#mgi>wu{W=|0w7JcNLV3~lCy!hG+Acy~>>;z;NR^;x2KZr9^rXa6?*OBh%TWDVJ zV{AOK)OW4yg2cz;rQ#21(d_=TKd9GQ&NEo_92bk#kI`lBVlQGPX*0NE*z56QJS}%9 z62QO1qX{#GGx@hzULru~R?88;7F{gc2YeHs&*_2GU{f*$cEMf|zmZ)?O0YZnFWT$7 zTDl5DJzmN`%c?q~DDAAo=C8COw%Iw0{)=2qF!gW>Mv8YmczjQ9adB-G$ONE>$0cNLZ*{ps-% z2XJ&2uNLB+YmTO*^Y1zSq74%KFcs6!iG0;xm@KiMYys;55D5En0C0k!l6w{MWk&L= z;TZBf!D=L=v{w{^Cg+5UH4@^g2Uv}jMtq07rBKjsxJb6gcNN-$KlgYkeJ0=5v656R zmNjW>lYkP(WXfnT!?=z56q=|O(c7WVGGFE-_!_i`wF&9u-{-`j2;(gGprnkP#Gimo zEiDzS!ye~^ipEO2lU!o5>|#VPunJ!l^c(V&|LeO7xvn_i@e*Szp0|IfE=GbIuaNSQ zOLlW@653=OL-CiSsBTklN}fvF=~}D>3Sbb*hlW zBZAwi9TCAInL0EmQ2bf_j-X7|#CW_!R%$}p>dSS?pAC}A6{;Dw(CXtVv;IEmqUyME zd+lbm2PUOJ>MYQcHd(FUvFP{IN9YWuMl-PH3ENLYF8<8fp)qIeH7u-q@$*LeVkDde_!cPe+!8TTZfn&_!3O?DG@@t5x`=?2ggDM=%j$3=Z-Zs(#2mqP^V5-s0f#x? zO6>ZG62BW4Ao@hkao*zO*Nu0w zspXVj$L*@ewA&7H(LhGM{aj`h3$X(U$(*6KFX0yMdt2GWJi$oowUI}}-z+yhUW!hd zKQ=!tC~JM|JYICP<+m9wo!a8B%`0a$7s~@GWlasplWK^Vv~rS2jg#3oY7-l*lz%B> z>W@|_slD|9MLzViuG*OkneEP_@iKO?Gd$eRWjZJm^ZA|j%_EPBF&l%ROtQu`Tk;D# z{hYOhH)r{newHM(ztP+*Gq&~O+bU+ac_G9?SnF`nxEi1(j}4I-%?^sZuBvHMl_xc+ z@ophR@89rv=1Ru>hQ9GC*1Gzfu(){AfhJB zK0+uZyBijWcGccfWrcrG z8jfLtwTbjt!zF~M_<>Jvy7jPOVX7}fQ1?8OiwA+xLH?k%R-ogL7n1<{WCS~bBfW|kd|t`7N=R;BRZpb$q}tZH*&kgtHee8 zF~DXbHEY36>N4S3=z8^J(Q9~FQ8U0tx-+A}dUQj4EwoW`Eo>p&gPof|L#N4vKF_5f zUhno*o+aPdyt(eD=%RBqb-(zCSxSe2Em{%d7wC}JunZ80hOvLa$)f9A3jC83&FeyL zP}2kf=%K2MLQGO$__ydUtYoGH7$%K~uK}5|sIUdletf_LD*RP$^LdUXD+srd?5JW# z^HB0PnC6pZfma5Q_y<+4C9I<30cpol3W!r*e5V8`xrM)I)!qBH&1%A zO2YpxvlU(!;`pSQsOS*>AikRD;9U_mAG9edCQzZ5$~ivIC84SWw=Wn+WoTlPMoULK z>c~H(%S=(k`DCyrfx1|>5|5?N!~+n2#znkIc#Bmk@5jz&Uy(a0Y;Ll`v#Kv|ucEOq zSTJ7kZYCm}rJNP#6rERj60O>Rs)eBRRj=@Qj@GC@6PB)lntqKds*4m)?8`{G%1oo9 zHcr{3ou#}MsxvEz6>asx!b!Z3>c29! z30RuAIG6CGMmz18c&4T?Py-Ita($j7tF?qb3-!Qh~cU^l%{SHYUZ4AhD0idgG)_KM}4G~J>CCHI_ePkIkOS$8Y0 zUT{nAHto2mLU%7v3&iMo!+*nn>FeCS!e{h@8>%YCnGCkFswk7fkY6*y#8ENHKBf|> zsP>L25u#J(m<9`iXa&ZX%uf1KCQxc zFlChE7-KH=yrY`jO}E-_mYrsfvGejLv2NOars+9y+q&4#JWt#B(EkK|tWAMNQK~iF zr!O?rg1Pqqcg-7Jk4x%W(k<7^<~O6dI~DtyHYj?kb~V11{HR&dm=C-oKI7){w%1K- z=*yTyc|zO)RMJ+v4whYIl(_=)RLpNqG|kFB<{T0GgDZD%Lf`X2d+Ycm!gcmxK5n4Z z`osM_kYsu7vJ?wC5DQYew4I_;l%H!;DH<#PXl;?qs@~ADfEa;jYktDZtu1MuMBhsp z-b5xnrao$1Q5H*gHeAa+#|*81n|hM#~>f;Xj+MC_q zi&xlYxF!@Wm{VaHRB~o^h|ag{P3IIvXvK?JBP3B(*E6=U!u&n&8xntei$&Y%C|d;b3S0FSx#H{0hM#m&?Z;XmpMsDDL*< z&hElL7xXu8EdDj`4zVdXZ0=t99~C}xmZDdQ6F|NASoM`o3RgjzKP#2?wAS8HNg6?+ zx969xp$%&b%nhNRZ~2fql&Nao7$atrnvPA)f zsG&;f$Q948DK!lM&XOmoKZ8eWAIok)7buTg?%lqam_V-~;_V90>ko@&(7?F|0c*Ph<#(Qs+fD!d_O4mh|TX`L{4X zp=WxAbhM~5_OomZpbPcIgCKFh0elY69O4PqL6MQZ^wRPrVYqp@Z_TjT@Q z3(kmV^2>y`06)eN@enYWd>^QS%FA;>HasNX1KNqCrSaj_C@FRna!x`D9gP0MfPh^X zBbuY)FL#pMYI$hSvFhzFZwdC01TFFO=CmR*tXaaFd%IY9f%y3%?;Qg$;Y1$nhz=pU=ilY)XUg@t&9#zv*i03pQNYJmn=k<47jl`%l2^p*+JK8{Y6(9U$iNly)3DgMk`_8(kg0Za|^ZGN>g~<+JiZN3&v=l zrCb*xy74iYqQ|`*G zf!issESH6QX*Np}dp={ZrHmTQ?68ciHn7K-|1R#~EHJMk^y|OOV9IFzI&)>TLh#w# zZwg6t*>v6i3OLf# zLo7_An(bfM(p3RjrM;RFi8&jX_cgIkBM`+Em+9|F7U8 zWlnu5YXdFFb&--q-{lIeYG;-^mlmyO9d`O=b#thWoyj!bP)A(U2EN(8BE%r9w%zr= zD(%P9dzz>VjI=#H5twj%3E^R%mJW_3J`A0g4RNOKM%BmgSoGD<{?P+Rd zB~kMm@6}zUy=a_RHG$FIpf1|Q8eG3Q>pXiy{p6(i+!U8OY76hF^F)YQaKdqD+&i(v zG15B=x@FHJC`{W_%Z0M;PP*Q!;`A)Jl3lf|1H;zWpzTWV9yzmZ4gYiPqt?^RixgAK zt2!Pnq{X*#C;eD+Qjs?^p(#IWH0xfYEom8NeZ!8(d%UFjCm~kBOxL_|??s=T#|cW6 z<4E6L@e%t^i>%Cjo|kTK`G>iKmHjI(&HjxQS1<1T2^vZCS#SC6wF5i8FnM*`+ke%5 zrGo7PD?ZS^wDm0n8M>A)nLO6m<_C#)+2@+xM?T`NXnZw!J-?x$VcbWNy`Jlx4Zd(? z5tM1p$BxI)^r{u6M{r7wT00u?BPYr;kZ-jw&}zb;(IhUBT%?6@-LR$fZkkZaV~T22 zvShYb$pw5OXJbw=PM9cDqU5^;p;5aP3q_g`yAlMTao)-kkj%@dU5XN0X@)UUt-}xK zOLm!Jz|gvQjTRb4xrpz8N71U0E67Ot1<_@+KQo!rfc|1_pnjIz<4mpb!S-?2mv~9r z_~Wu|vbln;WEMVER2;QMUI7$_m=#%&WSp1M0~zdP(C{V1JilQkZn2jMzff_bPV7s2 zrM?1$GHP&NFrB#@$%G17zeUAxE~kLwjihnsQC;XH-qY%>lJSCy;$AFJI6qq@9VPCQ zOqL!4>``lFSD}Ou9ljPBH*S!;3q9whS5L<75N#&{`DvS#f0s!zbP30@mZ-;y3fT{2 zVlkgP4qgQ)c#*=RU?*R}o(<^)da56+6K<(4Mdpb<7iXYyU|=>Rv4FM7WmpDuHfp&v z8VL_k$vn}c;|AbMFyeAwxlWc!G=q&*PPKM%FLT9)f&7WYrywR^@z+Q%3Uz{Z*hjQe zI9He|{zr6>9RPHRWt8pUTi|%rTc`)zR2&QULI<+eq2BPyBolfSxfXd=@(sNfBFA20 z>ErszPDr17X%)9|VqaLhKsDC#o&ArnMNj9ZiCoI7Jh>Q>CiB+;(NMGCCm1F;E*b{KCFuErcdY_a!Oeak6ER-3SjSP3}Z3^6BH;Foy#4(%?^&Kl}Vv zd{ZTujf^Zws%{W-i{uZ*T=sCR3iIJeus=Z+_n@?re~|Y<`X_UjV4AF^jwfWw?p7Wb z)#KbEOnel7mU#!bEY~H;!S{-|$Ybyf#p%hj5Smgn&P`%fRe5P-r`2-^qvd7wZPOn# zCw^7SWq8P=FZwx>!T@(nybLhxGX^>!&R-&7izg(==yj zN_D%OO#iMvgx+AH>YG3o>ypNuSI?QOnZ{`1>NMoq9Ntk)Q-x75NVB=nTUezzk~v=F z(2h-bAl{}eBq)8g^2xKHChg^MZpazkST7CsRaZ(FEt_>6#_e^G{vS;mb%6dEewx;) ze~xA{y!1E4o0xk2Ep89%uKpVR3nxK;fxMI3p}$ssmuUGqR=^d+=?~B33FZ2~6CR1K z=$}Ts1^Ve9PTC7DG7KK)4*xR55|m(lH(|8QHjFUF*ABB3s}I*5vP{M^sTJl=$U)jQ z^B!>!qs(mM3YlHz3OYC>?hrKtW{^#&UGZq&QRj*caWLX({@O-o?dA0 z&w0-1vTdWiVU4rZk~XkiHjlEUoR8K+`CUA!Rgkfjzup?0FhMxcawlTENN=%DIsjx@ za{MMhXU*Tew8%B{;y%A&lj*Y|t>#L@W7P$6b3>A>x-O}HEqs}B-StZpPh-0vj+B1g z`JE91BlZMm)Dl2*42mcFd5X)c8Mb)TB#!gJL6##8LywC|0f)L#s7 zgRW*D^GW^VvLS4GeQq9~v(0rd{ReM`D=ePLU+a`jFBeX9{2BbO=$#|PFBm*+7kcU7 z%eK=5WwLFOUQ<1;qg~an=0f`-X+7E8b`ttkH?{S-kVWZg9mQ^?MYdE^8|e$0O*L7} zz@{^$7|Yi9Hg`Y!eq(leCfC}qAZ{-2zxq4jdI9db75q?yyM~X=0K*+$2ui6Vvd?eO zZ12&BR#nd#tK3#IVD=ab81C|{0FPM zV5MR$hnsO-Y2rmEq$#iPd!{o~3q_H^gVl6k&KM?kS8%CqVEVS{v%-dvxUy3C)zMvC+yOGrK=Tgd*W9K-EgQvO}}bl%~N zGm1n(Lqe)Gzm+08N^b=SCcP5!89l+}>XGmZ1ALL(>?H7h-bm7}XJK~x0 zI3Ry|xxyX19NbrN0e(BCPKBb!25r@HrNj)pVV?Z6aj^I@bEJ9&@QhW4$AGWc4rB-P zn6pMa2ENX{!mUD1^9R$5&=!F|sYikeX=N)hKqSaNDlHOkPk$%#2Cm1GWmh10dLhn( z_Xhox2cgYl$cncZYtR-=ybLF@@{BUdP%ON`d98XYcH@naH3EbAqmfad7yq}Y0U9q1 zQhx5iZA@9?z* zB}}o1pv+J+bf0*mz-h|O{3P(0lrG>wQs}#I6--#NMQh-@?3?11h?e>u*n#HN90E6@ z?@F&gTO@6HaqtE#CjBO|KsqW;g({`D!-bNCvJF9>Frw*vOr`9TqG0eMWr%7rk;pua zQdh#sLrKcP+*b4sc8qsPLWB5%L6Qf855g&!fL$(1z+O`5-N^$ESX;JEi%}_^p~EKgHsy=n@;d zTjc~JxL&IL{BB;p>OWR6A6ExaN(2klr0NjiF?CA`DtfBknR7(^Mg1(T6db52nz0%R z*SNx(;c(5tp!di=El5y?YP$z7!hLi?f`aL{YcTpeol71^n5O5V?^sOT9AFW9yKXr@ zob!b+LJ%kKy4`jA`8?gRs!qW?-O=Jx!ZW(9ImbmGbeB_`#GiF9W~>EE^!>w{p)9>m z&`9{C-Z7>^Ql;N9coAW2A+`oo?+iQCuW1#gfAH<}Q>Ftb$P6*LfDtUCNyJ;hzG#}s zbmNXT1=oe~hz5(Q0sMO7z2fnLGsayxLxiu5i&D;uJ&nz=w}C37HmnuwFv3B;u(xr; zm`Ze;an;~OnAVs>ukr6w~UcCs(2n#W9!EYVqLOsWmq_)tx)X- zZml({(#~6Cxl^R#-?z-prU*SOB`KFgm6j>7L&b>s?zH^?k+4CdA(?sQXc!%9)(&1Q zi82Qflp&@|s;`uQ`eNA;>T1_+L_-g7c@tUO4)<<;I7CG$@FvI#_ViLH?I)5~RbXemCZAN1)#Ady6 zn|YV2vo5UJUq+%FX@Za;w6w;BqFwae4bQkUn0^ga^cEJg{vdf9drke+@^73^uI|Er zcr~u+SzGuj=Zch(f@jW&F}cDXM|3`D{j}lv!<^?iXI|SLdPod_BFI`9NO5*id&@{)IK7%B7giCJG6ad7R#yY~^*HGUbD+Loh8iRP|c; zEA+iOS-fikPct48`>xgXNAd<-Feor$kIz(xFV?WpFVrx3l;k|kg}%q;(60iEq`AzA z{BhEsEIyMf+s0l+G+pO&9#)N)_v6hdE|Z_&E3-8Uhu~4lGo`196dS1A1Ehq$R!s(t z6PT)Q_>J!>%{KJTfb;sHQiMq4{_=(D4Tu}#6mCSGFn^$V=tcHKG2yo06!ENB9aqY@ zBlYC9)P9n#;~%O#Ae$w4SJZ+h2)|@Q^2uU)$|yMsAhBZ<2oxFmRPhovPoOI2qmb_k zbv?!>TFA0x`-rT3qO4QBfyQx+vP^u^g;x5&rxiLwyR7qOsKX?=-o=}57k{b@^tlP zO*^5pr+=vXL@z_vI5;H?OT1pPRDVrdZO{m1(aEb3?MY5t~z!^2Hk`q~ZSkt2HLtG}1v9H24 z$P?(1s2ep2_lZxVzc^fAhr~&H49>$wlBYl_YpUhIGRh#nos!dQ3TU z>UYUH<=+#^uve-s--Y-LHGjYv#ShJXB9TjUd*qwBbj5SYc3!J;I=F^^SSb+L1b-=4 zvtxvJl`p6VMfX%wNd3gORe~}WaG9tbNC&U0&SVaNj;q6xc<^ksIhuy-Qs18X4PB_o zoKPnDp!w#zP*$cTDDr=`#73uPkN$vs9J^N=E*a0Aq0Iq(cx)|={~zC^)v}fd=4fY8 zCkt0=SJy~Io3w{Y_lehOx8yei$F$uui@*)qABl^g`MNPt4e&Ny%G5w)lg=85NPKjk zeHTh+>(dF!Kpk;*p-DH)$LF#R8+qt_cCRrPXyS$$)A)Q|jWL2XoG&-}QRWLejDxBl z2^SjPm3E0X7|!JVBR*z0kkJaPHFPB|1uqzOMKwWx8P-mn2!A%53`9|yam2`7*juB| zfHN{r!$BgEyNwI+QOsr5M%0h>&>9B}mTMxm zQ`Oa`^|ED*Mb34|D&`ZXk9ZzC%+bMvIbz3H<|yt;``NmAyd!p?>K^~9-Lpg^=&^0h zn;{xyt4zNs9&8($-~-IGZi+k%7Fd~6il7zNpz&?UB_Xfa-(U zAxot1YDAGlrhCISaVV>*!IS%i-C3_-EahBuy{a3>eeW_=G5JGWUd1;BiO#lMm$2B` zFMYfy%dtLTw0NPzhj==++1E}ffxPVz<2&FbwihG!NJO?Wg3`xI?AfX2Sp?F1L}opM z|7OIuj1V1Tnwz;?C+lp}F~$`3x2EB>%enrIs>=I3Qe*$(1ir0-mvdWSu0NU{BHUb` z8ebv0=W;~!h+jA#h2S8=88*HX9_QFQav%D_etp1M)LDTjl8(57Ryz#?&LO`Y79x?aS$`>lR6bSpSb$1ZGXwHa{YN?{ zIIm{a*0O8V*Xw>#w`k(1+?wN>F4~u}6fMAL%+JR|&6&*tK88 zD*s!$CE!ON4?`D{A908)DFqL1VyvtuMU6 zr?0*Y{p9h|I7+&T$jYsXiFmveV6f3F*$Ac;NRu64ZRL-`YuMMANAb7ZzLXR>pEtT% zA^*+KDOsx6NBCK)l`+Eh^d8kfao>bVs!$+4;*07z_&P+T&V)MxcB$LZ2Ubg%SFf;wJf(O4TKX6qT%wez!P~h; zJPx{@eiPpXpG_Dp&qLluypZ2UpNAld#aMR0W~E+wVt6m%$~5=8ubZjNBeK%2J|#_p zZi^9E1g`}`MK;6$_T#G1H1IHECi)1%YVS%~;FPLFEEDc2K8U?Xw&r5eIjADtOBN!D zi%*xG$A(2b#*bq^LI61`D-77EK=2lyhpIY7NWc4<-zwsL(eKk-!#crHa4B>K8jSQ8 zzJ#72^Ei*;jp!KqE(Au8)l$$v35l5Xe<`_H+()tiW8`eZilhV6-K9gM=i(EjyJhx> z!LrqOMTiiGFdY;CNU_Jl0+1oC;S@k| z($n zl&pX?_#PGR^RHsD>Q}#e>Ua&&MxtAxTO&ym{+6!;jiO%pJHcl08$}9xGjKuCL34ns z6pzW#kV-kNA{#DM@`|z$f90C&7sxy1_f$DLPgNJklkinLBRsGwb@pV3G)le6zgxCV zGiUf$`C84t{qCvyYu6B2`CY#S-6>e6z6iV%t|H8Jexgpz1okMgK~qV63ve}R(ju@( zv#5MA6rkBuNKCA1y0RnSdzz1_I5JxsGGh)Z)MiYNkmPAsPj+C%x?%p^((}5e;a~87 zbeHS+jx1=FJlKxHE7tmXO zy5JG`Rev-~3Ek0OOxX@E)?b=27n!ZUI6V^8>OV}{ATb%j35t)QVmQ&=Y?$5ep1k;f zE>6u)qZZl5D>nZpe$A(vyZFNd408i(kdSGnQ|^m+=A@biaf#Whj0^;uE)*nyV@&HZ zkAfdeuH?7SHIp#*JbcxZ7EVRFOeK>xqR&iZ{}Y(eR6hKxY_Eyg@1D$P8cI+aOkif0 zy}}w`n(bon6=bzdG42tOUn#?fNw-<=_q&hDt;Bar^%(OEPQw4xaH!f#@T4K7bc*nP{mwkXEL9&clO|s7I-Dc}Hn_&d4g|M5)nP4A zm*aczRV2Wn8uu7|YVRFBTpDjD{`V#4Z4QD0S^LBNS$EpvMG5R5t!ue?+~KXBjA-8E z7GdpI{^aI+RqcYQ&DkZ*!bwf7dAXuNjc+p6imo-1lC;2phNhTwpr`)Jv;$DzdY_;# zu)rlH&fW{03y1q)hn)TdCD;C#p!BhJN!IB2Rr%m0-Ja^Ff`4>RNqY7!eSGa?8b@DO zx0rNSzliEpzS0m$TTv)AEM!d1dS=XHo=V9v0&IMSmr>7^hwV0p@b?4{H|-a8`9+!? zK>1LbK_SHOI zN;FVehAJwMzZ56w7l0wkV#W`CsPZ&x3d>KG$u6NhQElg_Ypkko+_`1z)lIxR1v>RB zeoy9Sjb50aT&+QF#kDd7!G8XPQF@zqn)VjXb$@5@Q4;rJ#%A?n=nNuPNEiLuNyWzC_R=FHW%p5Ggfx0C}Dw5HMv3(T#Bxk2> zQ$pDFpm(bNGJn6Z>SVmdJ5}>te#!lvK1)U9i{Y2%5tM?ShA`nW2@VhAJir3rZS-Dj z4??SbCoMqTtM*85qRUHqWlBkU-UZn^$)lN>xE`}7G4Wn$MNFuCk!;|!4T>y0G3d48 zqI{a)Xw`JZJnv-nS!K5SJMHWLH%b?3?|?33lB7!T33((r$4Nt%U^#S-Bn`VmUl)S;x1F};y<7L%&dJb)&sV8c zNxhTQV^wPjN}WbVWaVSs8sHJM6{iSlU?skry#$#q52Bq!{*_zE$I(Xloys;zu3|!Q zv*aHIkXwRj75g)!(r{&TV!d>c@<4Q{tX$qWWr(0p`k<7(f7u- zD6Qm{v32Ty*h1r+paIe~#`R+(@u|iYL(3GSji24$$#)uwZx`AFrc%)#q9KISDNfX5 zeah5|uUS`9mH>OLw3_)~v(>Nc6_jB4tFQrfv#ie!h0j_fX)%c0k{163&9e-M+$b4p z{ywz_^Dw`h7>fNeca4pbtv4?mS|)dx+uh&cQ_RG9vbNG3A*vHbId5=}h{iYpW?!+p z)14A5e&<+FGY;r>B$Sndfc-|nBPhXc$&QA-?MbN$c%AKed^Do7$s@O-0vl_ppQOq* zd14qQv0fV+Et_i94=tAuu-3Z2lhOXyccFP_=_7n602*g-D}`kZdl+q^^oFFm&EmNF zlhw<`zw4_?_W)~M=ZR;0tE(!@3}w6eq#B@aPD>mJA9VZ^aUYrIAPnzlmt*FHQppAT ztudw2mG;b`<#@C0o%=iKY+E0K(rLLQY~#;vp>v)KI-B1xf`s;Fab1!KZ|bQI5hpZp zOHTuCjpy?h0B0H#GOvL8h7&2>P;SGxI4K-cZ;TiQpL9K)l83x@#Z0J>%yga`Q!X9v zcrvsC_i`BB-(kn>#QlpV#Ht13O!F$A3v8wn)jIZS(<{;h+G5ii@^P}*>{VA#!8S)y zP8U5fb7>_x67v)K>2$HBhzTWBTN>DTkv*0x+_))H>m`16V5_xT*gk5mbuBP#@G83# zx=+}${+1BAZ#U!qz+%Hu63l;Ncuao89%&3Erd~!Ht0`AVJw_gFY(=K=U%IO3qG>AQ zUJli?omH6DX*$5Z6t6b1xQ57QW*xtE3Se0*lmymW*y6@fvn&U|kDeXYABeHh5#>3t#F^^XS=X-A%qC4cC_m-o-QZ zYSFZap9Yfn(G-fI8)OC=jAB?ds>yg7jrMFcEytGj?KPjkc?4yYa;@kF@xyQBrK<0; zk1>s!G|u0YC7M~>S2eRVJ9w|kGqj@x6ALG3%Y?q!eC=UTOzJZ2Yq2iAO6LYNMtszr zhJvS5>4}GsK(#&(jTlvLh?acwY&K4ojqclPS}(s!WMzfwFHwqe6EB&&Ni~i?gYjK8 zPmoBNq`EBhtO-^piiVWw)kno63J0nG7GKMntAT;u)HaO`d=sCiISl=bc%>bK6ig}B zR-=u9xVA;|el(Yw?{v2Lk9jc%r zHKi*Qn~;YE5y}*FN0waKB%!BlSDwa($MIFeu(uJnRT}AqDH-ZXvd@8_`WU`@v_SJ) z;qa{2ZBkPE{xHl?6B9(nChdJek@O6jNz6M|qSxt(vR4wY)`0UQ->T-~_pthsM{<^Q zKt3eDBwe1lNs%m@l(I&#Sav>+s(6YonEqRtDi58KtbCv-4-~3e6bDB0)Gt+YJY8C~ zI<)T(-Cr8w)Y|a3?gD=eRwWzB4#!^0T4?j6R@|F>Pc{g5RDPE&#Q!Xricgi7UZ?3p6uB>J%e2M#7W$Li2Q#R+XyVKv4SVmJt5RWZf(xD{mSQ zo)bBt`OX@MBHAfbiNsr5Oxh{gtyNZB!fLh6#VB@HyD9gDl%_p7(_i{R`zCpyjHjC% zyF>O=N17gp&(R$ZAHf$_9A?Y!!$&JHIhMgHq=_tby!oS>M*hl!6gAA9$ z3uX5W&nIi~4#S=CCb`ZSI7+Md+vx4-R7s6?x6dk}aSD-@|Cj=}Fm%zfnDH5QT6h#5 zl5Lq*vkCcX{!}hOXPJ)|`AdS$OLJ_Joo0LbDy+yXNi<+L%%w3;q%Lz(c(JU)JZ-Wb zcQgBrH{-8O-w6uM^x4y?yl(pF_E~Yj^cO+F%zZiUz)y|{h7Q_e|Eum9thQ@vB9U18 z__Du{qqaW^RVdlkk^L9?%2t%VLDFH1OtfOb)(iK&Bvct?sqj1J zxq`otPNzKk9-8W$kk*49b6iL`CMkE2qRTKZM`D-``_KMm(rW2GI~=f5cGmXOcdndl zvkYFK_`{Y$P=;EYiA3INnavpoNSa#dOTd(-$hyAZ@5YPO8PLu~cIg^8rQue=L-<7l zJv$m{YVb%)Lfz`k@xRbU*W+lCB+X@+wjK*~MNC>Jo#1Q;SS^cjoFBOzKk0}fC^7af zx6gR6otPffW>^#W7wtZk3)%6u$JKplCfoU%4)Oxqd~#;xMB9?uo5fpf3n}@zUu-9- z|7HBL`_Vg+PTIaPLu0fKHaj`2+tI_72mf%W`9=PH9Sej8pU?JZ;s*mRIrlREf6>}U0-R8{=422zg~w^?K9fw}jsos8y;SJs!T zfr+oI7PdSFv4wDjVf$@Y_?f{U?eW41<9h8L;wYc@wrAkxfyW#uLfj%b!ZD%=+`dmf zkGH@~s>@`JFe@p`s1mcC`kl1Wyq;cBF~j^P!&Y>{;?3NfJIo?x{md9;3Fc4|Z<}Xx zpT@8)6#m+9Xk9M@?W#Qe-0Zb)T& zQ}hN2C%oo}VI{Yuyws4-;}!m6(D4m94-EB!mh^Xqv%+hMhYSs(?=kttwEz&d(3B3o z4Zdr-59|H^G@V9+M*L|8G53L8=Gn58KEEwB^2(rWF(cbDmv{mAb zWkT&SU~%Dk?Pahs`=@Rol$SnOHyut$_)8azjE!dLn$gs0+jaLPnZbYRo?vbMXLP@% zH%DABc;ovA9wZ!F!F_(4K$V`z%7>cI>^V&M!O)koLJbLC zUhuDG7E+lVsd=9LmKIOSpOq(N8YoHDY$i&+r~1Y7{N}Iqho|D+Osy zHpL&of!YuyPKd7vRn8W96elQmhz4`sDIbg3=?$u(l8K3bsoEshq7SPMOP@^MtL~T0 z9d}ftP(+N_t{bkzdHm6RR6#Xy{SVD?0#*7`_ysdwrW5scE|vW!j&App7mMdL9+NMS z46XH6cuQ85!xTx<;YBC~K`P04tXM0nN)stA$(#uXm4$L)^nT@D#i&WwRDFtr;|{9< z<=GMcXostQc>LC0Q+GowOzmR)5y>{`VYEz|A*=87miEhbwDF|xWFH!RWfHldrb>2M zexb}QA0q#+s6|dwaB{}UmniP0vE@&dz6pC20_B6K+ln8`SCcL)pQ;XxJEXdxhK<;! z>CnI+mU0aqVi}=><5R>gg5SLG4|= zt*wO=S*%aS%@P4xHmXVNq2+bl7Xw;z%M0;$?c9cTi9@@&>Z0U7?ab1n(rMZ?1^c8M zw09u?We@G0lzlR!E;0VD+)Gy-bwlpdRZY66AnR5@ECk)m5!+NN_34n06;`(k(#mi8 z_qgH0*~VeWgQ7UY>khPNgW+OJy*S;lw|=#Fy z6+)P)0CrRO5bCJhAmV{H>W7IQf%B{4#2p~7q(i(5gco>7^1<*dm1HG2B>9ar5%?PK zBRvWni2NgK2dop{$%_GGzzqcx$R4pxX#&DMe#>>nLl8^2aRxSo|HrxxabDoIqG2@Q z1ncPLy}}iif9q(Xa7%BcRy4!XJZ*vKn%$DSjl3ueQev~xXJUTK; zy1=x3;s@DgQ~uZw@_XQq5&tN?!Brl=WgEfykXG&nIxy?`EsjG7692A4*Zx*Ob)+;W z3QpM1*B%j;*~JwXgq!S9(^iQJZRhepk=|CB@mw5eo18pFeBXK@E=MA?R!7E2zFQ7W z94qrPe;fNr-fZ4H;va?5Jjvsabh8<%zi1kP_XL#P(iVpPV#l`c?-bhJb(FT1*x>M^ zjS5=?qNDn_Z4B~M*`Tco?J8v2f-#@7e%f~7{!8g}6cT>LqaB{)v4IYW|6%g+MjV-p?R>21oxqNt$^e*{*-lR2N^B2j)m`ozq33?l(&UgP9e`Vth8)G zhgPq%9K@_D-DmlZn^4$kxrhg{URe#q`jiG+9GMy4X1zkqjf7jT(upC>)-ERBzuqcl z@A7_V8{nOU@B>9qr;0UA`T}2M-iDZtdSP}VU%?e-08MUfHj^>C8l2`%oOjg?vl)*s zU2Wb<*ju1AXAr+;-7&k#yHZLlL#XTGYb`Q*dnDvZ&8!KjGk;?Hj>)k2aa+9~Sn~w` zpUtin6Cka8CHsS0Yf3|3K(S2G*eUQKrVt#zb*jk|zo~&^N+3L{I%LWujV>JoSCE?v zIKWd1kaZsHqfJi9GW|vWJC0&5WM)U!n69!5L#j-xIe*5anG5-O-uKN*gpjhBL;vq$ zX4qhp+53FUlCk)e}%uHIlE(5_WpHT2Lgm8>-MGoBVy8_=v#nGcO|?7Ec6 z#s{3ixHce``#v%sNZ~U>3IVs^&6q^sq3Fi2%fJQ6Zb&8nkwevLV7bZ%J5FCuOF}%- zx66LEDdN@_NUs~HXMfpU!w9!MABJ-&!RCUQ#m6xa% z%I+8Ys#eS6a+j!X%O7TpP`yzkCY7p#6boZp)kY;hqF8-UIWr_)1E}_m3D^3nork)Fdi3$_r%*rB(S)(KO|5 zRcOw6)o@jRdYZ~lH87Q`DpVK6HmFXk7l)(NA!=txjQT%~ZcLbltsx9MuXSnrJm9)w z?f-A$GxVd71#+flct?VKfo6QmN%>7pV?&OjOtZY2udrwqm+=*wHG@UDitm~yIVY73 z+7anVN}@J+Dp9#mJ3EG_nxvf_j!?0*8-q*K5xN5ZO!Z}5^RO$LeY&?Ea?NYq0K_s} z{~uzR%%V5IX30+K%`H>pf%@6?4!KCbplYXlyvD*N;+!x75s`aQuV(B#dB5DU!U3$a8QGCX+dD*Z=Dkp~TP5i!zjzynyY z%nLZvY?Hx(qje8u8-ZC>zOq*UtK_>p0jMw7B<}&HX6MS!0-dh2T=v5aV|LEY%6)p<(CM(~T=UN|k4fI*29FNb1}wd13KvUm~qEe`|Ux zoo~KdS0R0Eo>fVd#hU9&s%12Da{edTLDQA&VtJrxV``6_Z91Q@O)=EeGiAD>)szyZ zQY-=AO~5Ke;DCR&sv4Z(eN*)loCqn)W1tMu%0ob4XQO0_J+M7nVzCcv!b)z~&eSfK zM%j#&LMhEwThcC_Z5x{ZNBY`o&r->%t<01|vYl3P!frX;@+^9{++ZPvY2`1?k0#(1 zr_Gh48o}Pi-T)sj%0L(_MeV!J665 zfzG?-Y^NCs7L_}hs1G@-ofyoDv`4NY+^xi~ZVu6B$_TeNX=mtVR}IxC@E>O#o#tnC zZe;Zi{pbqh-h^hG7YG|6En6rZjpDgFV29vUP6s@%wa;;;^JK$e2Y}42KH}&^^_2}c z*qGR&RL4}TJZFvrgZE6k2Ki2oOMKSpxEz{dVWQj??zd$d*>For^lsu+%;oGpc&EJpijL)7ryvcZ(+5 zo)AKFoOU1L?6lMNezG<3f!#?3qrcj7=suyxY=4+Vf$MF%*k(V_K8?F+=m&d-pbx?? z5JUTzwL@NwY_ZKpdv#RVuo!8}LYp6Ue}ftFeI8xiW4%Qnm(i@Zh)armtW!w`bJW(+ zl=!s0Rw^wl@tSQk{dDwOTP(9F^nlgLE(z+lPUR;08Lb!j=|ewQFNnM$tvoJ)>XnuR zMKj`9kV>bZ)j0l77(7+Z?#xSp;fyrN#vr^9`h;+vM|*gLq%oJH`{1;Qg2!6 z7>g4=T3#^kL_e`CV!J|jSyH+FLEYvDyuE&Ui&_9p2C$&SP))?#BZG9`?5C_p)R_M! zUWGN8J4v6L*P7;%|I`nf@~9zIi%fpBxKai9l%8KW2|UHf&UTxIFuPLEn+~yVCOk7s zIKk0(%pTm`p<7K)`J;lIriX&9qnPGCvA5TA^XUJ(*qG#UXlFK!Ruy!f1|L$3+b@9~ zw5nz+@P}SncNn#t};96mEkQKZvIxvb3zLUtjo|yt=b&wVV3KJC5)ss7m0U_gDd#&*|b7RvE<5rfd zZmDr6TUoiuxRBFQ;xta@h8Lt55!~Nd3ym*%-&3~%Y=L*eWniIja`ZXCAj%6}1@wr~ zK_GBQveZur+>{OTdJ3k?H$YnKQbMN-K&gHMpJ9yW2-{W~4sf#?UmA9EKh|D0Y~k&w zxNF$YmrUDg*d>T9m}HnExS!Qym@V9pa>ejTG>~x07$LSqA2a4kfY2qzE~!0852^4x zKbi4_{E*iZz*m_JspJ9Gc_^OOZ0~rbKfwRmHdMb=u&MEc-X=uV4%btJzbi&Vx83E_ z7V4YDMfv4We)x3eW__i^ka9vlAdQHBs=p+?8@*ruKz1y2fnkKaCvd<(P;~oA4V#r3 zuP4xr73$?SCa8Zyk)gGBl<2hL!In3=2Fa9$Ke`gho|*z(sfb$;*;o=t|@>;_vF36opYYbYjJ)$^YowN@-xP?utt0C)T&BetJFDzt@CA z`bF1#gW@e+HLO?LEYEF8&=$z84SCu+`L}9;maDLoCur4*(?#F3JjJIxtk$ShWSrK{ zRGvz{uT`st#r@HmR4bz{YtO3#CU4eFQd0uEb!1JJpGbF4v(bB$UZ530_yxKcC_3~b z+n;D!Rm+>%nmX0RdWD9mZm7m-H0r)Gf<~sEUG!38Q(w(>X#UYeq<_>L(&Qz3YxZdz zao;o_G*2SmYZElTCvVV#T1lWwdt7_ZZ>g?USLe;qZTtT%On*oZb$1vd^qbpj)e7B^ zW|SJ#<<;#`&(M*p7O2`Np+iqIGSfOIdGSTx(XMt&B>TvelYhQmCx4 zjxN}wbXaz0=P1uudeX2eU(4R9Bo$~e#n!9No8LzUtG1ZuO?;(#WsVrPUoAIn_BE^D znKpSn)-XUw@wMB4V-QO#Anlmf7vA!-HNE$F+lof--it79&9>eGxPQ61C$!U49M<&{ z@iq_9wE{IWV_x?#%-W>w-OF&FVmEZ(Bs`0-b#qB4CYE=7qxuDmcQ2#Y`TWy`V9grx z+x?pdo%dXT5ZeD88>Ph^g*~fbL9Kp0j*e3e54(ZRn(BL9NW{Eyw);0Kvv{QY7Dtj*+*0U1g;?GY;BG~QR3CP|M|YR?yRKtL7JYJoI7}|nm5<+& zzRq=vIFR(0JBcico$oqD`4XXWdC|U2%yViPcLP2`ghJZ*vRUY&h0o^8RUe8KV3A*nMZh*i*P(56{Rn9^2s-n zraSAYdt(P3&*>!*BFANB(8LT!BI{Scd;0}0*=Lgj&)@I)&Av~Bh497FG)OCJ6ukDe zPC9l+bCuHzcc(twagZ>j>Y~Fz6qb=4Eu;-a!|WLHs@y8u2g=3tK6@o?UXsJUmjR1) z+D|YyM6m5RHg{sOeJD3E;Einuf3nX;+hbvc=T{q14DH6&Oc?>v$~vW>J>4;a@Tob% zzK|4O_uF1eMptdK{X-F#x~#9LK;b*9fTqb|Sl#rE>7dPz$(y>&wt#gf7O-hJ<0DAc zcidAG+@c}QbV!e>ZV!| zG-l;}%WJx}RBRbwbQMmpv@tt!IxQ^LlC;09-kkWUv#ly_S*+ahn>Qx{V_D2UH!;cr z7mf&cYAF{D`mD3CBreY{7MP3$;g3_Sg0!+Bb8cQ^^P`KE5}@QQaPCHGbdXlmpw zFNgyl^D)^BQ<)$y&1`ZCv!{ZlZKBc`yJ@Bv8PQ~VC0Q{s*wij<3bS4;-c!YR|%fQiB*1>=A{B4(Bo zxFU{9T?#f!h9)ioF_P0U2CzZ8B%&JJBilPM02In;0k^@Uigi9KL6TDG`5C0Epga#S zOZ^#&Hm$q)AKq4L5QInb#X5>5bIiWL(_0dggJ?0aB_s?%o$Fh@Pv^E1Fy|A18Tmevc3A9STn z&y7!{pX+BDm&i=j3L{ZItUT2iE!P+CHGGsm%Oe_gD=ISQ8?Gx>rEE96QwAn%HhL&~ zr}P`^Re|9sqg6F8B*u6|4IBH`$X37iS#H#7`i3SzzRyh%eytAD7b8>my6K2faZ7Q zE&W$*W@(T9ffid7tUs-F=4$jewd*nt>mO?`Ccn||)xM4Ytv{%n8vRLsQHKn>raz;T zhpg4#)m;u)uK!mbGjdRWT`%&A&_B~dFDwjH{VOOgGBh@_^ep|WT8X~Z;8O|LPcuZ7 zj@CyS5(*dU!wn_5qx2buX&Fd;wV@^%&=(jw66pGN18oXU&oc1B^Yt);AtY3fFxUe; z_05LWK3DWDhNE87^mM}wNWUKZKN^w@KO5HSR)NE7R_Nw|V=Gg1ePD3OEu9R^DWvOA zpnuL;9S*pc5vY>^2a}6*(}B0~b-E+KohcJ^4}e`^A9cHdWxj2rrf8Q~k_#4U=UGN)ztiq9 z-$;+pJ~O{cO4fOs55#@erJ3bX=X6!(=+IuBzv)eItuD+oZ)~P6)dcwX>q<@4Lv6Zt z5ZcG|BLL_HguVeNX#O+3xMg;OY|z}st(iQy5jM4a;UK2tbn&kNZD(2@uOE&uW`cdY zQLGeY-$%?qyrRDlw>>(iZyRCzfx_Dk8#*9J80>+2T}TPe8;LNA|u&cIBaaYtjCh;$9x+d-7oK zZMN((J|dEl(UoHbiJnS3tHIa!I_SoH z(V_00v-v7UY&iljcGA4*6P0%V;WE<6+kxGEh6yRY#d!S?=6Hon3O= z@s_r_Fu_qlAIzaUk{BD(0f&`UnWS@EWv__Ob8O&xMh~^W;MIg~w|Db5O_12jg$4fQ z_GZ!V;U{eh$uh_-%OQhuR+ia{(~y?URF^lrbDXB_uQ}+LP2X1;<49v%DA{1&#JpTM z-k!-?nA2fTVQ)_t+q<}tNkaQe9wiQDC-Fs5KWx7Rn*7i*%O#CbewK4m+2n(kJu*R%!Td@d>|bE9D9#PLXx^>_pl+*GDySx6 zO40a2TDewhuaCDPgv6?E))At>vc;C&qW#5>EOK#Eey#bk_-2;XyjTLJF1GkdQB!5+ z1=82CXtQ3nG0Mv_UjAtE9`h0fKS*O1D$%1G&68E=VHeCp)gd0gA&=%s5WZ3y0L3s} zVI9H}D>+*gYrZGNm)XtNq&JI4n~^e99^Z6Nb}>tCx+^E7PB(p%KT8}iEmTlq+e{mk zagkx>Fy+rlmrV0jjX?_YFtvVkJ@`%iWZ1u^F`8LJet-wH7)Xn+=tlitmvxV8?dDoV zPSr?ronmI`R&$=>b5WkjtwiNvO?k>KnR85dRaDAWQKYl7a3A&7J*N7y%l`$xNc|3WUyEFppXd; z>XULVf--$W#xoG6r=?5)J@xd2-{1uOUsJvT-}GA}b^{mnrzgz%QRTN6QG%PPD25mrso9%K)&fq_ER9r^d&tQ7-sfN z9tHTC9>%{mel~5Ivfp^$qzqqbJY#}J1Q<7%3IcPCF!0al@rLK7al4MYft;(;qFzh=jKz3v|1LC9Ur!a`^jsgh1e%LH;&-Q>X? z$h^vb2IinzO5YB6q5m$b?LUH@m}}^d!|5^x`_l+XDdhfq(ujoe{%G=vDL%c2sH4K? z^_-z?2tjnOX2=7+cU@#rd}nm~bH{nT>T2TOgYbWeW<&Tdq%ug$&KAne5)i+!$Zw-p%%ie(8pB$l(?jk9$0%+7-wz2zc#$EqLbJ z>pUfT=JmoMkqknsD3UoL{Nw*uG0Qx4Momh$llrmzOt%m1O^Hue1O0sg#ofoan8R?_ zFh8c7+~e7$$#B_lHUc8hNKb~`Re9(p~u zx5}Wk*b3wiApF-#C_`?3qW)Tqa9?JYmwj^US%uS5+(Xz|14)xtVyCd<3w3; z^^QYgkEoCKU*bz)-S*eg(IGju2hwc;4{Yb;!M;vgyaMa>)Y_+nPDd8x|Lwl&*cp|PKbC}vmBrpp1RS&kwi`f9TOyH<4WzVQexCg zd$x=cy4;SCYeUklFXcM}?%HaUHNJMsUF8q2CzeyH`w+fV4c#eBD>VOS8K)al+2iOJ zpDLZ|7?8kXH_OkHBv$j(ev*xt!o;&N;i@{3W!Z08l__WUHQv{1KB5M_o|vC&pfAz1LJRc`n!L2ozXLjS+bVY3=gEGSj<@fS z;|sg&W%6UWt8B{@rI~AO2NnOO9JU=)ViRXr|0vhQ!mJ-vl*m7}B6UURTx*xw9$aY+ z(Rhu0Zyl~3Hgbl!O6%wK*gO*Qsvd8$=%Csw$k#!88aPZ3FMnwdR@Ic0+lQ!Zg<6|H zbtUIt8(LkMalxil|CO@S`dfW1vCEpL{t??~m1@>RezTTpM^4^iF=>N>OD$Wp|BZcX znW$rooNhX(V|r(p@^m*m=7R_HRgg+P(nDV*;MBh=kF;^MsA*DLigsDSYU^+9*PNra zNL_jQH|skcKKZt_Q-@7hZ%xvzh~Zc#>pn*YS@!6CCjV`T*8Q1)wAAT)$NHN&`qREc zOhx+b-mAer{jVXTz|n>f2%lwWg<_CFQyyg9ZpbTMZCzqmoPW!@#yBDSqt$B6NdI81 zHHIZ$w+0)d5>{J&8%tssmQ}{0$Z-~!(P#1oOPb-=1d{o@F?1}-^xIhETM1q^dU!tt zQ;buF)I;t*V9lzVcr!?Ct_AmwZ7~tSWh1A76cFis1lR>;4T%9d zftL{edLSB#9mb1uS0GF+#_3_G#x_Ye7yT24vzf5_;5Z`%&quVWG=xv6EHOxuV*EKs z@>A?<$~!6^zaKk>cAB^hW@nU;H`ei(XQ;g;;cO2&Gc%vl$-EcGhHh*w#z%PwcNe=~bc-OyBiRQ@UWBg{ z8o7_O4%0%uo)zoh@VWaZCh=PMT_g4hU4o?9 zTiTnE2M5YJO3;Pw#huld8t-j-1KZxT|UiTF<5rBI_TQ+^1SVdm4ySPOAjx`o_M2xOc=_Yk*2 zFNmYbIJT;G3FQmtb+MCX;8`;8^o{%@u~ue@up*?2jTHNj*vD%Y&zt4bR80=Fj z6u4sAR#Pcv3hW}yqI1Dt&^OC(A^u|=7LG-2XWnFu$8@t^kj~>U93ONLVHh{1Z8PyC z4_@<%jN)60?UX-)BNHv>U>cGt8BT!3mI6+hEe}t*R{F+?i z0Z~TLVaS5JEZs@DCLzYGqSZ(ZAyteAvKzxsv#-l;&A`<5avt{v*1zU1b1Z7A<6*$J zEsOcV+S0cB{426murC4&|3jy@FpKGn^bx)wUO_z;twg0@4vNLCSvZ$uYE2#iA^Bc( zm^4CqG2KP(lkJFEMU9rv2qw}$D5wxif+BtT{OV5Oj~;aGd6B~YxS>(>7R+zjFJ^0I zxB5cUs{Gn>B|JW%11WjNFmy7cVqyytE%QRIM>oo5x8`EAbK41im6gg zt5G519c|yD*g}8R@vqXGu)p)Bl7x&wK2+{*89-lF4X^qayG+$ynCc6>iR~PhABDId_$w* zrL;}dnCWO(gJvlq8G3Vh0x<(&(L8B>hf--iS6#sX+R(x>Tr8AQ*-OaPI;Wf?C22PV zlc>A2Cn4{r-CB=fagWO0t?d3?lBFmRW`p z+Of9lh8X<(jsQbA!l$#y;M?4aoTk51H3i+IKT}wZjnNN@bauXs}m*HxE%wtTUOGikCIKGF5R$HVZSOa^vUicpDwxWI%70Vc}Q05xx!_QDo z8`}HH#TCxs?n$NN9W9nc<Z1fj=o0&5~PnMeIjo44of}wr3f(-*9 zuH@o1eecW*OXu_+)%~bY^=wphR|j=(5k=Pac3ogkum9~u9sI+%|26=OR&_LKQ1kq2#;Qe)6pZ4;xv<1SfCp+xB~OU#J<Dl znrZp86|*F|k!8x6Q{;;({imN1W>q@|ZLHOGlLkoSiw)EJlQG+xZufrZ$Y^ctxzup8 z{cQKva%#utE@!?k0_6@*=|pup4@8Gy=Q*}b07&`v;t>Z(H?6~G+(MseIo2!3vf4H| zZsUHz7Mn&BX2A!w6{Il;rEDfyhpOVApdD~L>} z?qTnwE-V_(xj<*8?dJJ17fw0DH*>-#sDvB1pN3tN!uUI^y!3=?cD>QAUA5lkLA#w~#c*GI}Nj-O|s{P)}8zWhK$y z78bC}nLAP+a#Gk;(Jy&1T-OApU5qGF+VFa(y5^X)nXPr-|9&4>`f7UMkR4^ogi484a; zl`MlMrZ3_7;Qgpi=;?&T^hEqmQZ>VjfRpzzuQp$yV%P~)BWPPVf&vz!lb4iwo7uys zM?Yo#6ub}W;qDUAyzdK^h|Uj`wO%J}aQ|q(Prhokc08thGu-XGPn)VzB6rfuBz|ZW zqm!${rZAbb^|(7M1Kvac*z-Fdk;Zb4G{=(9a(`EOQA_z_3K+EW0(9yKM!3)t{e;;i z_7CdeG)f5G_xV|pmHpyo6P@nPZJp1+SWdNXV>TLcJ5I0~l{Y)Lu~Wqwq?I#?^BLXB z`9@2_PUJqs`{NGsE_bR4T>i!8DWs`_gOzpUjlzuubZWEcOzH^QF7ch{5sU!ol%O7V zh%C$dJ}*p$>08lQ%*k}gnuVO#mhr6%xa;);?I(E>Wo5@vev;@#=Nf2+2OrrZm`>e* zrVC-Xnbg%QqOjYQ#JWHJ#91PE#|QD>(7f*Fk|Js}@)YC> zGY&mRN`&`dS<+vPXYtW8VMQ|e|T0TnQ)s`Xe5dH(Jk}qXf!JFh?pgCm4ibgDmic+kD zdt>~R0gY3z$CMrASMf8IJM*p)6I7)s+2jb7CEAA?s?HBw$(W^vdEaL(QhW8dtA4Aq z9E4h}y4>Vh|5A`E8SH*mnQe%$@2cLb z{|(~m3iWriJ`IcXM`Wj)p6ZtiOs%1M4QqURfgVG~cEI&S45E{%N5CE+>H3-m6B?zD zEsw$G>L=!n$Gz9*CQ}Gc^r2D7jj7lL57-Iuh(^eVId*7!`(!cCFQ8vN8+BT!&kbMM@Ud^%z)Gupa zSZ_!-H#Aw1e14O~a*65Hvc&=;HMTu9KSMWmOfYx1-|n1h4zABa=9re1Nzj?5jNEqY z6!3CVKOVY-Mx_xyfZl;?XcGaB_kGG;BciLhWKUPAZCts|4KuE+n&wigSv9Aei=}Dx znT}7q*NtLF6~orN!akpPuJw}b6Y5`>KUBn_!LzMT>)eQXYgy?|RGnpAjssH+Re}a_ zaH!XLaBO+QJxQ3!I?c<%5ZSOid9c$`lbeil3Nr#gGkqTM5JdC@&L`y^_ltA6ScjLI1iiNOEuhzQ(3 zLD-fLyG}k}>FxW8pVIQmwSh3B?LSL8$qRN^-%FOm*DE6^TM(dV1#L8{oZ~}J!uV1X z8Pl<+uwR(#@$=y=te-?{V;(1lTwnf(JBKRGyUW{6=O$D5KbX^_l7#6TpK;g454pF8 zevl^%CiYFnbazZ~UcrUK|1%TuI}o#U7m2y3I)#L^1pQLvL;j65vsF|qK7z88wwAC1 ztEGP;;X7_K>nYidi7Y2|ZTSt3Cw+FFKR1K9J{ix;WwRpd_`Te|aaTn*c^`+qmtGXS z?af8`A<2$!=u>DHFRE2CC* z2q@V zU&nZ)86OL-N0$*e+IGwf;t_cu4j>N~oF$B;)UrrK3)MwlLi#~Ff$5=iF{(R;QZF&7 z4NSU$^|O2oV;N_7ZayoSdm#y6FXX32R&jOm76Fy4~miG?g*Oc?19yBs!`lF4bP-%P#1 zomtjQ59670a~UB2N|J#!TJSTnl1&w@8F!gCL%e6`d*NzHeb?&tSlR=drDGiZ43G)` z!#J<;LR?_(klsUev;N|5#^kXbOf2>~hez6o*KzUaO@yDkR#-l%mtR}IkupV)Q`SJ; zB#g?wBVguH*ZErZGMlZZht&*U(v z^CerRE~mFh6%iE14cV)4mpMeabLe}XOn%Cp+H_P8g7vp-=){+YZuKK_1g`}aHsKkHCXEh-aG+L@^j>K-<(*PT#2Xuhc1A-dlBLbsZ;ul$I%}Xs3={^AStdkxK&cDLPfwCBiw~^u!CKhdOz)pKYTF=CKz#z64GhCK!RHz; zA^3=1Fayh^-G3NtJVw@^UCf`HeIZHCgJ@97M^ z%HdTt+*SfUsxeys)*i2WXpNH}Z%nkT5*}=ZS<>07TM6bJR7yMB+=@HZp)q@QzV4Kp z<~9FBwwUs2n$aZi- zzN0x^v!(04>{R`zt|5Y>jk)f4)~4nL7m31Y?R2ifUTv2-zQccYSRAZoR;SIrx#~Sq zVY^!N0FAT-W>#WVR%YT*{5#8$a3|@yB|hLSJ;C(C>jm{DxX`w!Y|Fr2fL+t9~MmIvGXJ zP}>~z%sR|*yC@+EA8A_>wu+Q%y%_M0cFZes#rYA^m z*M<-J^RL#|_y1uYYh?F*BpaIry%#ZWTg^S!pd017?r%+r9ml$URW)`#biXc2M4ogl z%51^}IW-BXxcLrs*c##iyE))Jt-&_M>jh=CrQH361h)EH50g)~eKHcLv9O!!cv>}l zo8&cJhfwjBGM1ro8EMRY=o;}_3dR|Ux3wMSmJso^+j;fm^pg90kQ$$LL$Hrt zm+(vE%LKxfihbGp$4-*9a&Ha!rMx0=x|8w0Ix?&tL^}M5VI}DjVxh`J8HcKrz^PpH zf4uRu*;pQZCH+0_H{l6$BB2j?o7GGzYJJQWkgwOS;jE<2D4D=LMK8^|%pb#KCwvn$ zu?=C1g>)`#>_o{je#wwu@&iJ*3ypOkF_tX+IMhS^8$u6;pu9%>i2YZ*jogYa=9(y5 zpu5o&+ECIM0+&9GEI{%Y%cor zh$LJ%yWRwAwmqgmwSzk#wPJx)M(scegkecp;{2od6>9Y@Syns zIZrsJCX#YkL@id+vc>tCpXe(jQ3>Oj)shEcbJ;O6=GY0mh4NU>uL6U7mQB>A;R^u{ zOeyfunBizazib5}S{NqSkGv^d!#aj`igGA3u<4?sxbe8XVoGNXp-M8mc|Y-*WL`C! zjFcu9t0>>3_cCK>&9bh9@r=>($k6SqQ3}-93ETsUAD&i zI?3PE^!P&Rd9^ik2ct)OJ;Q;}>a{7k(j@X-{V$N$<6{ z;)^JcwYNfd(&f65V^Y{M?Jv)-Y=Ty4o?9;hs`X177XkjtTg~f?mn8nJ>x{E`x$P^A zSVm)q&6rM#>?9aH(d&@KhW}u%(ZdYO8)Gpm4XnyGoXOBym`#W^z|x(>4~E3}X_N&0 zuh3nz%LdOeDXi1_1D;=*`}NW0QFZ>7L;BE$8cVIRvx#QDB6hZj%`EPwHkRod{WuJ6 zT0)!)&o#B7;}KEdHy9WB8l2MrpqGFZ74xws;C_Jw9|`PC?;<7wk~kW98W1q~IxWZe zW=txp(unf>%7`=UH1*V+aYpEt)Q@*i6*n6T?OVj7n_>1qZcb~HO$OB^imXqFp&jv7 zGipueWXq5Cw}@93eS-^i#4@D(8OCgu7ASFab1;R599>rJudZ4JyCK;v5zT3;axLZ@XeoDIqFrpua>fvr!jc^`Q5o{8G#PI2Q4b`Pbx|)F`taV#zRpo?mG@ zz;bX`)%Jcr?fF{IJ|BfgeOj--D5tK~AV%itEC4lih$&0j=J zt_l?_C;J!q2xn18qz{O0(nI4|;$KYuq_2`gY}zQE;t}_R#~<}`VVw0dc}B-H;{(bw z_(%0;>TkqCnGZb!l_~IJpwau7UzkE{19=H+1@1i7lf8k!f{*8%AdPEsaQ`K*tQyPv zMb#AM^MBBz)4POq%<*wlk&KO;^hu28d>X}-`}3h04l0eP*cwC3M-~|;k*1@LsjJ8r zFhx=ZH5j{`FQe7reV82j0D(o0V(cKE#<-Zb$XOjL*&&o0jb}N*H2=ybu9AMNFoRdg ztWLM_d)TpYM8N^h&q*IdCA_VpIMM|IqsJdbmpIzehI@i-Fu({1+@Q)vT#vsb*-Z8# zj^X`FL6aI8*Qv|N^GOS7S1G436Btpn#Exl91YOs7kY!{ntgK{TVkHz#R`lMeER zbKb?__+32nq<6wp{)Tl&Aygmqk%CvytbV^C8%8)cNH8$pU?nFi z;e`81$!YkVYn(NZF^EEdXh3&3P7|{s#A<3}%|} z>P9I$0Jm0t;I!d?7xm})X-8xp=L1?*@)cpcc5BpP(F2`Fke772u9Bdn==g*!>z8h! zWk|y?y+DUIKG%!Y_nUX?Aux|**Q=$|$%T5He}eKt?_?gNwduRbO2%0IyoMvp6Z(af zW$aS@`oap%6a7D#r+B!*FWE)lZt#j)BK&4(4jLrcYGC+C61FE;+7Hv!hDgS2Q*XsQ)@YNt zU^4r($(-rRWtw`D-T2WaTGTS(M3X$oTQbT-^^wWv82=*-Q@4%lOwVfL90l6e`qB2Q z$iYTeJD?0}dSLgGU@aR7SKsPZiA~N}N{O_^v^G(nTJO}i(Jj{Q@;A&NYj*x+)&Z+u zMh=H*`I0n_S8iDmc}zgHD1&^&Q!S-FG8tgLN0=o^W{HVk{i;KUU#)%FejO>Qzule# ztZY2owp{$Zd3IYoFOEcY?q`IN1D%zmiv^|JC!QW`(1jdOT+FX2O(wkPCa6UF$wi`cGUFpGbLCvl1P4LCK^IcDXaSdC# zMvL2;+B%6-krrylfAp2D@f}POiTtMhbX_HNR(nQyHNBzDlpo4?=KM54%i8ZG#v^cc zI9(!7@q--sf$<`-eYcNXT4i@}`5|6seQ%sqd4Fmi?o$0|3LTcz{xcaTzE5{dnkx2d zoZNGio7LRX<3>*;Wp|TXZj)WQHrKABtn7SQwwQ)?M&t<@9v#97Cf3mQZHYpTXZ!Go zH#|yPVqk*skyGHKklb{15|knKYsR9Aw=?c*R#v^6{tO;a^JLmD1-)+1G*?k?L(kN~ zoMTOlDZ#XzEiIEJErQnYN%L!Wlb`i$E`3j((tR=ypl5fTOy9{2>3ozhja}3k6Y-vV zro%fhQ3$jTB`DQx9xgvbO6L|EOR}=TVd#{uXllXE$nch-P=7f<+Aoh&Fv*Z`98gCM zVtrIjq@AKj!I6yahVQCyR%Rs(4QGETltRC`S26?Oqx>UDxyTja^T=~(e~Eom1-e;Q z>a|oiNI_Jn8Zl_PwOd4Lsn=f>(@Ed41W7Gf=zNEBaEe1h?q;s z?*JJ)qv5HtopYg*2l9D}f<-E_V0h*bC`b4%DFbGU4@JB}5LwcwQgxsFt=AI#FC|f> zY&Zs=w(JskQsVW2LSO1q)FdjVdxDq5EQUljSR!Yh5=2P5+2PEOvMC$`SubC}ebbPn zn9HxISPCo>950xuJSl3(90(qi#3qeX?UJ60cm^$#Ul~=5`~fWUTB7juyUW?Iv5YeHaYeyC4Shkk^O}m(L zoI6B4oHv*kqtx=7`LU9Vf_Z{g{s7?~A(=sBmw@Rxg$MO2G2#GEDj*|FFDD zwmlGJX zd=EV3@Xi%FdtY{w@LV1DK0-L9uJPp-z8yy?cp!es@Pw*XY}ItpJ^%%941)t)QEX$LQnrf6vJ;ih zxoS=q=%CxUcU2LjB7T(WY+XMA0@ald6YhuJ<$V%;g4a)=Niq?2VzqRGIyvHwJQ58X zl>;2Zyax3^k=RATG|{T_GK?qBfKsrV)JSLwq@^u|J}NvI!ElAh!JI~ze=f3qA)&NK zoK|FZ%R25BwNLFYewZ39qY5bMyLsb;%TYAFM|2$F= z8@zQtTB3DB(K)0Ox(HPwS)@yo?WFwDWeAgLQ*_mA6T?f#r_N*AbZE1f?W%LuY~Vn; z*(Gner*yk>VSbhF+4zHkwfe|}|6SLmhX0lX>yHPJWxw@@y%s6c^kl;QwA&b^Q#LI! zDN*;9MpF$qhxFYPD+?hnGkFMRQZtS3SdVD?jW?+M7?s8&P3M@mjO%I!vP+FKOFnT9 z8vn@I!W(Ps9Dj(v-MDz{5@D0k7|~DMY`hadk@1aNycQ|W8L0#%-&CZ%)mUiTs>YjL zZO!1|mIJna(mAb=b)8@k#ly;D(WrB+Q52M(YI)jJ%s6UURLy0jS*Rt2?0uH>oSj^% zB{;2*ci8-O>~g^x^QrJ~k;!~9fGWLcUgWh%KKp-led=pwp0={Vz3mS2w(&|^sj|Cy zfpeF16p7><$M0_Ka~xt`r_68&$?s@M_V0}c=m+gns`fC`?eWEvS;uWxvp;jlwoPf@ zc>`>%@n`ubte?Xpg*UB-18CA*YqQrP*(D3{wp42@P57m{uU#h)yx~NbyK+R+w9W#_ z@|L=e#r$EU&mF!@DtT@@@m)bJZ2Q^pn0BhoR^`X2alRIz#-Tdj$!cVH{oP3mfHjSad-h0Dn;W`6 z@#eMs>TY4&ZC%~9y!AbW-1)nqhI+G;Ua6!DI{qkn$P8}p%g$wOZda$JanQDpabI~k zZ2@5wf*xn@NK6vuSm?D_de-6I@3-WF{WKm_n>XDB-d^uAEeI%TSUojPe59#qN*yn% z`TJxILr6L?=}fDdj7=I;zm@X6C#Ox3Q6SEg}=EVKrZtRE& zs}c-qw~o|^7q=CAEs+*EQGychTx|HLnAq^A1_cH+fzTV^WJ`d8q+CkcCt3q?$s*1% z6`Am}tbs<*mb7F+?--3W@8CVm7o~TQF7}?>`D!kA{`h<9AN(otx6#?crD1j08_81t z8JZt5aNrq3CqQ)anod9)429C~EmG`~>>TL}M3o;QMqf2fbx>zyW2a{G99#3B4E{;C=fqhVF3L>BKqRl}5j@i?Gc zuH5*)K5=BQ-YVWh@xwwS+o*&^N_vuZfv5uMWh4lv%GWXN?7fPWtc%nQz${K$bEC3{ zyRaq{1r z>?kQx9u(cn*#)kYl%;)9Nu>AUPeU~Mvan(}0igN!sEweH$9~O!P&bjto@lrB5nsx- zsAmbfIWxgr;TrBN>1ELs-flsI_%Z(mYl-Bl;2>qE^oz*536uR2524}{FA~LVqaFI=(qSgr9_e|O>Y`14U?X$u9Wd*UyD&WU4AJ0k)m1g zGc6dvfWG*{%01xFuv~Djs@1;}#-Se``_Rwou|y)bX*S{0*o9&n(#$bRs+8W`i&AgN zcHU3v34XpHP=+&?3tQxD@>vn6h-+LSb|}tNO_TfuY$^t%yOpikcV(wQLE0$!Csjh+ zQy>}|6qchj!Ak!Qs1jM|u~)qYl@nRH2EU+r$)w6pz?)f{6%0Vm>8H3Y@#mHTa$Yab zsvN)^&Of5uNiGrG1#21>iUxz1D{qO5Rf?jGl3K_wdxTU3eMs|{ZG<<+J(j;jRAE^_ zqk5x%yDA3dd+bFd*fAn2ZxSAuEV@&b0QYCSR`n`@@K)e(< zwsj4k2d}FCNZjHLuDmXMi8zYZiGHa)vVA3q>RsbXq^)Ra+#}fz^l{ik#RTlDe>*rz zBk|Y^7vpIJB|#sKO{Jb!w?Pzop89WvKVyyhz33Rr3ytTtvV|y0Kfzg#{@v=zeTTW% zSMeue)s@i#CpNS2jc^%~R9U$`eZ&%grO))(3m()xCn(XzL1bi+L&k=K|;st=N+)`{Tc z*58(YW%ZOvmdS!&wBeR2b{E}X8BG1gcxJxUGM+^?uc`B3?=|D)0bDOLIe$NoZcfZR z#J^)sPFX3;G!KYP6TLBejL}K9nj;B{pLv+aJ|N6=l%QmpQ`H$QW1UpcgLKJpOnR0K zJ2C`%$`AWqwmS{A^Qj2^rQNG}Et6?mS1V@ivXRPU93Pv1{vmF=^IRA@~cqnC`ahK^V!@39>7*rynnOAxmD5|!bwJXVR-}D5{}=g3 zdnl`oYG_+Rc}M%w7TO%iK%E_t>_u#B{I5N_?eU4GdH~4ey2V)pQ zo;`DnQ9Rx5J7T%q(hI7 z_l~!I`(y(92Z9o3pGr_-tZ(7pjoPU$z@cWZDbW(VW!hvBe^_hSq$AA9@D(>8Z6skSjZy3Uo3WGcG`=e4pPcR~|T!eRYB*_#*Lksm{RZMUa}EE1t@XZ`j| zFF0p;?3XQfyd`q~ob8fkifU-XZn#0UtBD4U*`Siu4i!hTJTK5JS z!Ro2lirnYK6s%Cs;_lA8k4EriDf`fG!Ywfpj3I6fS*}Tz`3`@pJ*K2KjC}^eV7^aGVmYP6cSn0%YIgO6Kdz?mcM~> zc=HQpBjW|BnOBh&!nG;e)oaAPF+8+JN)B0!UXk|_5bs~5SB~_Dv>Uy z3sohuxr|xzVRDdpOn5@UV14E^06CmE+79JluCV2{@-}aC-Dq$-|6%!E)pX&wf-b0A zq|ZD9`-$Hr-$X`8+hdr>N4Ys=XRA#>p)%GJJ?&^fAU1McHz5TXOlz{H*-PlR^fedPlY&R-)vBc3GK zE|@Q|3)ivpq%9&Lb)SqS?rHumr%2js8x^_rtU*2x@W|h&-~szHUIQ)4OUb8{25@OiBREa9 zJY=To3mofv9yx%_biayyMgO-)9)LfG_i{UAmw=tT6LO7YDgT=MGanW_QA}cmirj$; z$|kWt@V41S;-kcCrbpt1^C0=P5pne4L)$_SRfgVK|a0V%M1Oudo}zYduWUR3-0 zo`ct-Biyf``!QmAgRWHD2Ya#)fPR1n=Nkx%zwn~K2fS1KT2&3xC;(MQDMN*AP*Iaa zG#}bqeOtU09$VTYIS8-K3zyDE>N5gm+mW|PZ{^q2-O;e(7n&8kNx2d$@;wK&W3Sz> zsCjrGk;v`(5fGc@i!>PJoM?@FBio{hjE3Y-G^>L*0Soaq-?OS2T37cgNRF21fx=%H zqErVMPK{oEfO%RoQ?!xorP<3ha4IzaGE%vE+@tjPI^r<50qxAFT zQ4Ep3TjbB&s_*9f%lf8oqtD?a>MgDDT)BRF{Y&0by}j}h|At;)%olvuPtTbziqWr6 zw~8_S--)FXyMBMvEZGP{T<~W34@3L#C!mL6mHQR&mO(^h<$uO+;B@Lq^Jdu`dYoAz z>|(T;Q#d5%Wz#RZC)>|-iL`)2GHt7`=1w+sR7UfTnBb!I{0}Bp_AOz$sbTz15oAhE zEEoS}@{F1zg-ztqXXUjfc=!{g++=aTqFihu`X4n`vm59^nPvY=Hk3Bdo+WgpL$<%z z=NU(AT$+vLXB$Eq#%5WM*SBysTBlYfaUWTminj6tt%I`f2~f-Z@wq0xJ{5Hq0?M7h*m(tdj#p11O81soxLm>eoOK&DSJ@E1_l_S_)$#XoyeDZlCT?kUYy#(}O+ zwFc(%uB`HL?7&WY!E_GP8JZ>J-t91_#qm?yzl~ifxZEBS@lZ6oZC=nj=`iQJ;r$e` z&Jy>l@;43{LGiTBf_>D(8gzh{dQQ^_@ki9Nc^Pjb+DXc1=3&EHFOrvHRtl-{pk^d( zc-2cyFTJA#!C_`|&OQ7S>(}_FS_bDu!YrLPKQ!`+PA?o6G*W+55Z9PGeP|Sk<}CxW1l*MdXkuad5&Jy(1-Rg{;ax&<+H{X z_hO&e+j1^x6kOT(JDN58(Fv1qj<6x(t#+W866B|KO52B~8eS{XT|b)s1&MPQO9E1> zIthLzFP2|c^-^Aojza~s9PT9OCEdDB^`?g;X6hRI^Y%a%i&dB(Tx-gRxk(nnGlRUbV~PVq6SYf}eXbu2>2MiAsX(8Cjq-1-sWPSFFk#+B zfo{$kPJ^<7`zPI7`H^>tv>05?@2lsj#DZUy*-*S_RMCFux41g{Ic$;0(mo(e>Ei@k z?J4(*xUc>P@Cx!ok0>2O6Yv%l&-J}tiYzCxa*M`AIZOJ5mn1zRJIWs|xGi5INM;{b zlnCC^rULH5H>4!srg%Voy>hPjdgWMB_4t7jY`sO1Ucu2QM~HmwKpLYOhNB zROiZjWzV4c!eIGU=uuX#LJLn$8wA*pkz=PQ=OPCqu7JJjB|$Eb9X&iW2Kk9e-JW5+ znwLaY-q(A{fAW^XF5+DNa+tYFqv4nA5qqvKL=?c; zrAy()aWCp}86mtEy1Ldc{LeaR<3^#EPF{Uh6sQxGTEzi6WBzH6*z`g9qzB5R?hVd$L9r(==Mx$!;^g=FH=) zHy)!;<(@JA*(&2bGHz?I@!uLds-uJ>jf#@9A}=E|@2=S2$j^8nNi|YaCDK}BW!xQ^ z#aJHhFaOuLg`f;J9vd2?I%)jw_DmJ=|3>Nkrkm1z%tD*D;0p`2uHp=4FSSpII(7bn`td-Bm_GsD)N?NtkX)$O{!^Tl!}_7USj{DSt?~=5=xRr1#A$!$-=? z&HDp`0jK%!kX%)Od9~YfNOVS)hiF2C(W15^}+1;!)j%_pt`lWdZd1>#k{%${cc6#9|w!>m6TPrB2oPS)VAMJ>(jv@ZX;J)DM4V?`HN z+S$M80B>5yyqt~vvmI^|Sc13hNYYQ?_qM*+H1U$Qgs@y`z4InGbR(=vhe?3|wyux3Mm8Jo0vlX@nj2Nb35kWS-Xl8<7tn0N&+tY1 z4$f~x!uW^&3i-tR+PX(=WM?&;SAXLOt3~Jx?)p+!Y#{GZ{vhnUK$bZGdn}xvvPH91 zJT)#rb6IjctOVaE7X;9?e8s=sMnecVw*MF7eRvWvQzZ&LB=b`7S>D2G$b~(EBZ3xi zLg+j=ikr}y1NZVu8+IZ*zN~5)@{d4V@?H%K7v*K4L89v!NocjiBV{#KAzdFg4BH_) z6;_~WRP+T<@HtA8w?Vf-HNAhI;UV&qNU_h@YH5?w#+@Np35F83^2^|#e1vviRVq-B zPN*&lrS)H-X`-g8DR79myd)03AmQXCBBfGtMig>SwjlYedY1e^+#r+=M1*ByF3R}< zt(ujpQg5C10rZui_@M)dL>_?WNVX~T!f3%@AXDVWE(K1Cj?w5!rFaFY791g&T>lU} zEN!ejr((*|N`fFa`S84O$RhucF&g#-+>?*NM}g0=W04tPSXd_U4Iv0<#?s+v9}zA> zEdBd*lQ0+JS;lE=C7!aaQV+gNE|e}|tx@!sjinw}oRNFA{0$i8d+Otr!HUAl1IoL= z(Bc+w3a~ntq>5L<8NRBEU{>-$Xgl~iHU`#0ZeimQHe4Feh-M+re1saR`bqyj?QHBB zk(Fz8iQ*>7Q^f<`Zs`J`fYnc?1RhbN<%vqLC0gF6{7{D|wt}Kc7XSet6;~<8shl}o z$|F$ZghJ2?olf4Xa=@zC2&e@q4sC>6)u{pX>KgP<9|5)&o7TS%KdK>i;>2Xy0iuoK z397w3KgmESlu4Idhq@_NX)pAn*&)-w%vxeU4sWffQ1~H!MV*TK$fO(xFj?)Nu2Pn& z^+}Jw9JF6-m?{EY5?T+HVWxmOBwf?%BS079rv7~zD^5&1(HrzXMbW|~>U~_B$czqT z929fV2J(GL0XnzozBCZMSvySn8uKonDm#G{7M_&P#a3kRQ#510(v?7*rXd4QeV8KhjRD5lZ%G^U7{ZS7^zF-ZDhHD63DNu01$@i6TXNEy)`grE?`H5jxoz z0Ho=@jub;ueT}yU`DEbs?^B29iPIjP)cCh>D8HX!0f)!`W|&N$FX%JqTXzYc88nUC zMb{0q8j1LCLwVU<$xcH#Q$c#-I^eHW!}^nC%kJ$s}G5; znrq7v#Rtu)1v`n$xbZv}MInwM#Q0mN)-Wu3s z`q;luHOoZ&E44=R8vbL>H+vtui05WsM6>ff?3tt$0vFqZ23+vo)>9oU?6cLCnnbs3 z!}I5fcUhlLgeCi}y=flOUTaN)h)Ax8s44Pq)*&I20n9qm-wqNv>y1OPmVf&9f$f$~ zf-=gY<*PV;?e(k^+%av}Xl}glHjos`4{P(Tzb6>sTvMeLMmg(BE(m`+p5;9d-E(Zo z+$jF&s2GRo%vC?KyX$Bfsgo`qc|n6@pOY5JR?8y{|NV-+Q0 zmIe+Kwb$~UcP99>1rVnCov@A)zwz8=m``)yyQOd+Pktbqa1Jl(YBQ0^_=^_KaJ zU%Gp(5tRPAPh>$8F@u2eu(rScBdwvFV^GllEX0k&nYCGMMn3Dxcz@$I&hms=rfa-2 zQQJ*`;9Br8GecbMchnLtz3jQwx=TUWvTXSvF@exVL1qcR<3Ov4TZA7WKVbmcq10Hi zMk}I8nrPbp=+|qW>SRpc@=Ez z|82Y{t@Jx&Dv-x`Zm~#!vxF_{J4Ea(2eW4!%njbBZ z`^)f5aoKZ|X$Ht9Y*`249wIAyu;GHe>c5#yoDpa?i$`xpH?qN26*iL7*(k?`kTnM6~@zhB_cyc8BpoT+&&Do%^UdEzq(dVH@mJ8FqGMRqTEm-eJ$q2E5eQ90jp zqv5;i3SrB#BgDiuGp-31422(XZm{(TlY5PJ5;?&8i}as5g1@g}nfj*SLUkdUAY4MxM8UP4A;7Oe>SB=RL;uwLA+{vqNozEGWjV3Nh9J?c3P(??-M$2dZ8Lug3!5Xus{6kH{U*b-E#FrOOX}nrQhf%a^3Fmt!PA+GU{{rWTq?XB ziW%F1NZ_!@jR@iQ3tof9siXY1Vb{>e-R& z5O=gHxL4hX75i;L-)Is%*WyBapUZdcUTr@jD|hK%b8gA95dniKdxab)ACfOsk7*vD z;HYJ_tBA9i^%ZHrI`!w`G$j{p%FR-KKzB~~4eGFgsjVs!riotz`C#88PD5`s($R;I zR@~~hS^WsVWxo2+HFdJ%darV$e40MK zaF3!;Urd;hU+JlY8F{synPOG8>1W2B2UGOBB2KER4eq1=h7KBJz9$g1VX5aD)MOmv z@*VXt5K}buzNU7zw`jTP9Bq!+YMRsPDj}J`#%gJxsieABdeszFc2>5|6jRtw-e4M? zy-hLHG$>uHxMqBlyct+y{1|sZIn^{E{1rIU6drUB@;4>;o`Bz&%${r1yG#_9??|PI z$QRv0Gt4pyx7(U%UZVN70VIuBZr#<;Dv7fSsy9dnTE~@zNUvFk77mbgTb^ZokXKj^ z68`0Qi#h3@;);b7`vtgWVTTjGYzsN)zG|kW&i5pYTUefJkujD+m+#OKOC&)_x0JI6 z2yQy(Q+tTsu9B8dqA8BY_4~wCj-Kj4l2k`dDJ1z~|58vVU2i{~l`2!)4da)}dG_H+ z4;3SA|Hgg?qHSBkhbk*<*1(_OFB^CGztCG-u;*I1$@s~>ZYU`WSN z>IlKp4t5JF+|e6skQ|@FeStNOO@Y5bm4iI|Aq3jrc&>w-cD>6FRf@fapupC9+`lZ#>tmTQmaPq2 zC<@E%rt0Q)%e&^cwf8N(r0$CUEUU=r#Q~Pjl&d-4tPM2PgoV~$3`)uX`ze+!F4Gan z5rya5+jwALmfcfucUXkIRJ_{#rc*4tOw4WjLun+|RQL=x#B`y_!RR)riO#Wv1!KsKN`VV=c@J#C- z!KT1;>q*g@VWGAfX@~ni_HPO?k&-XL6U2HIdCsvJE|L1^eTH{rGP&N+N15DYHWpKF z)qFP2rl(c-7~e3QMX4qm^JdO)(=m4C1gmK?=R@)vGlO?EF2+I?oC{Ai9}#T{OtF|H zzlQ}|IdYo&4O-WFEe^(Y4vm2V~%PMP1`+3F_xygdY5NkK;#{Q^xqsG%={a9La z>qWhg-qqxxzr^^b=9VFnX-{#>$Im? z`x>X{{MnCdhUrM0-{rBoDZJFe8r>p3Kl_93mf%?WJl%fb+2jFwi`W|Huiqv)5gutc zAv+uxV_2l14ht}`lvCZWndy)#k(*V>cVa!NX=EMO)NovBbj^J3K+<{54PL*7wfIE- z;Oahnt{}RMf&VG^RX9`PiiQxI~c|=Z-G{DB+{Dp~{PaV{`*liNk#LtaV!v#1#fU60z1GGsCnH*ePIP(lgBvB^n#0nFUS{_tSh1!EgyyP7C3&N3RsY70!SbLlVaeE5_(EWSrca$VY=~BkMhrZzw`+)Va-&d7od1|g z_2U_FDu$9uzOA~Ve9#O+tstka26{x)sLX(wDt5_o_`K?M-T@>F;!b2BYv6#iaw;lFYYcjiGuKNAB_waqRDls0Jgbj+x zRtI8>!(!EMu$O^;SS8LG=A*IWbpwxSH|i!5lt_I7K`AjDp#1`z=tE*+i!T<}G(ou? z6VyBaCt|ZJMuUg2tHq(J7EM6zYt=)IIuw<)0kny333&46q4g9%Ek9vqdZp00m4 z%p3h~$RBuAbHF%&Fj~U@@0d<7390MkLSqg|su*lcX>=oMPP}Wz10{x!@o(WR;$vC@d*v8m&S3?3}}O~DzprqZd8v-M7|q)hK8e<@rC;( z?2&N?;hH>aiY3x>yE&H{ATwKfTmF$JTEK=>`2kB@^<;(6;#IZ_@HW3MTn}tC-^h_E z^UOykd{W*wZ%?fSr@N?^8f?~FAaKEG;YONRp0#~$zAv@6?Wy}E8`OqWRm--wMVE5rp!0n}k^HT5ch(!lY$qdK43s;bCT{_5 zI?l(hQEqe0h+G49I5I*yRGaJ{1FB)Jea4V1Sj) zJ4U|lbZuy9ZgyO1JX-t3(brs3Impr6^1Aq?!?$%q-fYK23MsSPQBVCaZi;gkBW~=K z_Ia$zsN?Nb9D2yEw!eAX{1-d-3SRrDo!`WB-Cnn2^8UnZRlSm^DQeq_B+yseJsJ;? z>usN!tD4T)wza&jy>8>T$|~O2l;mf{7i~+Z+T8uNC$y`X8TJ8;N#m@J8LWk4PdcY@ zx}yGayx_HlY;`OUNJrZ2eWF!9u;Yrf$?bJpsA2_?o4ZsY#9E`yr;oD6l9F4GT3)s; zXsWl|q`235TJBM46~UI@w7-jfT1AY(x$CS;nBvSB>pQkf>QbADvuW%B+bfQCEa z0WD;`ZI4Ji(qvmL+3f=oK8{qkR}PCZn3#I%2eF8?AKFOkFiR+!)mMwC8LxqzR8^BpPx=H&XS6DD5MahMTIi!Oh0S&;z&U<`^}x zUp8OFW)qYWElerFGekpLM&laM_xfo3f%sK*5dKuMvMgO2DuoJ1Y3Ir&WuMeS^3(~X z+NFxnl)x&rS|UQX%2 zH-O9HH{iQfiz3(Kzo6|QU0NHwVx(9%U!6F(L4O<#cY9{!Y6=L-5-m|pY96c4Z+(nL z0ko#mr~-IbSBfrCVwFAUFEFZP5ta#V&(~pkRc_W}?1buhI#=TbElFOlsem)$&T4ib zqau58KlShs!bYjyJW`0`d)4jp|c6(}Ag>NG$gLUJH^}NBYwG3IPdfGG=`43{& zwyEo(vlS=R%V9zBHT8S=a=rr1N0PJr(D}&g@m=U)wLWg4E{Vu5p1z!*3Xmsg0B1E$U-6XOKX&u3`|v zM^_Y|LbjqG@|LNGVD%GA)hg^_+79(zjZ4yLG+U#HeSz-7M@RgP)#KU0i!_1wV}Bh! zT6@cfqixmIx;@q1CLGiK3^xoV1nZp9*zya?z=IoJLJRSlngHkrKBuBT+>9SD_JTLy zuX1lAk=oKs9Kp1fv@OVe?dHT!YL?C?_Nn^0juZX>RqDvW3$X_MV1KQKq__KU@Y#k5 zZcnx63?M-XHxjpg#w=4)OQR}J-`g-iwN$^QTBiD;|Es(X;^;3F)j_B9PjUm`d_zbk z2Cp-W9`^w8Gt?)3Kza>xV!hR44QTj%^&!K`(RrOEuX zxlC!X#nv5H-nRKvodheauS?&7o2{1$?yDlKhjJLIHP+1&o^d=w*ehtdqPm^Y|q`EqGTKKHrAC|I|z#1%57cQxwy`|$UTemBSem z6x+Fxr}TZ&5iQL0THQHHlG^`U=QO#3n07u2B;I2kQAk(o`1UJ}bxpqQ&Zd8BOWHoQ zBvrb%4JYj>e%jWb+>|$~ErxPwqObEKEpdEW+Z#rH60<|iDvITH9OjgT7q@@n#Ri49 z%LPu~hix{|H?Q9IrBYk}ZygDW`$Sf@s~kjD9#hXG?Ql+MscM|)_(gh98}8UlE~^;t za8i1U&pKw(vhpmBqx7DM{T-7TFVb3^Pgutj^=+3pJ7dXh5Kj@F?c5^Z1qC}7iE4cB zISxpiUMt#2^1%LI+s-KYL`vR=b`vX#E+U25caqmO`r5@*_nKz=I9hJSNP7%@Q_&ZD z3*&q4Dm$5#n)%LN%U+yT>gdO1B!Z55-mlnN$9O?cc$$MMVh8=OKbEBW-nQ?Ssl1ju zQvt93U!9}DlSE2#;CIBDfh}#BZ}X-7YACf{U}V;$T6Z#q;QEXu@2WPZ1J~nSh@9t2Z~)+&9l^S;PN<&KX-o7AoDEVj$D&j%Nnt-j)qF)Yg~DLp*W8Z7$}^xm4ISnqqqO3YyQT5SEL%I*KfHU$2INXf@2 zok-+C_@w4;(>3mj`W6$Dx3#*+c$0s)jBI=@I9mA0_*8f*r`LE!^f=?a@u2waxC+x6 z>A1uy(@$Aj49Q$9-ya@qZU(}GUYP;q6yJ-MNL8ZOLQ50euYaF)h}xH+1Yrw_HARaw zy)*0=hSzl%_KPB_FvDf>@UmRP7V+c4FvCH~gPa+LBhq^r{)U6H4db$ngX9Mjq{cGE zo|qcrRp4j1zi~5IAN16?LN(j>oXH>d^IBl`LFT!>w|Jq%U5rJeAu5+Fi*?kd3Vn;@ zSnWPNPr9UXwZ27GS_Jr$77_c>_#4u7N z3QN)VLi)g81|N8??`gv$b%EDBlP6l>`p)d5NhI=XKK_xQjM1kxF4hfG&}$cHzbQUe z+}3#m%SsmM3Y6l4OkF)VAnS>a3C@|2sGF}UNo~@Zp}>Sz9Sl8<=IXlP`(g3A4akPT zuX>JpnePdM50>CH*O-bubbV(^#|+w{cl(wt{F*5Im{h~(G!XbzEwJDlofkU((@h`rIbv@c;UUT#t zbc6njlYbat~srp5v9f}w0lBx@zvUcf!)uXG#uD`t(^g;#qK0Je?^UbD5UO}|{<>WWO;2nuMfBPe9^mfC7W zZrM>;flRTiC=Eh3S!Nd;LvC88<&>*)Et4~rs%Kf+#$8i?v`k5Sfl4g%WACBwET+iW zSc^q4hK_BwP)5aQU<+;7d%Vnodalxrv~;+>#SdAOM6&#}5WkDzl?AM&z%%S;Dx2XQ z_U)z7@Gbj_f^*1F`=;z}glnHV;V!brE*s~qjG~GJJxQxyQLcxkPanP zR1`rPR1g6X0|6C5&;HIhH)o7JmwwIm9 z&)bk?_S=6j-^<8x&@wJd&vp#g7fJVa`lD5xZt4P4-AN~QomY@gpL2&uD`tH5^cQ2# zOz^fAILIjS73CgG-|@fA8lKJ*7)Yyg*)>bgVH!$!1^?4w_N{e!BWrMZB!Rhn>?}=1k!T76r@GcwS36MsM8sJ z-}2w2`TDDH-%C>nOkfR4tqJy^RZNu+jU<~&lZjx#ze&3tjn&6f$>X%Ky|O29-a7d7-MlD<>SV;L3Tr@;lo)LZ&YAwZuXb%e&B||ia-{s##6h*w_E=D*-@vz=O zprfI*Vu+x))>JgsSYQPq!sxGmy2TKe+_4imf_BUv&BTP+Q6A(J7|UB zT5%VokKq*w&iL2xtt2vR|2jy)$L_)k7PqhiBEqouV1QCMvHmH*8va5X2Jl3*tKS2N zBE6Ju0Qx8#J zu~f3Akd@dG32VrgIBOAlC}}(e-v_8*{54k{R6C)9#TQzT_>h(lTA7qWItL(=UtxEl zNV&%LFC-ZI>A*Q5LuR88(pVXzG6-SpoIVlwDlSQ@3_KF|T8#i)7H_U-0`80dCldx9 zPcW7+1V2ew5tatOORVDCh43Uva8*G9lge2)8|&>2{fvk&Y8?q-ey--l?@$b)}F97vZS2&fT(FH{8zV0Y2|??e$x(wsAiPriXz z8eAn`LhN)plHWrdG`=UFL26YTQ}`e=^2I6IkQr%IN)(h(43p9i^%61!@j}78OdwOJ zIhM%$0NTME3nl^ZXym|wfC}j)coXO)06{zetf~kdf^lMZae;wNVw3q`#s&t-EHDe5 zc5HRhkWQ9@c`N58`@urwi<66B5UKmgFJLv;U6f&cf*~n+usUAGln1a9>@GC00_Ipy zENqn81hfYGLb?aGgtHQOfnLM0GqF$|_-$-Gf~y%nPclMU=$9s`Ax*T;lT?sy>TyZV zNN44uq#~rV+_R)9q`l<(WM*Wz=>6mZq_<#r@<${uk8<)El7!=BiVtFiDL3UcVxHO* zWPrFNodL}twg}i#W)NxE{`Km=2}~#$zHeSq@WNun_jh;q6e zB^3p76r6TsgmU06H{ukzM($*SYq@?NPkFL)eZBfPBy&FcY%s25ulN&EW@LR2q9^{C z9UhAHbYxRS^kLUW#b7&f24Vb>xosZ1J*UwPHe0tyUoYpj!;Mx)HjPu9>esAp=WRuX ztYz0M8P;rd_dIdD92-wh!NnX`?^`^W>W8nEA|APjx%f-mO@XFf-Rf zL53nLz>84qBAeCwiQsHjzONh)G;`ShHV1FUb|3-cvka+V8>-k0uh4hIA2Mnqu-BFh zj%e(YOWIr<_R60+o%q|#IaAq1*@z_L(ltrXGGo#0yXH_vl!u_o-}FmQD+T_HF|T#$ z>r6VILb2nFGe0ZA(F|VyIi7^{XMuqn?CE;Ju8i|(d!ad0QP|wK1mgX4$;j{6^=o3V zPZp^$@z^UrW+Lg_6rKLo-OKPOy~X2^ZhU&6=Z(g8+AnV|m6J3*A3X*7vFQAMOaj-(^rOPz?uDq^Xuv1i!z z4HB?t3+R{0`zH6&GQGwP>Cz0m&vmL&JALUj<}kBbbdq@2RYg*a*%Nhtr0Cp6^tH^)g^w17L4n`vL=HPu-`G1K{6h;%7D_0 z2T_ZG zq}Bj@CrU<*6J8s2q$CD^9Ni^r4gVJ7E-8kPh~*UdjxdY;#QzxK5Z8A*2+D)r{O7FHbzLd9QHJ3PICuV z6dSBY28)QhrDzHBj4PAXfepkfNSMH`<7Y*v;A{y2{14%_iS)O<;rWT9>@;vJ{fJ=} zUY8s}83(sbF(uxCvtV_%zi@o;f4$fMg1rvFE1*t>oWOB>s?IF%I)PM^6lhL>s7eFK zgx`vqKxQIL#usQwT$j)U=97#?2!UToYy3knk7QTw6j)aB5}OdLJ|&*v5zGZ7Lm2~8 z1}zhB!z93c`1&wM2=*)y)&Rv``QcIkseuI`nRKkv1CS=mXvjg=l4Dhzp>LCS6#SvT zQlw-Yq5o1s#XX=OQyvQQ0%RaLzGc7=l))Vjc!B2G_<%65C;ciA3_hl42g)HZ5)+^p zN{2rTeEQ#vX(!-69eD4SEa#FJ@ApZ z1Jnv)EzAyuK~ni%KxZIBTwTyVkYzRxfEX&rpas2vR#M&u7y$y3Hh>K9A;1Up0qm&- zunt^ecX5N+>Wx9tpnKXZkZ6EZ-4Ox>L>^>@6Tp|SH5%S*R9li|;i0!& zLThzfOQOWrdS6YtgxR)S(X52V9+2fMI&k2Wz%M#-;um^eWac8n=U15Hro%;2$m#)O zsVHRh=Ae@;ko5(~$MUNJj0pZNvQls^?ESwkrzfT{`>7h~`tdYKR}@d%K$m4d-in;`7<&EqlG^yvhUljIre9&icKi z6U)&L_((pSQyTn}@FGV#oDch72u5M=#4;UYo3W>(UlYj8*8O%_GP5@Vn(2hH zI)iD*2eSi1_X*Fl)*`S^teMl%-Pm465;ueGH7iLZ2KRD(oE3DAaw%MTHOX>X+=Nu= za>(4{736c|J=tU^a)P~%#V&GoebR**bL4#Y_;j<8{??qIv-Sd6SI+G0Tx)sK&Tbn?F_5%B=WwD zdy#d7KQbtzSF!IBNgP(+PkoX|gzaUU$$R=kSp%NS+GkmD-gFv_nQK0>D%_d0zE<)& znRoo^rCBpM19Zf;Go=DYgmN>Mg8uSaW#omVaPFqR3BzLnr5A>G({ZHhM~0K%PuGdI zAv{a#jK!)*sT1+oj)_@L!m5@SqLfknicBwG0d1CyyM99I%IUTKJj&YXP66uj(&=@9 zQ_|$=C&31yml?_-ae^)B-$Gk>jnY-aX*k!?-bRG6B&4N9exYMclZe(Ozn6L$OG|i` z3X9vouH2Y_{iUE!lcliL4205qm@XEOqII2C8HiEmNxKsirsSFC5}YY#nRY+qN=h)D zAWTm56Lw`A!RoYo5k9STH@iGF9Xr}lZ9wYRl zL>`VUv}7Wf8G`0Y@}lKKb0xQtw;^{^v*gqpm%gwQ6X{a z8urK^aq}wi$o_aM1u(KHep4n4#h8#TVTk;fNGx&-g_qdEKY_|gD!lE5>P!}9KSMbt z!v31(a)rBOFs&)fy2TDF;dq5$YfX zxoLzS2#|&$5%qf-EbRKS0QOrAH;2h4t`@IB}XzdrmO^qTuGd<9@)??&hV@l5du4xo|70}cn)Z_&c`U?RjI zSQjh^e+XL(v0ZZUzg`C0fxXnCft$fx)qLT`uu7$JY+aJ;#n#_a_uy7=8u1{wBV0gO z6YdU|^rP@Fs{RfD_~s)Dhe#ctr4u;2ps? zLOeoRLLNdHLLEXILT|!oLO5YQVIARp!bgO!2;UKYBf=x1CE_8HA<`kTA@U}QCV~^? z6V(yjCwfHmis&8DH)1?uT4Ekz8Dbq`8)9$bXks{VK5-rKed0&NuZZ6fe3z~i zq_0Tdk$xk?BcmnbA(J7~A+sU#CW|J6lNFHFll78Kku8zEC;Lv0Pfkb9OD;>UOKwZ< zLmoqpATJ=VC+{VnB3~kZPyYQD{w=y&ytia;>E5!v<#Q|M7UEXHt@>NNx2A3_-FkoP zI|V)k9R)9iEQKzGErkz73TS_0w7)k_X z0cAa9FXa^F66Jf!?^O6ybX2@lvQ)ZMwp2b;F;obu0;+ndUaBdoC93yS->LDb>8N?B zWvO+kZK-{zW2h0-1=RJ_z0_0GOVsbFztiB;(9!VH$kOQ2*wXmW#LysU3TWzSdTFL; zmT2D7e5b{yrK9Dgm8I3CwWal;jiE)*7SPty_R>z#F44ZH{Z5BZM@Pp?CrhVGXG`Zp z7ej}jE1;{V>!q8bTcUeU_njV}o{pZEUY1^$-j?2nK87AaUqD|^-%CG5zeN9@{yPIc z104e|gDitCgDry(Lkt6gp@5;Dp_gHbVTs{A!*@n}Mmk1bMp;H(Mq5T7#u!EfV*z75 zV=vftu*9$+SPEF`S$bKfSe97c zvwUa8XQgB1WtC;sWwmAXVU1x$uoke^v-Yx1u`aQ`XZ_BG&ql|_%O=aF%Vx{w!xqDa zU@Ks2VC!R>W_!)H$9Bd}z)sK3$1caN$8N{&%O1;)WG`fIVDDp}W`E7T$9~2^z(LQ! z$05g|$6?3e%Mr_g$ z%N@&&3-Srd3F-;j3Hl1g3L*sy1seqW1g8aG3+@S?2@wd<3-Jlb3F!&h3Hb`e z3L%9Gg&KtVgr zM5jexi|&b@i4lm=i}8uciRp>iiTR4fiXp`c#Tvx=#b(5o#rDO1h!cu4i1Ulfi|dQq zi~EVkiKE1e#2dx?#b?Bq#rMU3NDxXeNbpO@OXy43OZZ8|NuVT(BpN09C1xa+CH5tL zND@jiNb*a{OX^G7OZrL1Nunf+BpW6BC1)g;CHEzNND)dgNbyU_OX*A5OZiE~Nui{Q zq#C9ArDmj-rS_$MNE1pkNb^g}OY2M9OZ!R3Nu#8Tq#LFCrDvpyl$n(p~%j?VA%lpa4 z$)n_pC=)6(DDx}JE9)!UEBh(ODWjB&lpB@%m1mTfmG_l@s1T|!sPL=EtLUrP ztN5wJsi0JfR2o(KRc2I{RrXbWs1m9&sPe1ItLm%TtNN+NsiIViR2xw_H(nR5Tddop`#^VAcSZMu?ztY3 z9;2Rso`Rl%-W@%Ey?8yeUa?-2-UGc^y%oI=dguB?`i%Ml`U?66`gipG_2c!?`o;Q9 z`VaJH^;h&i=${)9888|M7$_JR7~C=NH;6Yt8x$Kf89XqUHCQqDU~q0oWXNbJV5ned zV0g#S-!R?~ZCGsBWca{v)^Nq}gW%o)2F7=c{f*;|(ZZS8Ic*I znShysnSt3IGk>#qGqhQ;S(DiVvstqhvkzwH=0xU<<^tvl<_6|>%>B*d&C%w?=1t}g z%xBG4%s-f)TM$_=S_oJuSQuE`vGBKuw?JDITQpfbu$Z-YV{u?{VM%PsWGQH=XlZEa zU>RVUV2QCTv23;+u$;4eV|ieCVMT1kWF=^&Xk}>SV1>oASYfP6teUL`tmdrVSRGhh zSQA?_SqoY#S{qtBSO-`qSYxb9tedR|tmmxXSRYtl*bv(=*$CPw+8Ek6*aX-l*kEi* zY?^HbZ02m<*c{kg*b>_^*$Ub!+8WwA*ap}p*kWu;Y@2NdZ0Bs>*dEwk*b&Deu;jmex-gbeuIAVeye^T{Vx4U z{F(iQ{FVHT{2l!R{S*CD{Y(8@{0IH#{a5`z`d3U&+*3{DJA4K59C2_6id4_*!a z7+qG2}9oB$PQ+C{!ubDAX}DFf=hV zHMBIeC3G-!K6EwoW9VfVNf>jOP?%DfQJ7;`U|3>UYFKGlOW0u8eAsH($FR$Al5pm5 zp>U;eqj1OY!0^QI)bP^qmhi#w`S8{7kKvaQBoWLJLJ>+4MiGt?f!O@l)QHlEmWaWK z`H0nsj}e!VB$3RKLXk?5Mv;z@fsu)ksgb3TEs=wf^O38OA0vN8kw&pZ2}dbM8Amxq z1w|!ArA3uRwMGp^J&sz7I*j@mO&ZM-EgY>JZ5-_s9Tc4uofcgd-5Na<{Wy9p`Y`%u z3~3BYjBt!{jB$)pOi)ZxOj=A?Ol!|~#*xOc#0keK#~H^t#RbJB#ihlS#kIx_#XXK&i#v?_8BZF|5-%LD z9B&-&6dx3y6rUDf7T+2_6#qDWE&ee6X98&gOM-BMa)NP!Q$kQeQbJlnSwd^VP{QMc zwS>cjpNXW2EQ!L2%8AB_PKiN@Nr`ERWr?kcLy3WN%N?A&4%23MVl(m$@l%F6{5DQ2cqzp0!Ie~&eNuV@P8K@OB z1bPfw108~Xf=R(FU}3N_*cj{t4gx2E)4*lmR`3w`F?bDp2>uBng|I+`A<7VAh!Z3T zk_1VEltEe{Ly*UiHOL|4CzKS*0u_cTLye(M&>(0MG!0q?ZG{d&A7lB7htQt@DZl~< z1ImCg-~$^9Z`;G zLkuIHAl4B_h+jxDBr8$`se&{?IwOOT$;fnMIkF8ojC_JzM;;-6p~z6IC=rwj$^_+% z3PvTP(oyB8Hq8a(ZZK=bl zPg2)Yk5Yf7k)^SwiKMBdnWQo}ONw-j+U`{v>@p{V4rc23ZDchDe4=hDnBVMsP-QMtVkhMq9>k#*>WojH8TS znPi!)nIf4gnI@UenZcRKndzD3nQfWFnNKp;GmkQVWszmEW{G5}WSL|+X9Z^^XQgMA zXSHPwXFbVU&pOKbl}(n-nk|y8l5LXhoE@BE!rqLF8W(rll^WA*Cs$8Ko7a?WH57PfIsS zk4t}-k(aTRiI%CBnU=Yfg_Na~Wt3HvwU>>QJuTZPJ1+ZOPF~JdE?TZyZd&eA9#WoC zo>5*=-d;XZ{`) zOHD{kN=-&hMNNCnNX^rljhf?{-?ikmY_+1bsrcX^*&9|D_o5h;dn$4PB zn?svH&6&-W%^l67%?r(&&7YdDT5h$lw}`cQ)SlU1+1}AU+P=`f+5V~hs^eA%dxuzu zT8CMOYe#4Ys3WtZvZJG8v}2)Tv*T08Rp+fv_D-=*wNA56*Ur#RP-kXmWoJj{Xy-!b zX6L8QtFBvJ>|J7AYF%bsu3e#BpsviW%C3&C(XNHA&8|;fSKYU|*}KKM)w<2PUAsfO zLEV|%mE9fPqumSLo86zfuX=9vu=j}dsP&ljxb}qhfO;}}DtkJ5Mtc@|HhVtxT;02M zkNuw5J+*sg_gwFV-UHpsyjOXzUX4W11C z8KM~C7!n^+A2J_u8wwi&4`mHi4RsEU4Luv$8af&JGfXkeF)Ti;K5RbhHXJq#9?lxB z8txn(8-6ytHGDGsXM|#eV?=yJeZ+jkZ6s_2Jd!n1HPSgUHu7v_Yvg3)&nU$x$Ef(I z`l$J++i2J*cr@L1MZ)mZ1)*x0kN zt+A7_KjReR9OL5S>f`3)ZsTF&;PI^Ss`1Y8vGHf)TjM9=e% zz!O;$RTG^PV-wFNwkA#{{!CI#a!iU(s!y6vx=n^nf+w>kt0p@q$0namZcUy{{&`68 zkmI5FL-mK|58WPyJp@0@dRX{Xh~g2)Bk@P-kIWyrJqmjSew6j7 z>QU#Tu}9AyZ9O`9^k<4d!RAG{>~~ zwEDF9wA*ypGvmCSHv+A=Jv+lFuvyj>B+3MM@+40%uv)i+uXRqfd=Q!si<}~Ik=G^DP z=OA<0bJcTQbK`T*=eFlQ&t1<`&U4O7%xlbB%)8Hr&qL<3=d0(t=Evus&u`Cvp1*!f z`Iz&u#AA)e7LVN@hd+iq&VF3|xa;xwGXrT0C`s8vYdWH2Z1w)2^rEPoF>Ce){?8^#bJr z=Yqt7#)8Fy`$G5vWFdQ@dZBA!eBt@R_QL0d>t~eDIG;&8(|Cr3gFXv?26>kKtom8k zv+-xopKU+;{OtNU<#W#G63;cBTReAv9{wEiJo|a|^RDOP&!0cve*XFS^$W@uoG&C^ zXuPm^;r=501>{Bci|Q9$FUDUyf3f}I^NZ_6%0Hq zEB9C7uOP3oUsb>AdNuy)`K#?$pI==sQ7&;VNi1nBSuD9Pg)c#tvX`otx|YV5o-b`L zeO|hLP5GMhwZvyw>MPtQc#eF4w1%e&9t6u3^8DDw6vc2+o<@ycf z8_qWpZ#3RmyzzJw@do-P=S|Io1RdQ8x)pFHiHDVRInzLH7 z+Pyll`eJow^>p=SjcSc+O>#|h&2r6SEn*G2ma|r~*1a~d_F`>k?R4#CoobzHU2 z-E!SyJz^cYp0i%F-n~At{$hP+{dE0igKC3oLvllN!*au8BVq%(k+V^=(Y-OT@nU0V z<8vZd8n`)bDTXI`-+j84uJ7OEUowHrD-Mu}r{bGA(`*izehiZpw zM{-AV$8yJGCt?S>le1H^)4emX^I~Ub=XB?0mui=5S8`W#*K*fmH)0pMo3mT9+r2xn z`(k%z_jLE>E!A7Dw~}u)-&(%)cpLE+`Znin&D-v`6K`L<-FbWZ_U0YcJFa(H?j9(Z)fjx?`EHBpKD)oUvuAb-(x>w zAG)8jU$fu6Ke7K}e`o)6|KTQ0vg@(DN|z5ID>|tUc^GoIG4S+&%nq`1gqVi2F$D zNbAVz$nz-j2sp|;sy*sCnmk%O+CBPm^!J$hnEP1jSnJs8*z-8@7&y*7u08HKo;+SW z-aY}C+<&DpR_(%ee(Pi`3d-x`>FO*&!@>xi=TEsefjkFg!+X0MCwHA#OlQJ zB=Q6}$vvq(={cD^Sv=W2`Ev62GxcZg&r+YYK3jeE{2cii_?-K>_H)nY$1cbRO{60)blj*6gbU2tv&5IojhGU-97zs`u7X<7w#`oU$nkheewJf`33lr z`=$0v&zH$Bi(huXeEIVCEA>}yY=FGhSF5j{Un9Q)Uvt0Ke(m`>`E~K@?$%tr~c0UUFy5mcdPH7-y^>R-*dm$ ze((7{`F-*G?)NX>|DI8wai2+@X`NY}d7ed{0cW{qwP!tNlV^)(yJug{{{Eo;!Tm$( zht?0PA6P)&58y}ckJ=wSKPG=H{@DHT<;UN1>T~XMsdKG!t8>ru$aCO4_q_JJ=X~;f z@qG9E%lY36>I?1*sSB+Os|(MI$P3^i_oDWq=VJ0=@nZMl%f;VI>PzlRsY|U(t4q(z z$V=cd_p^f8P7~@aM~)Z-0LM z`R^CauiL+*e`)`+{^j*6>KE)+-mkmA?)`fB>*cSvzrOzZ_nYST?cdVBwSQax_WB+5 z8}>Wz_ub$3en0&E^7q@{Uw{9*qPe<#C4HrRWqsv!6?Fx>%DcLIb?@ro)yu25S6{FG z{h|4D`;YV=?LXFky#7S}f&Iz*bNA1^KM()B{PXtD*FXQRX|8WyOJ8eWTVH!!M_t3N z^RDk+-@ATz{qp+l_1EivH#9f5Z=`RuZ>(>;ZlZ2rH+eUAZ|>bZym@)^_U7x&zrQqp zZ~vA4tNqvduh-wGzp%e~fA9Xi_xIu7mw(^>{rdOcKbn8H|4IMT{%8Hq>tEDA*uT7g zcmLh{_we7#e{cVN{r3;2|Ks$3oc@o~|8e?1PXEX0|2X|0r~l*hf1Li0)Bkb$KTiM0 z>Hj$WAE*E0^naZGkJJBg`ae$p$Laq#{U4|Q#8s56e;bzh3{3 zziF|+0PKqvM-9MH18~#;95nz(4Zu+YaMS=CH2_Bqz)=Hm)Bqec07nhLQ3G()030;{ zM-9MH18~#;95nz(4Zu+YaMS=CH2_Bqz)=Hm)Bqec07nhLQ3G()030=dfQLYaK!?DF zz?&eN08WrkP)Bf|;1R(qf_DVp2=NGM33&)*2z3Z;2)zlT3E_nKgmr}X2_F%@B78^q zjR=p3mWYQ)hDe9VhRB;Jng~vmPgF;ApXd?ME24Kq--z*uX^DA=Wr%f%ZHT>zqlw|f z`NVa^_lX}7zaoA|{EY;UgqDPdM219%#D>J1B$@Or1_+Er1wc5k-j2*NBWHn4@V8aQ3G()030;{M-9MH18~#;95nz( z4Zu+YaMS=CH2_Bqz)=Hm)Bqec07nhLQ3G()030;{M-9MH18~#;95nz(4Zu+YaMS=C zH2_Bqz)=Hm)Bqec07nhLQ3G()030;{M-9MH18~#;95nz(4Zu+YaMS=CH2_Bqz)=Hm z)Bqec07nhLQ3G()030;{M-9MH18~#;95nz(4Zu+YaMS=CH2_Bqz)=JKCr|@$`Tt}* zxcq-y{y#4NAD91+%m2sa|Ksxiaryta{C`~jKQ8|tm;aB;|HtM3ZB3m_{h6ki=9m_rR-ZPX zcAE~H22W>AS50?Lk4-_hK8`7V`@ZlJ6C5#K65J>>jfroli!Mxx>whb^4JVoyftQg)XzXt1uAO39yva=|E z-~*AFWQU%BTL$IUIB+MO>y-dtpQbTe0^CXUAN~wDVCuUW0B7`7y#Nq^_ACAhB%$7> z>jJr`STGwffMSeD1NM-!Uga=SB-~aOCWtiF3xVk%ndKv3Ziu6sL+D9<_L&j%O`g%m zX6WNwvMmzmboSQESm;dFqnRV<Qm}e0bKq^Mciy!Dim)J}L%V>Pvf53NCtUMWv5_xbV0<|l({GI_dD4shwfvOi7 zZdyZ?3*IbFLRIrqW|X12c@jfqP>YJ~|v^OIgLW$1wnq_4bXqYQGd z^v5C@q_4z&8VQ*!njJiVye!o3rbX&113sy2a@GJ6J^QB$qSYYrjToX<$M+!$qEgGVz6nvT7Jp$0 zQLi+eT7a0BCk#qKd`i1J!XZh;XSL;!q9Wr$6lAcV4)YUokawQ^6Dpr;5S9nc%r5en zhd$5Tu|`7wWbo>20{m%yvi|@ROvBX@_-UK(R}Jv97MlH5@M7c4Is^E1J?42Pc>AvF z)GhFr8m)nN2uYP>`w)buLa635M5j!$;4LJmLwsVq`R4uO0s5DlY(>gTa~*kSj& zvF3zMX0GX=pr`4rvO@rCYTOkW*rR9hOC;E*OJ;8s9N1pIrU#B_rG7RJ1~rvE(ghbZ zNI&QUci(;0b_0G^lTxDw{#GSbpbX(G-$Orw+$l{>T7@(h^MS8o6grQex37?mq72K=Q11L&+L=(# z>`<9s(CYNjpB|vXslAgGP}yYUJ1bDl=--v+pyr|Sr%s>;540w}fR^rm>x%`QcQ?1Z z0ke0wR7rqMTIutQ!9e3Q@*8-(&NWdSLQ%aJLIm-uuysR0x=YF|^C3S9I<>W-N;%~+ z51~mJv6pe6^d~f*u0Xl7+HVn{l1B+E6rj7~qfhRF?u{@`sDqvizt65OR^@lK?2=>F3=sP{G{%vK^>uYVh$FQ0K(#cphka7E=hz`=a?S?XE+3f%DtdgGj)KP0B7$A-8KAEEplK$@xz zAIjj38FCHTyugesM$%87BJUtD{cT7N1Z|r%;ut zh=|OsiF$-;#zvnXf-?QMLf0SmF*8p!y%|`jaOE7N}yWqJP&5%5JI=a=J z2@asyt@z<#sB-O>aBCEotSMXy*>Zk@*egCh?nhJ=rR;(c&V_W#1_;jlp(kwc!@ThE z-|(Sa$=-+X-0XiXj_}Z|{mM6Rlg#xzI=FPkI&uRpp8hUT5-ySUErcB|nMUf?2A53b zw|ofa#~5nI!70!YGUu=ZROz`a!nLyE=mtSwez5Zd{;EvvbqTzrr1o(n+`pJ<+y<^v zSk=o0XDE&U~;jpP34WtchGV3Ct3-&DYesCjfGb7SX1NJsu($XEa zmikSb9X5j*lxc=Fp&>t_;cIm^M>_E0TAv+SxMMYFX%Bw8vVMLNc2>SM)(CrA#&e|(Ob^R0TCTVYi!Id7Ie~fS_aJ6rUU|$3VzBs}lwb*1Zq}yjOITT^n#CVjL3)MO z91NNIOU4}*hH*ISfCshQIfTN+n>e>^U_a_lmjq!`cVEmC!^&zNjedbeSIypg2(zhp z-4qN{Ejui~f$^7;<=Da)i$xF$Fvdd7_-2@7zG+Y|%q-W+)dzMb+tQ*6W}9iC6%R8> zSCHX=si*ROe+wt*s{Ggwd)mRYl>sYmt$5`P3uu;@(}QU?ycv~-vDD??BLS{z?HdPx z!)o?&dtkldQ+6-#qHGvm1H3Frj%NdoizI^NVYl*ET)x4Wa-%F%VT@TnwFF@F88Om3 zFq+hZ?=-N1zSV;RSn_?k&2gA+x82K97+pu*ECl$}`fnrxm~W2kF$Fps_8R^HwRO&A zEkJS2YBn{HQ)LY&2J*^>;&OnFQii~O;CYdk3k=xKFEZZ%-sf~`t^yx2yQCX|k7*6x zlwk=Ycn5Sa<$>#sQ{Z3U&Ej+5)qT>LA)vjRd$=3GbZB?y0uikN4d#GfQ)cNOz@vUJ zD*|w--GoH}ZdE67gg|`xmp~<;q-4j14rnjzHirQHc@COcz-ZR4bQtg`-Q}wX%%pBq;snNE}-33QR)P!HYa4h29z31 zfOmk(UBXy6U|H1@Fa?B`>pFJ;Kyi<`Ads1VqG zx)<(91w1&$kYY+n)3i>fWQ7{ zEHNNoYZl-Dm{r1^EdaODT(dVoU}3N(0T7eRE{zX>GKNq87sY155Y?hZVY|CK0gF(P zMiEiU07L~71W`m$K&7Tm=XCcBQ*?KSsHk9fd+n~PUi<}e$>BgU*)z?k{u|y>U*3WE|}+hp1qBVfBlKG^aA$qE&FG;_q7-7 z)2F_jeazMzKiBr0o!Z)2{VzMX@mj$f_QKj1hLh~6m9L~S_Q=v#MZsMv;Q70dy>9A%D|qjo)GHKULbS##o0@Qi=6drZ+htKwH&XjricFQKNpu>^I~^( zcb|1*E4$L#eAx#(U8*;*1KN5Ogt6@!&lwi8$JNr(0qg;ls|hRXcga)c9oDac9Vr#8 zKi0fGPVBzMo~RA%L7LMcKiI?N0^fD)aiT@e$?U0g#j7Gt&@KK04QIf$VOM$VGZ*PI zI9t{;yj8?b?2=a%uvc{2=U1|4wif9%?BR`ek}TGr8U-RzgNBXer_icxZ zS^KuR!CP2$>$K1hw#y1Dq-UR7%!XES@VB>#5V-!)cf1vzaC0*52{F!jVYT4vAIDWiPOCz3A48DX_mSyv0DTDVf^Kqna2CWo;sq~-?Dm4tYw-5gWXK4XuYClq2i42J zf&YS}%RNvHGTx-%1Nc+_QDF$L&-D&$0R67}6)K>fcg#d*k#B2vAa%s=A^?dZX5<`! zf8v|u>tP$Fp`O4|=zGpEcmx`j)&cb(HG9WF0%ZQy!B7aCzZM6-!=B5npcT64A_CVz zhhDd084~8h*XVy@`|FcYrtnI45IR^OY-b~S{!;BlKWd3%ryX zMeT&1lC#-d$V_;qt%VNYkqI`)4NH%H1X}4*>IC6qi`J@C!A#rg-7tO%a%edJl$WNJjUJ6 z9szmL%TljGL&@I>p5T4rY_uO(fa^mqfP1m5<$b`3XtYZopcC_$N;^i3LIsDyIGby5=)E%GMAp|OHi79YIJ_f6di z)^IEKoCZa-L)0xWp45h>gZ9J(-zwlXCUR~EXmt2X89Laq<^BakV)DH@6q#dOa3&D0 z)7!UF@D{CG)m7-bdQ-t5$f{%+#z0%-h0?*$5b0y06+9)G&Kd|xMWHG8!4$#4JzKyH zJR<4{IEOZdP6wWo)xJr96+hvO0o&0lFM5#Ng4FxN5VyP`SJuK^*22>i9Bba*auM#G zGpA}6WYd4okA@<(xAlq8Ky|ZZAK0eMC+2~WT+GY_BcumYc7V=eulRf52*J;&!N3z< zWk?55M(^;Q1*8$*oi_k3nD|9sWM>(0Zw>sS_||0{&Mx%sIs=c%v$c$aDsA?ad!cRS z$~-ePI44Ng0JiCGi`zk#HUp=@0QGRjGjO~@a`+nXNj5Y-0k|SQwnYyV3r>e50t{Y1 zpX)#X<>)*K7>I{F_d@2>WZY%L#g+0)$KmB=^<9IZtHn>6qoDM{85PCQ^t|1U)q9;r(XZv@&D7vtUnInsw)+<|P-m5@=uI=)kH(fhX6DIkg_DsQB7sfFes@lz#`m7d^>c3FhbjqfG$g zZFfa1(9V1t+YY=jUdZkNT686ct^g`EGmZ^pC|oyR0m3Dxf}4RkLT8^a&Ij%hr%FyU zVS8$YpLe+3{sL#UR9y^&hc|kiR6x14-Hq>|;HqQgVc?r`7n=rD7r)Y+0wW6y!e`)^ z+)d~?;HLRkb|{c*qz>%@Sh~J(6M!wMxXoFBqf8Z?%K0ra`i$oE@IWV+qankd+=K6) z`f}SHPU#$ffd}_*^E>ebGBmLpS3`bvcgym?N7d2R@1UUks>TTnELknw4E8UmKvx5& zb4O*J1*GP%u+)3 zm(T+DSt46QcU}(6aXI(%s;-=5$L32-F`|_XXXmqXgc-hjMSk8>lQEn0XDz%qvJ9 z1Nd7UV!r^x4XjNN=cc+S$iT74a=qg@DZ*^WZ=B_{$D?cTi);VgTnMLKvYi8<-{-a* zzXOTS46F}=oKLirw1OAfvn}($>}D5L3g}dKnO_3Ds7ixtf&9`hnY(}kh3gLH0gG+N zV%41ga*l3V#%a^83);ycQ4q7N;xl=R3FpO;Ud%*Y4xwQticb-Jnb~+Teuyb!&cSXmt5fD-q0DFTr_n~% zqNp+Gd=?n;5)ra~`|g21v#ri0@K(;=r?uSE(D^&=+(D4f#cFyTSbAz1r39BWHIeUt zwu%j87_cSx1W^g}(hVo3aXQ65IK>g*^Re%ogA5y%%t<(0gFfOU#K)ssISE^8ku#jV zAqvEuL-Tfc zL%~Src~BJ?{v?vtbFbaHM?I#`UD!uCQ|C{-Cn54qBS1VLzL!rVf{D2{8D5QV*F4AP zVHS}crboYHv$4tOj_h(&j+{Dl6`hQD#3>OCT)CweG7(-C`~jw+e|!+=JH&H(3hjfI zKeo`L#e;6SQ`?27&YvRl1^g3R$X@*24KIn^ywzn0ewMq`)(`igoi#hKa>`3+z!s4a zs2MFF(z1O}CtPu8A5w$0#$?B_^5ATU^23pH)K^LN4v@WX) z>7cTce;`}PO}l&GSHzOdfp9uLDR?dP85`{V2|9@OaY_R3!M`4bQj@f8H=dJwG=tBf zM3ri5=S*URqPo5bKPsP9x(j+-cdh-Y)~fY=)E8OhVO9v1*W6ihnL9pd7H0i5oX)So|*V>^M#IfJT}Krw-4(v zfF+|ae_fi{gf?mpsK%f^sziPo(xx~B%aKr7YUT#`u4L~)4a^X&kEP+Mf`yyxp-S%W zppDQ|CRJVg+WXTeZx^~2F*-xAkr4McQdS$7;>p1-tX5I)Y^S zO`p+YhAQO&Mc^x!mjm2`0IBz1h2aW^9!aH6IKs)X$$FV>S@#5ZC0_=Q$ zxf{RT?sXc)y_$KgzL>85->R?Juo^}|4GLCHHO3=9OPgh%5q8l=nuYYwdkS2DMb@Na z&G4+83rRsxm43#qOej>nVPiCSPrh}{c`#MH)bj(_kN0fRGeAxfcXtz$-uvc6hc(Fh$Gms*PB`hJu2LXlCsQ!x`jbWtuDwIZ}Fp`Urn38V_uM;k=E<*1#hz z$@}j>2IKx+)1W2VH5&(m#}(h#aKTLywr2zIgnxTcGmt^q-DxDib9XNt!>^w?-Q|KW zKH1d#3KMrURFTF&QZqNxp^4NH-iwUecnko}d5sQvKUQV&iKoL=aB^gP_l_Iv*k zh-b>**$z$BFNtUb^Ht(CYe7$GhbIbj3C=E(0MS(2ov8$NIpyL-{Kon9r%2rS?4st? z81K}O$|302j<|ddJXcZp_yP-DMlC^%i-$1@x#6xJ=;4|vhr?#~lDvZi0{Bbv|bIdez2kV&>x3p|)x zYNY&)%%w$HyiDeJ(;BXY*;nzDzRVoJOQ8obXF^SsA9GEHo=jz?9Gpa$n1!*f_(kTo z$bYe4tgS)*m_6&h_YyReO*wu*k~tgh9Tvgh$1CfEBzW-50)Yg0*EW!!2LRP?d8dIH zg?G9CayoOWxDFh)VkfAE0D&`t~m@_Rh2h($0 z0{=r#bK<-{ARjrcj`hf7!0&Fm@ELaIvRUviHvBXy=#NIXCh}(@W!0N_YmkYBA>5-d zo?}nv!vp14sed3fH<_9Zd4XMI9QY)?iO_@k10nczFghk19|6ve+<^ImqXSFO6mYy( z8KMJcI709#(DCjJ;WoP9@<71>s;28bA0*GT+~DbnS5+sur--QqP4qWB%BZFnV@CM~ zDjEHcenXa_JHT+_GjcY49pR3wPI`nNh1+7LV`Xs2#vkZ&=viO_Iu%lSr6B8|O^$2f zY-rq_48cWV#ijlHCjz`{EAJOSt|f>!gBM!m%?;uD7r4?aI>0!Vs--r{ddRQjA^Hfp zoR9-|2@F4V>;c}5{Y+xxqcPWAZP;csZDTGfMcMKS0ie!ET>Nkv-69oKY{>*h+58{Bnu}w+^^Ix;uQS|I7E!4 zt{%(4W5~{=saPpdwksa}jT<*cpn;fn&3ObtEuJ6XCZu}NW9TD%`nDtgzGl=#N8Sfj z%Bcms!HVn6cHBks(8}5LHfeYM97-bDW*ACd7JZanASVeW6imbkR&sveWxV&thT^@s ziv87CH0{2tFIq-kiTHv1A+pyX$SQokX9WzPw-;4H^@!*eo1baqUVwNaef~*=TdM78 zg6XU3Ulk}lNadGDQ2`3RK9fXcPo=AfZs}_B7cog(%wge)q6tUkSfk*`{$OkzuXCpn zP3De{Fe9zht~H*>K%&_57#xEAT!ca-y7uNN{zTiF^XGW(mZ>MMaJS`rZM;rnhJVWM zQcb#hxlhS&nwPqpJ&Q>d#_L&kzs+d=)@hUK{9klH}apbBK!rio*%qg z2kYoF9zUQLApY&I{^ zCH#{){VyR08EaV^F}?0xngso%8M7}94N=b9{u9y5=7leVKZyse9ta2Wk9**dl8#w; z2YiXYx%Lk)z6tIA%7yDUA0JBR)-0-@PF=1XR_aO(E&Xj>N3JXSqlqLW1-(Un_-E*bMcPiMr<5Dx?l<8GPc{M zNOeU^O?URXBi{}Ewmn4xH6y|TVTt@xfIswDoag=ma_4(5+zS$9-Ie#eHD}?o1G#%o z8QN#i^zrsu52~*9O>q?Yw#muNAsy?ODkYIp)g@5kwdH+LA>OapixG`Q=5I@BNA=dI zgp0`2oF&`%h_m*^h97X2k{Qqk>XJNkuYo2BW-VL^?x)sY*~AMt|M^TJH?F%^8=J}*3H67&Qc96C}V#YDfy-Bn>deG!){@Pa2wghX%@PMU9`_kS=lW+f=NF6UbvXZ z;0#!ufd5V0J#J(EoYFIv;8ya1l$b|A|?TsR@>oy zf!iJy^cC=a;V0xWIOfVdnHwcPJ6AfAENf2`zacKw7K<(ueTu&ePvWaAVS)}!pyu;C z(HBA+?<^XI<#TWS?e&k)&k^6$d(=y~bMI^NJ{+;#M4X3Sh5f=?Aa%eatO(lfk&LP! zhlQsQ6#RT;wX{@_enudn_*>g2mP6++ z=Sr7K51;-a86+9swn6knbiBqO>=pt=Uj-e4aPubqalVU6!|Ucv7PN40aEGDi=-2cR zhK=q`4Nsj;jUY$w^&lq@W4C=Hrr=Y<_TZB-r+{79SahBHA9N6MWMLZo1J++2C}~z5 zI=x#gRXk{I6s5?cYsLw;O79h=2qGlurc3XlK#pLqNYgP4qqc(M7t9P5q<)A z>l1t(Pq*PPwuLTRB}8|UjqW>;IJ{-SFE{~hz1UmalHYP_ugH>{)_g|Dw=Sx37C@%= z1qwc7Je4zmN9&6eySW;zf~TcR)gXMDI;%{}YA2t{cOBkN{E#l+qa=oi$7}`h@q#-W zCS$XBvQ;7ILfY5e0dXZRFSr5wV(}M(#nMu{lNF-$B1`jB;jV)Dm9QW@w<`YwKis;` z7|+{odMiK9J!lm3`qPl!4fdw=nmbuOWTT38=ss~?F>=p5{Ek$ybvpJ$JZk-M^t~W; zRUhOhx6183GKhSzpb{RB_c<>SAFawb*+;awyrC&Y=v&&m;;z8CXhVJ&-#OoAILY(M z9VB1GU293=9-(7%x}Y+OX;_fiKq|H1p?yS=sw=(`uU7PpZpNCVi`J*0Cq!=kEy!j5 zWVa^xA@y*95c+^?&b<+Du5UcCOXO2i)_6|nP+3snCzw)Jnpe%AT71&5m}g(`N~Yqv z=Z&GC(;?QykR!Fnv_8|1WE*0WKNAA&uJ}#3T(vGb4pYhP*3Ut6#JBy~NQq#d8wIz} zR~95g=ZVGV!o)r;PdnR04vj?PQsMYI*K&(sXw}=i>HOj4oq8>ALP?oyDA&GFOUKe4 zc}%d73b$^}I7#j|%}>UN4E?>h_c%iXL=V9j%0cU{p`bL`eY7&%SC{CA@`hCGmU6fgOUF{T==p`!pcAz+ zZ(~L{xy5onc`^~7vmuU;$LlOnRamU*?m8a2NA}UbFLGFP%WVP7;u#jqhj?T}&n)qr zF8AXC(a27ALodOfw(hc{{4dRRwokmT^?V(J`=@50QSV!@i+b*s@3Me@p1$VSP)W#8c#K6AkU=n#3onY{wSl1AL6idJI^d%%h+xRk@aUp$!BVSyip$F zD9UlgM9!+bg|Z&@Z~fK3xYjN^Alb;4)3?M^*Z{Ol_>!HH87-)0?>%J4Phszi*YGB@ z;pkSnj$OH)q_(hM`rjiyalG7a;5?3bff;iEX7o6z3z2Wf&#FR@sD>oP8@RcAuv`VZ z=9bGMp$7eK=>%x4^r!ef_>xW)6@qf`qL2dyXZ#d|frFCI@rMHs;@5F&fVSv#dJB-h zK8XARnEh>p0w{AUz}^f}s*`U2_K_b1iR4*z6g z1+>VGh5ZBdU9by9!Ta4KReAhP9SzD5UQm64{2g~y*(jNT4z+bicTn*Pr28s1NPf3~RFn1Z{CJdmx zfIPu0Dk%La?>o8U;Mu?4Gc9f_jp1aJj9P(N*RhCCXwS-TxEby3HVX?wmM@rse1lnM z?kLTQ>+NdA8hLzOlm8V#TS4QWNZ&Uh(4n}1j+PM^$MFPMLD z7;)GzcjOMK8yGiaIGx5fWoet>fXTl@9 zS5w9O@-1pInfr3Bk#M4?u3Uw^Bm8|&RlewDKdU=ZVU=1jnX$~k3 zlHAB~u#6Gw4O7$)L@BzdB8@OeGl%dL%vQ~3ck%lwmL9X`K9YGISV-4M++uH2S)v77 zmXjWWv1=pnFWh%4`(QOx-LiNzoj5vQgScb!yVfeCMVDL0%U2epRo|1o&l_K4lN4Cn z%tyqj<_vY9XmyUKXn=5<{s&&b|Eg_d`}1z8IY)E34a((7H8d@I8=FS0lPESnAP0zC z*Y?Ic`SpGs7|xxu%oSZrrq5>~BeB*~d5YxnuPsO9Q%bj2uaNc>9WNRzAqtk7Ziypu z3sq&JxmG7(y5NVY44=inW^`e-@al9WN1eC|jeSxey-&%H9Y)#9UT?;TI}-2EYFs5e z;0I$nd0ET)qjRZ)^JCzb_{LNH6%n5bJ#>bXqa%l)_M5Y4cdLaLu~*5<`Ty=+$K0sN@Aw_gzE z!EbQAf&8X7&G&~-;`dMPQTR0VYW9@9sq3j4CN)>*6g-f`R-ifc;svFkQYrjiBogcu zT*%MCrt$M_R%REEFk6oVarYRN`_t&fx??fx$meRmO?L^k{9~vc9w|ZmhGP>1uU!j~ zJ6zZKcCdv=I&o34pe?t_D!bF{TbV7D)ZZ)!mqgScIga8fmBGpp!dGRp`Tq#oivB}& zd}V$cGn|)hgVU~a!_4mchtiV`=XSj#&uhar(F9vr9dZ|UlYU!q1^p@->k1%EyrJ_4 zzzFg2#5%>~&Oc4lWarwCR=km7&F}KtC2Jbej0W+D+Wrbuc(2mR-zBIhTZPWz6Gi74 zw|EKpo6{1x%dMvqt#m)rj9n74PQQ7R7qLfuC`5@*kteLspf|;?uA31Z@BX|Oa6FmS z*`pY8dU2y%)^&T!JV( z0WZt{S2GZ5F#c4b&<5pTV3oif#G1~>M zfOOLj9tEJGA@qF!^jl0V0mQDy2{zC$-w`_veDBsKgM1-!9?h0EBO91&BvJ6yw0ojgaN_(C%qJ+T0MJ%0?A1ztXWM}LL4zTtz;jT=?bPot&x<&Rg7pdjNsrGP9{ z_$sCm_xQ_YCB!szx-`3QdyJ&BY6#Xs)CdB5?An>@J5*s2f>bq!^#(2<@o}piAU{_BO1nfht$c)9De1~{R1Fog4WSC5$X}i)pCcT}OO@91pCCIV zk-QFur|1(mH}#QFNDC9a1T(4hT|PV$xh3)gcM9PZvXkQC4l9y~f3fMVi||Nv>bw(Z zDKfpoQJ1ONT)$0oSJhF5sDqWua`Tlf@^XE*Vztai_EvUXa*_K}nj%hy-$;gvMr5BC z6$xrnL18F=_r81lTfBZdpYwLmhDaNImGTUkMuia9R=5$>c&2MVd-`n|RJ&z~GL@o%n=N&fPli*)=cU=%+eE42hpFR) zqeMabviL@UWha+6i}x*ZGp*nT2VbX#QqtwWiEQHb(%aY*eC9j}x&e)C`&awZYN(s1 zi8G5!qg6dQlnqg?F>rOI@*bT=dQ%p!sir?jeygrPZ^RnKr>uJ-Kl%6+qu`#@ZQn?K zvN&|7BlndsDzXZ*Ti2XeYWE) zw@F;Iv5gd5q{8aI9w+Xsvht4;!jf?*l~a>k-pd% zMfVm*1^pq@1Vzg?;?KDcm&Rf{$v^YfBA4;etxnp^s`Q$8^|SK2Vx?+F$sfyE#m%A( znm_V{{6dMF^hfR(YP&>k$pVwaOLJ~!rUqJYIPs7{!{eX>X%oaQ(vfYD;}f@tXyXCQ=BOC(`3lQ zi(|xP(x(L?^0wq}-~_xAPqjSD_$h2M4L)>F5N(*1kji_gowdD|yHPbVqK;~mJrBwx z7l_TiPjS7#bLnVo99=$dD#9hEwhYjQHrG`9tD76LikPZpwX4ikimIwf>UVO#@;+im z>DA(2FFoM^m-@Sbo*Hbe%Vn3xc6PG=&FOo!PnqjSX`{Sxcb#V((_(^5oxY)c%VXeKdPLwaMQj7AXC(2`p zyOLccvpBy*UkWZ~OcY9UfkT4@PL^qVig^`A)%O0}nc8m=(d859M>}oJ980nyMxuMzWzi zfP;vh6gQ_^g;2rbWP@O`O|WMzPiyMlR?Ho!e-eI<+OK{PSWaA&H~R+R{^DaxbZ8}i zq{9_t9;s^HZu4LSRkd598Ow?sOf1HHGiEGc%vC?rKVsO6Lv#)dXHua#$nXKWR27Wy zj8}?Z4CbMGa(_l`0w7Ie{Mdd)EMjhoxGT(Iwgu_=HkQxw-dqjq{L(2D%x29SMkH`N zo4!~pIN4P@%@;Y&g~xL~vtO7UjC0u~YFZ!5W{Ey(^Vwl!p!zd=DZo^%WIJbA6au!> zA+78>+bLm{bO?L#cC~l`J2E0)=*{K_rSey@FZ$l$dUED3`Ayk#R1V*X;lSi3uGJYX zul#D>052`vopThbGj$m(e|yv=x<}x1kyh&h@`%rB4!8nvQT6~oGqx*c0nLY^Wd{L4 zLO+QK$lktN)BzlhI3~CZBnAcW?f`pzYv~(6_L6_dGeEw>3%nNi+%(Nngukogn=at~ z1;35oFr_KU;D~-x-OweY38FwvJ#v#UsRtuboCnIS@IM)&<)!e^Lt~|X;4ynTC7w{r zc6-qd=um{8AO&&^8pmTmeSL*A6MVJgBzXk9?WOP|LPZP~ zj6enjKIYAWRlfV_d9e4Ad~z=I)Zqj^8Im_HGnWc`Dk^fi1?2_KhWC6;j!i$4CsIw< z?&d0lQgu0@+XfpfA+$&b(Y>~XrZ#86zOLKg6`Jzl^t>%?*gV0sI zT;POjl`{S~_6zwdUZ3;@vS9jKvX8__eT@GpdPzRtc0f3vxDhVpZ^O?9vbh}WtnUQs zFN-WWNGOrl4mp?t>DSC<-jvfznUo)3aFBm6p3p_hU}d02E1fMksd^_qi@Pd= zMcM4X$RTt&)-C=qH*yH7~ zlJsM7(pJ%@gP>%x@KAh&D4zdt>ji;<7aBg4cY!Vq9875Z20*@`~1+rW@&YAA-C^o?S8+|A==x9Kiacr|Z&9 zGxE4)LyZBp!?}9>AxoseQEN3Vm)EJ^7@hb5ssR05v{|9oPGQZKeN&G<%8+hT4n0sP zu9o+TP0#5w%SGzOpcG zq41hKBKnkIu;fjc9dC(q{1N(t;DX@#^iLA|5x(a@S!xu zM(OQ}PV1*>cNcieiq%EAwLCvnAM0|YL$Te|!CWG%Hm*9Nl8)20C*2Sq(s=AXE38y% zqSN?~WuG?O9P%Evd=T+1=k2uqR;mnutbee^@h zF6rdjkdi*Kg=$&hYHq9YRh}FPQUq9^Ga6-Dv&)f%lJCZ&NvYzEy5il*Lb>`<^mKlw z;>iX+_nGw3>NE5Z(JAkC(veRs2Jp4CtHT_0KYqSuT@KZ7r}(jGj^8~($kcw1>f-0Ax;Cul zcF8%bY3hY!s<)6FB9JfMg)gALIE+FAiQwvv9Cpi{;%$bqzqjV2?pYl}OKI(^BcyZH zDHTiUR^^G(X>hn=dQqS33$i15x6{^1&RI+Mo5gcYX|bikc*EqVBYd95y#5=vNI7Qp zI_j)6(L08CDby_X#Rqb$?SG?liMDEwoMRoeMHda0)}xjKx*JW4wf(f?>z+%D>X>Q+ zy;50Ku^GCe7+Tsldz)-`p*8icq#{x#(U#$e3Y~=cY2oX*XCC7$c=a%)1$5=`%SA zj9GBDA(63+FXt2L?%WU}{?0tJniQ6@R(rGf zYgsoIyV950hm9-QID$XPkM9 zXVOPGD*G1VD=?*sQ2@bfirn%JL0#r|ww=&MO`&BI_*`<>v<2kRUdA}k3;I`o82FSe z(PjblX%1=>0QYm1B(N>kBF_dkMjety0}<=Lh#i24)d7NsK!kT74+Rnz52GgnI{PZZ z0DP*Np8pHqRCq7<4VGs%SRY`0HL>QK=mE*RoIA(^>XYF)5(|~;KEp4wBecC>AoZDg z6g++ZM&%^vMC@VtFo+qoLh=@JUw>BA3jSU_L%;?fdN1J31FyJzqgudj`+Q;@*jZVh z&!+i>VD3Sx%p7FhMc&YiFh`NY#W!=d6A@ItVIOXU_Un#e-?K+*U~E@vzDkCk-9JfT zL|4Xo%T!2n)Nl!etXOXlZG+DQd=t!q6TOFV&%+a39#aRPbM^-OH$+!P<_{9wDV$|{ z$3Ja)W4Xm^QP-Q!bKAtE@e+N7+Npm)eTL>}KTtEXx;4GYh|~kB@dP*Vo?XBi^auDmz{^@TZ&Mbe7IHVHIw-AlbfR8fL&e6Nme!Mdw^+ng zM8bMMVGf=cVB=?ESzc$k%TT3D9eEpRwa(e@ zG)dHB94MJg7U(C5u7Md^N1-xnk=joXlX6A5h3}ZSQJ%>C9s^4c((POJiuX~#`a!}? zvra!wqDVc=UB~c>R~EQPOb8YXs4lB z{&zQ_J1k2vvEGBNV7)YE901I zhvwR04@HKmc<&V%t>El(knm*zo1H`)@z1s21#!Z>RVtn@f0gHNdI;U=;!2z&m)VD5 zTufVfGq-nXac-mKYq6t2|F=ObRY1lMdESB~{ZH#1GuRnBN zu~{3nH%*qPdb6`fvR{$5>7FP``f_cxz)KvlDwa1!VD`LBJ>cGQnMhd4pY{P*0zS5M zhwWO`q+DOi?DJ?vohzq!NG=coo*{F1*ZX677DD3UGK zi+4_zIB9g7d_@zLqP6q+|H=;gzv7+|yL&PyIsbw4Jz^W3X1@rVf`2Osu~jz>v^iNy z>ptqonu@9)$bJ}0Dz5Qf>FY~xp#NyQi=ML1s~_ZlI996sXnU8;Qw%o0NC=iqGG5uy zEE%UWMZOgNP;U->$iF6k=U>JxkfeETppFS{IJXlXTnGD^=oeytagD90wc2X1n45$; z%A~D7Aj>dnYu4}%=xvp=&^_9^(jTlX>h7Wo$9$Cc^3}<+m1WNvORNDUM#f}aaM3o# zU|D#<1V$fTeC~C|K$LCOFeb8i=3NYzV`$EF#)jk!{cQ%6;HMQbI(IavU6~^yA1lr< zlR_`b)-$j9^TpMy2+xs%udK(;J9#tNJUbEP$q6X>R<@Zl((=@#qFu}wxD>veLFK9==4VT^VXyYCL6s+B!4vQ4p-y*ji) zdY=vZ9}|bNyFGphM4W}r8@bIKogJUN157U}EjQM6J)QR-XyiV! zwSd9sAPX1x&YEM22I`N^HqHRBaU{sM3d6-$o*zW&=q z{{U+|UJ9Im1m_@b3}CdQNeK8<6jvII-Lo7j?v1%>nFUR#P|D9^{Oz<0ZT?6Wa?vsz z>CJqZa}$;weQn5rmmPepOM-6hsn#rls2$$Qn@~vPQF%5rBs5hz7`)~0FJgjS9@qIj zV5hS$w=dXXN05$SThYjpv(y{Q>>`L7qjf9rAy-P*=6)lX+=JE@{0t&9)3_b8JSPgH zj@B4vqyHT&*SDq&9G(V^_ zd*-M*$p_o36cF+6#&0q&qIYON@jv*Ol~08Sv85go-UT$?c^o|%(b}=nDf%_eu@55`mDK*6~qUv<~`mV>=CHXa!NP?gG! z*`B3XOuKJ9A$?Bu54kQD5m#5X3uoY^9$7pBlQ{RIZlG+tEMf|hT!0tP(+)J{7u-@G zQ1|2smDj}o+BPWG(RP;cvMcb8oTt(Q48l++8F)mmBSe-1MVjrxpm?cjKL5pbSNSs@ zyb+h`x#L1i;x&|crCe}@T;{Qr7l5C3`bcT87`vnRGvsr@$f8?Dz_hG@udh~b%Uh#; zFGg&AHOr_PbGHhIKIG^XPZ+ZeY4V^WKDq#D!GSQ%B+2Ob70Rcg^leY%Ho>KhtEAib z_96Sky|`&B_Xz}4gNFyNFY(LiE)|FS*d4}e(DZy+k<#)kr?FtY>4WM~UVr0n@d#_D zVKfzB=IdM_AZNE`b9SY{O~p#PtQ(}TCcRPLlUQ%6tY0n z&UatAOc23!_ZY)HP0e@eqMV44cKdM>eV;$MC^p|e$G>1w?gUj*?j_4xk-{o5^^oVy zyNuP)$ebm5TlN}5f2}etNqa+$CsFEBWmX)aWXtz%TPgRI21b-hUWum$zZ7u=PgeF8 zOy%j_-*9DgfKvteo48@O2j7JycizUXc{MUtc$`84Pc}=2O*2A`)Tcrz5ny)4`edC;z(aPGbZ{_3U+amT#+9e-?3q>)aOuzH|H~cs5 zUED-&sFRkwL`v;;;cnQ~y!l1_$}bz&=btQ{u1wFRiVh2{){XfWi3jFcxobfO<44P_ ztiAe9(}7f(R&MyX|F}9ym%00b(nJ04R+a3da%aR;iB47(oFwuTfA>@KPY4&eo49_w z04MBk8yjpFg%8J9<>nT?s6KA!$uF+BtN4X1A>WJ_j2}_X_JYO_HddP1NKS#9Foxz>LcXc{U zBGhNQ2<#bNlslvFM*TiRV7|I0QIVFrtMZV*YPBz8;ZIE;iuHi2@pwT`X0~3M`#q&v zyT>{$@q^mkFPxOY7}I zv36p-O;C8Y>6yMMpQ^tlf1SIw=Dc98byVdEe4pt~Su3a3SX|ta*;mgls7ndZuFth4 zV(OU|QEZXoc}`NaP^Q=0hr38tsau1L!t08aE57h|NlM%o(pQB2ocu^1ZlK+2OiKV( zTg^O%m+nvHUlo&YE1$;b&#x=dWdFiv7R_Sx;qdbd8Dld(<+?JQQUWcNjIczR$%TQ( zwi=9#o@j&i4|8hxQgs9~Gl-|)GoP$zm*%lz-A;&PtdEXH{_elyfj#sT&RXk%Y99Ni z?s#Q?cDa0J8OT1wXP5M5`(Q5%DfW0yO#W2Xr;IbU0@nSMSr%v3{lwIq64t9&tzjf@$Hh^{`^7QMO`@8BxmK6zUQnBkuE>Pe z%3hRy`ui`tw)iN>!0HQrgY!5`^F+YS45Muhpg#PcIUh()T$|$rtd7mp+kgepyR^dr zyRh%7L|{QsjJz0Fy22p20Qk9;3r_)Yj%uD3FwQ+p#R8u#pQ<2?&}}OJjy{#umL5ez z`7?_CMVc{w0Soct49pt|cV`^68sI&L&za}JgA(T&OQ6cwNc|#cf3&Yg4mpP1QGNkG z1O>@gf}JaPl0>k^%_iIf);cP9eqh7gBh(E(Ew$}-vF`3-Yy(DjcPe%l7It@G{`Y@! zPxj!w*Y56hyYKfCYe|R7C&)SAjWQbPT7g%|8j^daR#QT3KK4^>OWbi(D;V zPw+a>BO%~#?za)%z~{K3_|x$w4pPoc++%AV<2kNoV*#}Vht;JtGFVB9YjwYwDZ((`{;vH4c>&RSJ!9?{BjuG=j zUCx9C0K(&+j3}djOT@*~*r{5=d zfjw*3^f2;$wP`%Zj2888&f{b2R7|!o?xRpCy^t6D1VU zy}Y;D^}^tcSaltLIQfifAI~*zx%?uhIbyxkkG&~yT-3v4c;yI;=G~1XNj#{0(+`iHB@c9!)bQ zIg6G%<#eRhOWraoFfU;!1G9b@XQz{QjFH-N4 z#w4#)UKA(BX3Gx=Q^IMITm1OIIFT1O*lRjp!*+1xa9%LyZl7Q+qFu1wN8d=nZ3HQs zNn48R>%A(=WTrL!<$*j!)$Y=t^i}19x_W$ZS+E8LY|}kg2j=!_K$Tg#n|hJ_XHvJa zRC*zHoqU^E5x!eeEj$#sPPCFg-}5{_htuSm$Z2Km-#*41r;k`~r+uYv-w2Zbk)*}W z^=oR|rL{G(>eW26s3{^SsL?F>l6Qq+O%nje5J3t;_y7#HRV{Cx5QWW zDZoQmBYy9>oIhD`)8#5>54UD}0rNO3(|R%upszDf$mF4-rn*ZVo)YVt1Fc~kedXn* zWU6&}L_G@WEqz+UMaSyUl{GoPH1qYRPH>BgN^T}vDYs}IAF7hI7mbI0lGrHv{C^19 zQpDp1@1bb3OEG61Z*aRoz0cln_1>VJ>@ZM{P%Km-onDwQ@yfPbj1~J%a~WnKMcVKf zGaU)5v&GCoFQ_JA*5ve7jA0y3BUX&|{RD?4kdJ!d0dnHP9T<>()D97rUl;E?7XxY}gO(MQm$@ z;A+&Hs?XT(IqHf)?Bf&r%cR)H32$_Fv7ZiQYZimELmP^=foA?S3NJ9!qd>X`%!w^` z!H1rhRZ7QAB-CrK6)(ZXg~BRV+$Mj#JQ{iF5iW^CI$h=pU6JZ-GVTkc&T1wr z18Ffypp7HFN^Scl+B{KOiyt+Jy}hxJA|+4M)stVt-PM1{`%!RZ6sa=DN#9IbmHMo7 zI+2^e(HOyAgj2*CX+X1iWKM0ZL5mn&^^++;ZK?=oXtSS`HPPEsMI|$7Hxl-1BB(!O zZWSr18$&lMu2X#don$k~nI5afn@CdUhk{kaOWO#XJH+J%N-iPU1g8GPcPO5;jtCos zjZH5F87y4G622Wdu-2FR6JA{<;#?|xUjCU~pIuQF%#x%Y(Umgs2}_H=GxB39irnd` zp_3J8T8#g4X*Jb*-(S&Lilg&6{vg?Q8;V0C*;&nE+7KN~DAaC(n?l$cE4?jDY$8gI zu(sCMi{6m_)w~wspz~Eb1*V0HauM%Z_R-RBTx#lCoj=F@==EYX>rYIU>NT@7~eH8cDa@Hzvxb-gR!^XH&S_QaNs9Z>wpH*Q7}%Y$}P+rf_MUq5LA8+hn5%WZtfi zmQ|6;Y6Q~7P=4hjF`;mO`5xiF>;A_b*w5Vjt5EuJfw8U0ih%?l3+mWwz_KS(mfy0;H6 ze861coW;9K_uA&pZlI25<4jc44Med#wFO%;A#iKdX}>cU*Ig<8L3~m(ujn7x zR(VJ@zks3dQf$hKE?qBkNio(INJ8UZt1pYPqh%^%0X1ZS{4h`K=P99bRQp1N#Vnb# zKTpM=ZnI>gX-S*kGF&O!O>~q`#6PlsE&Iz~@~<`Kl$A5e>$D{qM6mjy))fq`+)})% zz)H_4nw?6(2k!w-prn&5=a#JoarArgNq_oAXkc zP1_7t!tvwUO-h$|H#I2x8ueRwUQ%`tkNgt|2p+ zrcFtu=D*`pwbP13M;@vVDUG5^lohg~gWF_}By)Tzk{QDIy{*C(JpHcgyhZH$Tko>o zF(+-lNN=VsG-;(|lUGUQ&9|G1c-f7!>mSm+>vq>}BA8S|RhhuQic94N&#B(F%rc|5 zgr%b&lWU$8KRp7e-HLWc`77xPR`6rlfOODzulTWOd@ok;k^g#^p8JyXY-=BDn0bEl zAe}+enRHY3lk=s^n+IBlx$hf3H2tQXsWWZZhA*v7tUV5pE1IfQd1?B^5ainrMBahkI4 z;LJ$0?gMBPysY>N=<0J)r2$j-XysvGsZ*=?0r=LwOt1~|wk+VpLT$#5jFa$o@v2@6 zTn)#+Ycg&h72Wm(xrdWCk0CIovf({qom*449=?}eQtbyTlcg0$;hZ>DSr!}^d0dwc z?+Bh=90G6kNm5zBJN8IrVXMkvxb=s~;zC$~{>-k3>t)ta2p&NKP&fBA$$kD@`DLi*(T*Cm=x&)DeW`K3>Y5 z_^LfP>0f-HQ;ETGFx|d__ZN4;l4o$tsf|z2<8XPRwr)K$nf;;z&EQa%w1(5WaNC=_ zXzwul>%(d0xj{7uH7Px?vWilX?5#gVo`~C5GDZ%H+^BhMP;`tI-6Spa*{w=H)SyS@D{XBFj1%PsaA+>FN42H)YD zx?9Yxxn?y}7+&e-m3H(a$s6^)G+dl<$q{N*1I;8 zlTdhrSYxl}`4E0ts#$zOurZx>8eb_2>gpDJW5ewn{vf5YC7mZgE;Pn+)6h@qGCBSy zKUK@v+tR*PjIcH&eJy*;ToU_D_lxmg#C^>y`s^TP(Nx1MajN_?<=>uQ$vN^bC#n!2 z{jt|^zmsNJmau$@D~x3{D#1cDrz=GAgT1TWT+F5_=3B8l@csiO?fZiwyEsaU9ps^S{vo}iH`dX(r!BuLJJ%@XUvc^)+ z93=6Kn`x_vGlhpc%hl&t+%}+S9=WeMN*M^hXz-JZ&{Jz;rB6;StD;MGrfn$i6tR=6 zO796?#yV^N@Vz6p7cb(L9{8b}&;IL8lKo;Gbblwl%qVj55Rhq)>=EvA>PpKpCW-7} zJV1R$OcVU*OwvhMTiTp7<>Z9s73yx7)i6hO9aUYsPVptDugX`x^2CL5v~+jky;7z4 z*x^sw9-$!ollm&Z=RlA068E8ZsH~Iy%l(v?#hUMAA@F1@vCrY$qRp@@Wd@Sp7+<8e z5YGw(otF9#=7qM&rPk!BO^W#lp?MPSo`#NeGvKiP` zx?u~}+>~9t2YgThc2BxILNsDVR8+SSVUVO?rld1PRv z(p*xpKUFqE_{@E-=pujN<d-v9{mHSA3ME-Xp?pVA`muyUGg z|9%VUxY%#^4N;lEd1oO%f@@?ym;IXg*^Bm&WuqxS@r=d-HA7E=hiS7k}cpWLu z1n#(fQr^Vwc04XyifyoK7p(wyn!o102QO`y&#Z@XIM0WCA#Ykj-*@mA{#JJt*oxiV zkqaXE+}0>CJo8FZB)BDIPW?e})zN)5Tfx;a_{wRZWoSj&Am|>T&?SS1yrR^fK#JQt zWgIx@cwDN6X4>@$^-!$&Pc9J}-C)M_gWWj~2m5i&w5YyRTmb%T_d4Vic1!ysgp-f7 zoI+eO2OCG=H^+a~4Zu}LcU0?OBqqNC4Id6wm$|~80etNfc#l`GIuG`Ad!cv%2Ra^; z+QQLxBSL2wV=2*w&^dOCP z=LDCLW{Z`aHl%Yy1cQnUaYhEZs7l)Q-gwFfys~Q{#S8e|c7?3T4{UBEPsTTd+sp;@jxe;js@Q?X{~Y zdGQmKf61p~cI(H;CZQQ6dJ;Y$RTE2m?PXsyjTr8BUCttmIwpu;5&Z0K3FhL*EVgqd z;qx{S>G$x9Ik^5oP7C!z&k=SzKB{vjs|RRr9b%5=&uh{!?q-J7E9hU2OKQaQ#qoD4 zu{5U`6MYQzcxY&e6-5ycR{WHF)yqW1BhPg^C*Mf&c8nL3i5$Co{CdI@i@od`!tM=n zx&W`_g!dm1WKajX-|=_jH+P76%K*Azit#b;LDP2jjm*vUwyeS988w#7miWfXg^b$h zzhxikwV~T}gS5tgUBxi9&1=5OmeTFkC+jDVI35;nCS9=`=SLHtS_HC>5`Jx{qlXZd za8~y{knE$9yJ68d?n8%_kN^a?ycXEyl{KE_JlUXBMv7bwf^9`a|Bo>)Y(R}mXF4AEB*Aole> zP%($PuiHol;;Pz5C0~L0EmU!CUQ%O<@K;7-U7~{s;>{<`@66@J|2Xjz#(=U~W~ zZZ_+q|6BDalj(U$na{9vkm>7 z-PerQa&k8`Bo!Y?&#ygFX35H5%Y3i*+i7V&Mdie@RfFvsMUW>(Io-N(?pTW z^Kn}s`NNsKdU%CzTfPt2U9o10C5M zT^f3_qrpY{JAG-bV{uJ#TGiH~gt&|48FlqW4;Qv&SJF{0n;pvM zo;=X7qLiIhUAsuPGI>tbykd1+j{awndF1nwXNrb{2Q&k+?SA|sP^|W-Q>+zCxVA{t zygfT=h4a{eos@fsQEHLPpLzZy@FD4puM`|y;u12qRw1Tz=FJ zmf$`o){e}zVTKtfNi=ukXeyU%OLrKZ3%C-_4%cG-fJX+xG1m&-_s+l!WurQWFe9md z+PIhtiP+{)%-zF(>i=NAMBrsteFKy1bwbxf+u`;m3{*828uIS?GjiN4?c6SmPbS8HZR00A(R=J zR}S4bTFEko1L&TozToZ>ZVoYV83u|CZhFC|?wd$ic7BHtIhgvjH3^xTnB8Op4<7zd zcNZ2%psF!&cF_BZ>2Rd)&e9AxbYG~Z3yyL*pn47;b+DH`g|lqFi1aYWOu=)5PZ>Ef zyO4!+k5eZ}j)e0=bBJ0nyswP7tl(aEAc32Gv||ooW$Lq*oA{E%V~tAu?!zzZ{Bh4C zPFB6giGyC3V{swA4kfQ~M*BiEZpd4gAXO%E+F^(6B+_j=N#u<5n$>e}B7;WZ%xL5^ zedovrDwohbXi#Ao(xul?tP4(e4Uh@hf$bR5)6{D%UZlXp$i{iZzQgxwyNGTP$E)HA zLqU)9p9o>Twk1jUkNX0Pd+`F7K;;koUI!=HG~7Sixx%ZsGiJlwJe<<#B;zmcB;9x< zi}8a{I`EgV9<=J^(Y*`mySCCW*^X_ms4c18%}tbv#Jvq@is#|ewVTPZh_K4br0+qO z^+}{dzQ($H#L<18#qLBG7e6I|(CXkKl@i=+mkG1*m(6Z-SK-r*xQq(?Jo$ z_5j4r0vGo#U}Y5uJFA(6*{j=*G8n1F&DQj?M6>$Ow6lk+Yjo6a5w4Z~RLh{wvd5I8 zz6*6n$VK~{)MrQ!UA&abN#+i_rCW&wwi|?t2^Y=YaE1uBM%9d1Jcssn7!=GVWDLya zUB|xbk#S24vOAqPNY=O3_w1BZY*QO6CUIIl#0)*mtl7#4i7=|TOOFcDl;zP9eST@b zQnUA2spBb#%Wg$2S?I7^a+y?VYbj_a4w?NmeEV^u(~LFvzqFp=S>jrPTfa~gfbH$E z5PT@u+cC;lW}Rw)4;!{i1bWeXT-J`c1U`u=?@ zihk2fUA8LxsM8#rCE4T$wzh(!q*n9!oXtd*(PR2OLIRCAR4n%;Oz*dm_F+lgeUe=T zvpeEN-C3O0l|sMNRgL5P#|gT+avpqmTlEpHarpU)h3wNo(WSMl9G@QTR_5xxe~MJ} zVHachTUv(0cFA(;d|P|Le6r4bE&CB^yU`!IhR{cIAM#h-#t-yeSDIrzyOZRF`DZ$& zNiSs`Z0V3}O}X1h5Xlo#>H>tb5B;v5&c}qy%iFl0gKSEpIcYvp&3)FRy$_4LnUOC4 z$wlW87pnh1P;A5~od{O(?K19D>f0ZoEmKm$n?J?z&wp%ir0KDiUN4w`@@= zQbHSk%4?54tsRh_Is{h%;)mguC=g=)SZKv@_D$SzPY6XKyX*F4$6Dj?xFF?%xeCvHcgq_ zFsxc|R9wrH|2wp!YQOYd*c1I<@uLIO60Pux&wm;-e)HaJ6@|-j?v-6&AKCs-G{M|p zYr~&PJ7@04`bKNjcfuUy25qe{4;8 z7wGxsA*?>i_y!cMgyc_&A2h0&KtA-=SLI=lF7NWMdCS_~lmQu4&AO8L$2}X;HTw7` zwcFJmhBudY%7nHTf~C&6vSVD-_KPA8>x%7Ko}97T zJetL(hHv1}@<{^nn}MJ8tq9mlt2M_g>N2j9JU)n^|TxV*1E@)Am9zBuHqxme_MYm7eap4D`2?GMF+;G8OZ}pkD z4elvA5c%SCMtvN)wrz=G6LQwtO{_)Eng;Q{Am`SHv#gK{#1Ch_QP~jc6p3;HHG60U zWob@XpPrnQ7Sp|(G@NYQK1Q;Od()Ce)I~KnydZ80A=O3@ssj8f>j-XMugjj|AG)V# zf8*&+=ZoIs-M6igH{d5*2a0#%&Y7n0*f`aCH1j;}B=PB)T*JgSZR9WgOX06U3VlV6 zq;EORGtIZFf(j=uZ1B%KRzt6WcV@ER|RBI?}}wJ737 z1Eqjay3JVbLD*(}NOT21YKr68wyXGQLIS)(6smhWJ+x(q{y$uGmOh;Wb=p*gCrhX*iVqPD3gwIcb}@ zESE&E&J`^smYCLZ$%Iqu#~F|CuZa55$>PnB`7l*DSXeW#Sdft8*`wqyJn^-23->}& zTk9xC9!F@3W@ko))Qz$BhuBqzFiis{m$x!zdDWNvroDCFt1+YAbm~@Zp$u-bkS!!v zSVO`N62o+u>qJa7n!?Z!_7j2ALYWkt7+x!#UkLZ>#FU&BJ$9o1PMqo(5U7*rtucJ} zxTwZA+~-kNhIuJBWNy_f_Fn%-<-yFaUeXc?qr%<3cz~YZ)TBI5TeHnVT1OeRrV5Xe z(@gJh-jQY)ZDb@9lthoyE~=^Ez)-K^K%q~+hfJOQsQbL+*9m?{tT-_#s^z=zW}HPM zO%NRUw{AY~!of#X3~sQ0ul^hRrWa7+#)@%YUwn-5$f-h^MNc+RBB*z*`NBn%J*MwD zdV|<(FTqgtQ#dieNB%KyQC%GQ5mxcM&mR*+2 z@D%G-2upTnsqOjOom>=FoaU`RB@0+q)}{Pgbgr2vn@V|YbeC>R^20A18Lcw~N(V!# zsrkEmA5?tIeBG6%Kax`4_P1m#f!-|C#vRUSFjqg1NT{h%Wd$cyZdXk5J62XArFceY zUx|M0c2-XoL^>^%f8&;I9T(qby|GsDwG30U18hSMxzR7$UlMa-3N{AFf7^zQ2R1!Q z#~#PrziEuk!c?BUiv=;{z9K9SlhW*gEyqNZw`2P;F{;JbF-#%P3_F1-A&!9SFn0<+ zf&PH)i53t6WU*@vv%5db}r;uZh^Ie7AeWt0Ps0igFOkJBYI=i;ORmX_BMF;#6EB( z_~&pn7zS-RPy}+JLwjt&b5QwK8Z;B0`n?!)g^YR=i+Mt-9AAU^PJDX(2Jj!z>WmDq zBw%}!06+ZACc`@NQ%$4S!IB?&d7KmV>^}x;g4@a6we}EXQnS7{Y=v z5p=jL4nwBF@~fCmDw8t>^Nmu1?*?{}PZdlBxTLQskASx%t3&ZvcVb*%5|&C3x=+Vm zz@N3x0T>#8|}k?aGHhA;pKH7H_%r!SfDSmM@l;} z(^!an2gZfz%g(?Ubh~(a%n0pGJ_1-!2UAi3E=3vh8F)+14V;8^A^Ges#}bL_?AK$@ z;Xiy)prTb#FH=x}f^vTcsz}y*bt-B^I{DNs)F*LRR||Thu(=+MjuIG^E=F^C0+|qf zp1YiN1GA7V$7wO~tTlOt#z}_s_(#B%zAT0asHyY-CGeH}d-p-C2kH2ht5^=<%I7PE z<2vjM4QiI==)D}2y*lv9KGY$V_o=lgh9ac%FREXbUUv#LA>ouHp?8Qnq#w|Ef)A`+ z=x)9lZXsq7H#jdJ1F{Q_=VLxG1u@HkXu96elh{nHb^C{%ORn0&#vUe?e4bI5RdMzC z%|e+z#V2KEIL-C055p6C@hwq{B z;+MJ6=+DCFW4V}AUT3sD<~C=Fe<9$`G|y%gt)|sA;9KS}&BJRwRCflB%yV4xrwuX2G6lf4SAk z3+PVC-sEo>d!c94AV$M8^!(q%j0teb;ARtd@xVAVBHMurVvpMW4q@3#!j9yfvrb&d8(llL}`KjQ0O4 zbnUs)I-@YJ<9p>qVMFU$^=RR@Mmr%5wXfEVwiqR>+zD<)y)B!U{T3ad=}q!SH>d(5 z12HS4b-rU5P;kd}4(2)grrm5HnAY>Quz+zb?@?+&!=;rs4;S1%J9sX%aQZ1se|Vwe zpl?fJVMeb_Wn5uJ=YQ%Qg>PH#2>zj*8Z=Zkie3|pZAU#Uzn%@FT}!qkzC|n52@${1 z|KzE@0!)Hvm&*mrDCfTIO~9Uh;?11`)$Q!@k%I1P|86uD+`Gg+TV6QjoJn78q1~yj z=E1_`!PttCLT%6YqLRX=9iSi&wWawV^*3mx?UyCUWT?QQzWv{RL#kV3iJ!* z2H!x8w^;1L!&Gq}+m-^07%M+~MFubzp3Ox#7|PfNBp!3%(hg(?#$q@WG2Gc582BCf zUo8>7g#N0FhFj3T3~oyWW&@J|Q!$YU0VZOEd5d5I=K1lRFaZdNnQAx=21LRHY~=0$ zm;lbPH+%}7|KN;FMvR|^!`EQ#oou)Qw!X-KNl?EbKKnRyr2Re|2u-Vb2k!)Xv}a&5 zP-^fYF9!kUWOy!^ip+**gDJU6cp(TJI|r`@i=tKV25{7W0^S78ar+FLLrGh@;LXsL z_e405bn;0FY({LmJq%AKPQCCHdPImGnh%ZQJKOg_P56y95s(h2(mFvh+!pa)h>u)i zfDj9TVFtuT_T^eaDrD}lBTxhUDB20?g|GX|pi}U5HyLyuzP9B!bQQk#?kZG4KmGVS zM50~1y%tKM-o4-s`BA*?*gUcFm8LH0^Fu8D#ReKtAORJsXsfKSOM=l)Ukz3ph+V zluQ8M5am%B&@93Ozx)5EyS|0Y@p*Qm(01I=+f*<`^yZNe3=n4D>;oP6f6l!IH}S*+ z#^6fszSbk)0`{*eA~=WDTZ{tdGTEYo;1b3O+Dp)g9tbS}x6nyv{G8el`I=gZg zc9+;&y$kCiqzgA<{rI=2XRz@+Q-fO(4BPhQNu++|~`+!vWn12(f zm2)Uu;DXc{SNAB?bGB+G5KDElGD@5`mPa zZ}D&SWByYW8YQ5IId76{PQs=oJz+emOSS@5D0aP645_Zu*4 zeSXyqSkZL*G#*$~f3^Dxu%zZrqZP2K^0ht#*r@-lqyqM(vw1wgU%Q+f2V|;OWBLHG za%N^ba8CL(Arw1XR2445?&Syg9LMIfdz|lM%Ng@*O|f?=-p}6yrhU)u?E)5cqpuKv zX&p;XUBdip>FPEGCN&l|;DDKR`^(M%3#*qa-T>>%$GE=%$5IvP3J|Fc#kc`r(TB`^ zK&_(i=xgAGNjXEipM>*TD$5}778U$E_zZ_mwur6VnO(ZG~}*OxC~zV%w2 zGQoW81iP@9&#j;8A7Cb$;4&*Z8OFz^NRM_5z|y;EY6oSbX@XInbubt^W)sNO91BC>GQ)^F%O0sIvp?% z`>N~Im}gy`B|i+T5xFhqS5p@!3|LfGMf3n{tB9zJK!ARG`UH@x?TkMIsEgKxxdZ29 zq23)TdX-}i%l{%oc8&tE3oqV&pR~C*UN1e?_eID&mXqK+#Gf6;9+hJ{i~aT zx!-r*pn8Aa#gi#9pV~ax=YW}w9|%K$NiB?u1l%je)7e0RArm+qpck(T{RlM4|9DRV z?u#mRHDPCSZEPH|JLs|(FG;o-drY#6o!gy=vkz=+;#WLNkW`X^Ut@w8EWVt0i3v^Z` z;jO?;d_3+IxQr5ms|M!*!?;3lURE#82i%w#hFb^vMsSd~Alf$qX$OZ~dJr6BZTl7R zh4jx6L&AC1y(xq=!pIdIVIjff^b~w2p4!d8r{jJ$8sRtLPU?lYapbr1CQgGWdB1SS z5nu8cZYwe!2*6E&uVe)yW3V>iBhn1R5!(?OobNl;&@qj5$wn+-hHVY}87_HtgJ4cm z-aU*zOKrXU7@teIdFnWRBV|h0SKJ-4bAzF2j)c`u#zhj(E4^_>L_6L|#2T0GQ;QgSlUz*UE66?D6u1~sJhQ=5*k8t4@%F6C zm-pcAGaE-naa@L`D;nodSJi*Q&7+l+Wg=Io?TSuBK^fz|MN%nqN%xT5WDiUnvW|q$ z%tfXXPbHYa?}&@TG4K^al1~g=jURBn1LN?fw&rjsj{X$EeH9)Xn~y6JOuZz>1@KBo zEO1k};ho(`A7@2<41#ApDZP*QGrJY`$ZCd~n*zV0b4W+v0U8ng60V@4GH1X_O4`vr zm`hFwUk^j1Sf82jQQ{G24(viWX7d7Gh|hd-7MCr1a_2B^oz&~%d*r;hb{Ij3qD`G^ z5l=yRT^}-+zpK;-zQw&R*TChR0?s2C&E80S2!}B*qqAWz1}Z}c@1d_c8Uedd+roO` zZImeQM%ak-$JrbDODwjjhOQC3p8SXWR_kx;kXqI7MHeJV@nYyIVj^GJaT0zejjme* z*N7`i%3-W%s(dyaEQsR-!KS=HVmLgFYmWK=O|YSi_0Tuwh4|OdXU6idIOrWM+B?qB zUM+K;3JsI4+vGzU;{3-x2)nfM)>CA^4toJcrfMRF*1|(-xAs1mrrKGX28Sv38K%-^ zvQSwP^jDI^Zh~%zu!JF~UBE*fgBp0!bTw4R5yYdRS{5PnCsfTy@ty-|X}foILmbK+ zn>|o2@xmh#;##G>wF&-QvGe=@TvK*=Fdoh<$#0(pJ8C!Aw87KVZ*)P>EmecG8){VK zvzJ0_nLEJ-Dv(Sq9D{O%E$JJeY<_I~3@DrPEmR0)v(WpSpfviET_`A)>TI(B@*$DO z2jRaBl$(igL+$wa>2PYb+dvs?RZ-k_5c;BDR5KkKD4}UJkVHF8x(q5%1FT#qT=@+z zhTP>Tg+Y*uWHe0;xro*t5kM~dL!tJN3kTjG5A9^ayLLcZXo=SM4gctNjZcNIw8h*g zgGtTl=lo%}27Lb~=wGe6wHO+$I#V49Nz4CeCZMb`d&x=YfG(DK2ij81#Ls|MtLh8R zKuhI)X=c!3N$(MJXpyii~%hi+iBJ+8JUG$f~k}(Fx7f`yvzIN8MQdW$1SN3BP*5;4t@w69V=yIo4rd8`bgtO;|HJ{Td4n8#X!P0nhLE?fnT|>?St1 zK(h9Sm4(oWmat+oXkWt_Q3GUDYt7gK{;i}S1>obdOZidYW$m}rJK%89pSY=Dr~J{u z8(_V-$jb%P^EW&BfJH2ZwIwK~-ns7#7oC56^*wxG^x>!$o<97zHxe4||J1x3;`FSj zxCb5W2vl<*rxrxC5n9%urt87ahAZzoc(bA-?=d(~DoD)+8#Larday)!>mV7FNZmar zK&BwyX)%an{AMGFo zNN3PNtWTt1G?8FR97CTed_zpZEKA=<_>D=6-$meHu7%zr7z07x)9}^6$6eR(j@Txf zJX{}0d+0>{0;S(rLXCmuoqaEP4{R$d zlXwN&gs&hT#WohA316|zX?F=s?4=|331--Tp+tNq=(xWV?+?;<@o~4ozc#yY$q?~@ zkm^Iox}HmE!rwR>LfMGl+rNb@!*#SSA+N!?R{bXFkkgtoBzxqjB!$?IEMmSThQep? zTL`aUPT?#94nCHaN7w-GJA&YQV9U^L_z2i!e>Uz7Y_}^I2f_h1lW+@Q?EQ}vEw$j< zP0B)w=1dnEN50ypB>g2XYQd9;q(fC1q~*kVjT^C&xI{co+(RHT(+IZ-Q*o_?Ts*x% zOjwLxmo^{YjB7q(ijTxaggnH3$IaVsj^iSib}htNAS&xqNEecM{{UqHOMT6WjAio9 zn2nvFeIsx~7&s;Vi`QM83qH~50&){~*V-U^z(w9GCzJS3u1ZK>c(PFv=@>V> zH-~8G!fcKwX0yIjh7)HpZxnAKXcz;AUTimd8>0Y!m)3z?!b8;l{9e2jW#ohhZk#+C zmx_~;E`~Vbe2DkGxX63LHzyXtCM>g#L9FmQ?oN=WNS&{|C*_LUPCp^O7VYY}K};8n zG@T>-;vcOTA@F#!)MW%)PMhcu{yaN_{tKVZvO{9ePEU0xdpn4$hOh zX_oT8)4PZyxo?j>ah_DrWKB?s z-&I%<97SR3Mfe+nA>ny^0pF3HgI~>UfbZajI5v5=afK{JY6i}UIVrXj`Av^KNJiSI zot_g&3T1`U0%RE}+v*40O*k{gC7J6gF0+U_jpP)SXsTv);|aAYP9s9_P?VGt@DF99 zMOk>9UJEAhi{Va~-f57Qf zU#T^?g9;|J6*pbBKKB69D^{k=KybqOhZB(fyo}&r>@3X zsb_&@$bF^%$qR^B1{~)ikz&Q6U&tCkS@070E|=%g56fAhJ6FNcv>#TU@Cq{hRvGa? zXZ(c*!nrp0AwvdtvuVc&eym}6-8Fn}?V8eO_~lhw6i;y7<(|Ag+_BOi3KO?f8wGAe zhSi}bLlHo^^Y})@NjB?{44x462KB;i{DU57xPaa6I1W3||5&YozEJFLek1Pf8^17v z(A%9jw2ZK){bPp--cY1cw;i9{7+mU$pIK+A@W<6uPvP0(BFfKDrsAfQvauSZUh9!_ z4@oMzd#nvHQXD$u0^gPN1!chhW9TfrqDA@=|2|si`)6Eg8+g3<^ z7lt&U@F&63x<0mDAg%ha>!Tpl_HWKJ{wMQ|jR?P9uZ+LM->P1?vW-7ort)9TyC%Bf zVc|*e5mzTdi_ZIhNp>OL= zc$eT-;|;dE0IEH_3lR8JvT}s{N9Ch8F5s(;d*fmLYR$owrTjtiEB>Q--Si0$25%Q` zyMCCCU|v4=N>s{hJ3fsnXPSFr$eT=cGek~i8Y@o|o0;u~4|ompitIV|h4~#jjK!4& z@YbUhWnd|cd@Z}WJr0RyEl%%*%USj{r=Wk>p5fb|73{y3)PW>N>v9{@atDRGf&b%XEZGgt=ic^|0xVwMH6iscz(0G$F*tqf z{XuRO?(7aADg>XKnD{#ZZa;*F3!L?TV@Cd2=`-{zA4Siisr(pj3DU!#R??14<`38= zfp_y>r2ht;=iOQ32f6ZYhCK&4yq8Os0T=mydS(I6{Isjjs8n>z*#)Es(f1Y;C*l8f zwGh9+^BO9EhDmkrOc)zsCB0JNU$ z*A+P^<)2;(N_m}tW8-?*@I~q2j*QL{Kt!RMwy7nz%rgf4b z$VgERG8o=N1+h;+Ps#Dc&QKcuFn0!c4XfLD08B(n6NUkO$jX)X06%!Le}>Qqe(-1! z4hGI&-bBP1MxNe})4J3ne`1$4O`V0<9QE(@CsB?PusI?36a%!`NR$i`^Wb*r7{|=v z;viVZLu%T4R|hnf>dM&%Qsm-|TfotHU3?n=V$Lgd!cU07-%XeePw>D57eMl_HvGA@ z?(}$kfw}W=5w_QOs-q9>(?70vL0xs@%Rz+FBxugSUsO{16}(Mx1^N%VC7;DQ2d$On z?pg-+i!C|PV3g=W>JOlg{20Fp2qz}2Tqo?o-2L7P7b1QhTLe~U`Cr3uT~&DBR_uTF ziHCZzx#cf9W}pnq={gQ_-PCElkAxbUH1pvWT^*eSJ8P<-JV>jmVnsmX73Dh*gCZHd z^*Z>gq$pJlP;_YgP+$}_Y~@HHgtz;33VxtT9sz>A(5p)cczXSrQ!SXWcI%<>=;P{v z?Zv3Sy}b4cqOm1dgOH)tp=uG#FzpxZhh7?rz#CAu&ZX=icvpRSXDGN)S-f>Ca6>*g z)f?C#0pse0S4FQv4+|5?C4M}?MJ(UrcR@TXyQsq#wXQfd2rFq)AIwF2>fg8Zqho5< z)p{d4s}5T9@c-;SD#vVGUMX^gj$8f!S3}E;U|ArzPxrj&6zHP~%>D#4Dmf_^fcdhz zxDCP@@qy4Xp*vOO7bU31_qe|lxFfz7Uf~n>Ri4~{ZEUYSa1^z-bhLROUmN>t6i9mA z2g_i%uf|iA4lk|Dq|{J@jdr-tT`b+q9?)pKUbGJUQ+GF;0>tXeDaF7jMN8ZWAuKHn zT_PMx5Ad5HfQVi0Cj`TgOBcfMAqU={XvLztEeB?y%FdKl5_#F?RQ(l+YaVVcfV&$8 zEBj&3+5yxrkfHJw5CZ*fyUGj!F-v3NJ>aLYFxv&#t(%xKN%%@-jqMiZ%15vGEO;c2 zT6|xSMP|8M_z%(i^H%KZ(Yq&p$Ce*jyI+WsJ?C5ABKP;DS8qX9whwotG#i>vDFfjd z4JgTn=vsf_erRyzUB)qRuWff>5%ALdGV3s~*|2KEA>m&dAhys!GSaQsCpaO27dHyR zs5o~v|0p*2{0QuylRJ*@#R7Vd^*lkr!?RkFkn8&$>q|($zHg>b*w!vo)W8#31{0ql zprJ$<;2@*jVnl=6Di`kf3EV8-oRtQ|m{*Snk8Nu1LCznxRV_li_P;VVz`8Df#Z-7yJA){M7|qp! zPVia%rM(xx)apk&XyA0k+01_de+#wYH=*6&vHFH^lDd4wQUNV{yLh!=I6cYTn=ix{ zT#(3rVXi*;hx8}Y=YUS~iRs)nNBogFqo#}g!F08x(PNpBs>dQ1W~nHMTF$Hh_mEl4 zXJsV>To$l%9BwX?Z;iuRSc6i1(8DZY+#%!)dqU`S_zJt%PXXQGP#$x@huoa=Uu4;w z4<}G*I>&Ngl4K(%t+iU5!EvhzrHeR&&5uP0`;n@Uvav5wedKZWU*JgMHTz!Kuf!kh ze~MagZ_c>vuh=S1Xv*JcA_t4(A(@<;p;mY&cctGph|N9j{u4xb$>+C9PY5l?e@Ko9 zXYN;tj|gn73+U4V|LT6xJ^pp`4(cagsQgSh^8=`4GKN0@&=U;aX=W8(&a)LIVaIre z>`L?|&yZq9UhoWYA@DbzF*F|@!)y0j3eDubbiWOH@mHR^D)mNEj_;Lt!lwNb9ox~b zTUzPGP;~WLQ3BXu`b06n`APv<0W^`1h)cjSU=cA`c$pc7dkc3Le!*MnXdganX#h_$dt*oPy~OU5It8(LdvMjwHPvIn9=!9^**AhUtm*!^%Y z;IraCC`EW`@jb9qnB%SlXyJ&n)sl&l-;bq;f2UvdjG!lo&Na7)oT#3vG-?IeX&Olu z5Qh~;LXTe}N8{(PSHi9MQ0#XGf(4;Lg;D4ZWLs7vqJd=_4#7vD-q^j+Q}El0BhV;t z#o}hr10dZ?fGFXuvtz_c<>=nyv`XI6T_)1Y(wjY~dg=JeTjW{sH6um*OWPH0gu96A zsFX%+5`4!LWCX(nJx}=Tc#RIlr(~rff#|mlJK+N4ZtNmR3!hv8fxS@YVgc|BtZ`2O zMg!(EMPd(KWUmLkNK@bSNEED|)TE}?D(T8Na<^ilai{|~QzNgyuSmm*S@lFg=1_KCy|!w>y6VzmBunGg@xdf+25zIp|phaORG+;a#WC@(H3M}nm6 z%z1FJn6o|;st}c|jsS0xDa(7oak!`NOCSPy?e;>*g{n{U=@>|B67=NI7Iqw z{UB(U*c$T>Xcu`c=L2^Mvu`&r1^v&hPnZNp^<8$@Dvuvlikupjbgris)iyOuBx9-< z+HMlN?cF*nUT2G!uE(BP9-}X?xuz1{PIQ}L!0sbRwHC_ngdeHCY*`Qgq1cjK0fkFD zV=6!<{dRc>&_)gRMTPIM|J!ibET ze%&}+RW(=Ahh3`RqgmJ(>jmx`G~PUQcO)V*q~%A#C$wnBdxuxamK+N?$!lX$z$A%u z`9MG~WpP(U1JZ22&E1MnC3+$jt zUw2^*an)_B;2ApRuCaA-uR@&c zbPE%lL`^5tL}!or9(35f4!5_xA>SSdtnDJlboE&=BA_ErlYtktP8H9^sv1wg7t#B5 zn>oekw5m5HkCEhxg1mt+VmZ0_3{-C%oD>6I($0)#1K*WXLSg`K$uplb!hA~ZHb-E` z7MwUMI(e%2fQ-6w>_OW`^4XDfwJzk41IH|%iG|%u)EDsVjz(IC8Cs{qvFPPSCi|mf z!Z}+~gsiF>xm^M8w$0jH0BOv>C4K^XboZitfk!G)$T(o4Y@CllxLQ=?Hb?+r2TsMQ zuQARXTCCDC`Z}g5GZ-i9hbZPTPL%&EAHcYz{Up7`_)l_M(#>3i%EcyT9?y#=nJv5J zqP=A!@=esbvb-(p$XR6%liTpetiqW6ST%d(@kRN+rSUz34)*E4z$)k)F(^b_a<@u

U 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

<<{phuHKD`yQfP9&5 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 z zyDge;Sit6lPtks1cLfpDhuM4m&noro``&ZpZJdeAQlu+5fi4}QkDRjBm#f^kmm9VH z`P_ZAiM>?rNX7f^Ra|q)M(1U&uz>9t&n4y*cdq1uGnQB@x%J7<%?xgF{2HT$n-N{D zw{fGxUTT?KzaWUZjJwGHqSBW;$$P9D!uhbQQF4)U%S9<##W~b!8xRR+Hm3FM6r8Sk z-SbwUs_5=sE+{W)ca{rW3c~Gt{^OhpwhjFC>6a|G`Sj!=^HhFLJi!>ipBo*Z59Qqm zJFQ*Ba|AW2-tn6KuP9FPmUxHBhPdaKQ6zjW*JYEanH$=gF)&+_-0-q5TYS96+`|x8 zR}^(^6ul~$>v$@X=RdM95k+Kgx8)0Or;o*yguG;^X_e4FKFM%Wa4PDz?uq~$HmW(u z{~c7M>gFrQrm*>oy_03FJjXJvB$?;tazo_GwX{6yUoCHIQ1m{MZKz4;agj|c|J+q3 zMHTOIh$T<+Rh@ezh-{Gcz4&u_n#Ds*O7=39iN3}CXP}7iQ73gG;gc|lhAJ!yidHoW z*7~1PqzI;ZXUaVI&C6Da|M1qi{1M*Z{%&FP!&Gq%{=HTuxcWx-35C7f;QS@OTiomj zl}F}BbV6j7?EhK2q<_-hnlDHjldhR2N_NEUHUvrj7p2vujO9k^H8CP>&;pf*@PPjg z#bm)-?{w)i!J=i`#0SS*oSs4(udQWff2!tG{qEjY_1kJ(H(x!!Jj}U9m0tAQepgA) zyVAKtu_tSjwL<=1n#C-U$0bROdt|sclKz>rH>y(mTXHbWSN&6bX5}m83(*;Wr~HEO zw0E3zgW%M%!(u7_m}|DMk$1S+)bFlOs4wbWscWj9(w(Yhm7Q|7Xu66z?JD)YJc8}C z>Rwirb-MCbTB*wOrJE}kq?l)q5`Oql-GfQ zv|-wzy~{gh!TPpb0XD+vYXzV+h{}Ng@W03q`%!2KYKEZ+)`xl~zXkt>KFNHJfp32(K$01UHgbek)N=MyH%3&!Jq4{7NytCF9m|NSzQ;f*@l&S_^pVu0FM`yQPRpJ`50TS&weVo_F6w%Om6CwDfSOJ@ z1U91u)as@`*eTSnl?!k>T7TYif*-v#1wcH`2#++7am)n)J=Eo_@w;BO{70L$X-^xD z7P?vicuK46EAObIF*@f1Ptitn7r_Z*Tr?77l)jkz0p?Awrk2Ay={5`6C&JMy+| zAKSb4KzkBT zn#T63kYQJ{BlBRm7wm%MFNDo(d}KUH#vTo*rvTYsb`qPcf+HINExrQeNI~mH!J^&( zKqCLB^}qH5e46$=FoPc<6@rfO-f{ndl=Ie6_|W@2JbE{r!ixo2kxRM1n&zXo$TV{;=@*1lo1d7xTr(@4pyzE57H{={?UwIbltAvnSjrlB&PhNum zpYT&eCoxUXh!)brzW2TAo|Csi(B5EkU9i?MbE{ zmbl~d*iG*;&_+ES$%D|fcRQ4@8ckS35+YqiD(gk1DOTs4!Bol4C9T6rB)22V2^U2- z{I8Jx1gE!ZYVQxUjXLVNeI2U?8ohgv&fcbzT>_J-1@Bm=60|Mrd?l&{T(J4GW^^bm zb;J=+u}O?v0C6*nbOb{G=yudUf`8I%E~`NPt8(PXF|GpQAiYpFZ1CVs%t&^-LoVQU&c*lkodj}EjcIjwDdLBe`~YxfuCqV_w^b%aB}K06SR z0;bqzv;(0av!Ol=jx_u!-G-Fu%5$cox2ugw3$gzy4u`)YR7fBC_mVb=zHafTxw-Mk zTF*Mv`t|{jhRJLHIuXCFh$wQh-zv6obn!cP!Z%I^4UNhnXepanXDpK}2p;Z$jEoIxz#aA5CF7% z=4^NpcD*_Zfx$J2rz2P4=dgC5ZsE_6$}n>X8sr9SKC!uDD^5v@Z)m{ZB>R+YAcjyp za*mM{)Q}`O`4g=sVmh^rZtw@uE;H_IwE%+&i$>RgKm^XJUEnUlOXoqz4Pve70Bk<- zlxh#WjFcvFAk?JYtZL+WatiS)dhQt6z8Oub_Tr-QSW3*v=|g3+|Y}N*+)Ei&@c*p)tXCgmEwUF>7pJ01alv3lGES ztaPRtevVZ{OhfvyS`j-?Vivjm3i=yMU9Z7XS%anj;-|6>=L8VCSTB<%kg| zxz7mK5DnZNh+5<^uCkqp&g8b#r(-T~14_5!keu(??+Eib*AuT1PjL>1)5$8%R=8^&`bcp zEka-TYvezI&F!uzn;^OF6(*d2pcIL{!OzIvfY|UovJ4Lmn?$}bHA{&EWDA<=sAbjS(4UZx{1Y}7Sx)!!O9 z-?~h9r*B4^M0>9@8Sq*2P>$?0#B=6kKH*WD?iY!kX?$;co-~2{sTSF&1gF!;*<2VALp%l88fXgAXrLjp8p+z03ubsWq9FI0YMdx-d~ zSXFxkwOU?S!om=wPqSX*DkOx&*@P0&hp_h~fUwPP1BJ^!v7xRB>C7GOX})9M*7LZv zs58*!(RRSvr>kj?w)o2w9cxWnd4r&p#!Q+Myjgz|$AV_*ieUk;hnf>@9E4UKUCTg~ zEA=Iz7+?9Ltkt;x$-EP85#~wC!p@RHL@2)=N|k`W{y^iqzOvyt&43<6PkqbPE|s;j zt;n%gdlhiZK2bKSBhA*pTLC;|*)-;=Ej9hY&4N5OW<&SGgnC}vGDL!QO|1`VlKN2b zBlIKXi7Y7gihOUvF#f)@J8Ug!oEYn;qpTFhtxs%}4SpL^H2og9(S4%@+jr3F*803> zr?wW*-gR86X@B5&!Cemo+ox04gTC3^unvgI;t#EbMVS0s_rc#9=G2@)9@O41Hljz= zjx2v{pE5szgx@546ecHLlOX(fl!?OE>y9_ZuSptO&@?zQvAelt(coE2cdKHcTXP#Q zwT~^G-!AQeabtjUyUM5upaDl9HW-p@`wjU3eP-F;+6o^sL25`yf&OE0E}EzXWj@7{ zRHqX%@iN7-Fc@*2wA~L+z9Vw3YiRhmzG|?ysdjXB*VE>MtB+cIS|f)Un%1^;gJBYD zdqn>e&fbpwy;{mqP<3}WW=b##`uj)S*bcGh?xbB%wBexft<0h#^Sd`(`$ zKlnCfZCEZ*BCGN1Aa4eg8+--{gKz+n$pBPH-Tx>Z=!+$KDh4afx zZbmm5>6gabLN#oJBIAM6M@CWZAfMj1=n3GoPEYhp$k>?za~VpJW3c;ST0RxG4!)7z zjCUd);8zk9C^y(4A{z~C`;WxN?5({)mgD?OfRt|hK-M<%fy3OTFMWyOF|dTkD>`f(CcZ-{FX4Z^j#Z* zVHlKnSOABiLwl|uK z68eK#p?Lvcz-*GHAtIP4E)!YB6jC)PCew+PqKBA!q4}7*%!jS(abu~!nyq6#mlefG z!c$gN=5tadOO~*Ze42GFYy!2A9pihJc7Z)Gx*BNWVFo=xJ9(j92Jl_(XY(KEMDAV< z02aX2NDjh_xghQo1cjSQ^+Jwt-Lc-N`<(wEZ!jL5hpkOm9OqyS5_g?rEe<6Va>$w6 zh$lIS`1j;$4l48+6GHfaa1mt~@j$Q{vk~PfsDKF2X#S^`|1hWd${JT(0)KhYYy1x0mP|G=n^zWp zl=PJQCG;f4z;*hrr4?}V*0#6nl~V`qcN|w#IO~CbO;*<>K&D6#Qq52ef=5&a@e;^$#luKm!ttR>@=yK? zUp4g(@9Y{Mz#?;f|8RSWDcBLvAvI1k_5qLSZ>e0sQ*~QK8c5Mt^5remrooVXVei#> z=p00`YB@L(xn1$O`3yQ-zN`9wm_0IT(KKA0G$`X3VY>KAd_3{G2px(d?-u;=SPS_>wV=g@bzOpOMzVSUMr<8vKFQ z19Tc$s@~j;LS0nFSM9`r6dMco;%3R0W=QeJrL_3zM2GljXfatLn&^wCvIPkvKieks zWcA4aIbBryqIQL2v!S!&a_4WQ3uu|GQD_9iEoYcNAbZWpqy*SfV;>3%=Nl%1K!|T+ zWcxWNwx+wP1LLN;Sct=(QGChB#cSl#<8Bb)(pjOwwcXdW~(`!ONY? z0hb1L>Q{BR^bIL|f#9BgK|g3m*BG%HGS9J&SOX<@4j^@~M^*#SiD)(xn`)3BjER*y z&|SKx1=q0c8hZK>e1vL39D%q%P7irQo+5edn@=4l${x10&RYAb*Sjri^#R)&0C(7= z4{kp)2$hcl{~1UWtOSAjW;4v-L)|wB&!B-$3(_BEwU+|p5sR!}8-F3!nOT+XXprG? zffO6Ai%93;r>RMCsf4$R)gh-y_oWwolPEVtAJVc#1Ei@>_XCNh$opy?trS$e2Oc4Jh*{!6(MN8L3N^*RDGl2(fbQe(#x>) zj1EQ^zC3Ola{;k9>=PyM;49-`era&S?kiKU1A`}SuzHci!4Ao^XLH|LQ8t!1uVJ0fuv4faB!bdp4*tsDW z-+@aYi3wTw$*A>2Kl}|)FKHHGYjZs5Ut&)cmi&QaD{Q2^Bll;_rhcIuiX+n}QU8SW zFqYBFeO5CQ85>8&_7J#LeJhb~#{3UwP{;6ALp!>kuvqaCBP7^`3@nW3&3uI`CvGG~ z;*&@@C?Fw_^a#{STuPQS{Ut4-#8jn`mr>pqMpA;Q+tS}rvuL`w78;l?3DMGZ45LpU z;~;Z%_#6Bp8Q0r~*g)RYS%B1$zv^G0$dn`n7u`mY3+H1>D0i6(Y#KF&co!E%HKG>d zS5SX}TnYX(P}5;z1nqj&EK)JOxNtg|KtG#)hSI}mjmxH9W6m0rJ1=0K^RY2%Sk7TE zY$GG1cgYwhwaj)5L1DD%;m9@yUNHkz&XA1}rSlkrOg~H(<2aFvO=rAC9>S$DU4ZBC zxy*tlE}@Odth`KAG1nE`BAsA9PamR8X9dNrpjNUJAt>4^)_*=)`foOS=nS-ro7E$N zDY)}(@o+llsqP5^&DkfHBcU9pAQ%PVa2Q+B2o9LI1cT>PBHOWaP9~6yQ*ol2BJdkI zzLk2yOU}H4G50rTLOPqwW&eqrMY+qF9#T)s=J@!C>03BuLqQO|2;Kb>suYd0_QNDX zldc*r7KX~lBV>Y${1K#9KxM?C90D&wFM5=J4)FzZkk1Cr#optGHr~L`=H0H$CY11a zV{XANUO;*=={NUg>Hc4 z>5EBx{^r;Nlu7&rA%RpIkL-h^$8vA1`UbkIadhnf-%}s8P#}*~Z?&P&H>$=c@E-@j~Uj)aOK3`PrCTq@&Wc!4?W%%=U?)3V948R^-SM7s8SZDFjRhi_ZOfp~k#@^UKr-r? zX+?boCeJXZ>YF`D6 z9cCKQpqxRBDjeK2&?fo}VfB@;H$#Vd5-9-Ktu9Z@H^c(_A1DV|WxLXL2i0fktb2i( zZptj{#;WwE^A_QKwMnUMgk!3K7y^kT{~KIQ373}mxKlri{!h_e#zmFAaR6Uq?Oa8K(1g-#eYOfZg4(>e{Z|-QC@5|L6bazMT7F=5x-R=lPxI+q~3E zr7Qh7_m{eBc|yx+HBr%OZ36zNe5uO@wAG1Je{g8c0%$e3xUQ>w92Cft)Sz36P$gijpC-ErdI^B~Xz*X*&dPeIR@}EJ z2JR;jWp0PJOYbK?MEG(+%nj64*)P$zcyOx~52 zVTwom{g$Unynd~AgKC_hgOsX{3k}e4wOT~US^-xvSKk#lDQT<>1!3v>qM?wF>_%oI zbYC$t`4~J`$%{FHRI1K|l4u~1rGNZgCj$7u7SlafDF){gy>wW{k)8>J;`4|A(*uo~2WvJ>hvM2_6x z*aL?v(g2%ulX4jFl^?3S4e}~D)mrdQA*R+rC7GiDF?2s!0G7h?7#7MyhJ@zAMaWs7 zXGk5o$bB{nVdSD@(LH!-N36I3&T0ygB*G%Io8%X4()5<@h1cURWjgo@uuYzabe9$> zJdjY{W5pLlQW32@i>xi2rrM6Y&Um6;j|L%qxB%XzzH7$`0!nbiY$#r}&4og?#b|6JYIcnCQbEqFwj&Jn21h?Y%-De{g2+xJ?f^+PM zb`N0-v!l^NRLaaYIg7oS9^63jZ+biSQ*xCSsV_*k(Vh~GY!3C1=Pt*oCFN#?kg6?M zqU2E%GHO){@~*=cG?MdT#sZtk+R(qiH)NR4zi=cu$h`=$6VDfz>tE>y&1n*B*WnGV zLRS0Q*dWT&ma{tXWX)9!mJHTpsdFV?xigXh(udrvx=pf+?5Xkz@>A@rg6WD2%<+uj z%12Dx^!e%@^v>v4KsX&3@&?pWYkf9DPbn|=d?bflGXH!11oMA$y4Sxq2Q~B-956|Y zJ%w|OtJyz9kl`8nNnBy@QeBc{==tJCX@YJ=U9>D(`@C$Ye45rP|A``9Q-Xp z7pd&*(dg~!Gt9@3V~)vxqkQHrC zAi@WhbEr~u)_he}AU5rGAm)#@J5*+qj+nDHJzQ z$X8LBJzIHCoNx0Gt&-$gKGar8i_CjUU(4!EY`&jdYm82JQEb$|N;;!_p=*eqt`67w zgaE)YZo7{Fn#fLZchsWj4fAI3WSx^*%lTRDjW$Jn=$x*4Trjp3r)goomVpQ*{N1!( zDG+^a%n*f(zuTYGe3f>$A*CjVDBUyff;`;Jq@7X-jqj2)%Jqh<=sxQ1y5%9sfKv0) zCk_0{jdz~{>zKT`hj~L5$XagmKFwQZ{m8%3d0*e9{&2@6x|d+<939eIxT^KD;)`f` zOSSO0cy-giH8#oi#_^?-q-X449whr>-H`?Xq{jQWimV=O@@MoZS(iU_`}p>V?JP zs@Ap|Pl>E~f5}m)sqt3cSlQq92Wb=J->jz-Unysrmq%$-YmDmP2f#Rem`{I)AN0!I z3HE0@I7By5bd_f63S~zH4onoM9NN2s^mDstx zx_Ym~udSe@PCBzCI`^OqYw9)Yto*QjQ{sH3i#0haPQ{uo1+PPpvdq&H}C)K-(L zf?7eavpUVz0e+|v=(j)_wI=!yw4-hVatfZryR6&^FR1@hL?c54v91VeT{a=Q^r&B#_G! z+G2t2`~rPBIIcdKR)LLzaD;&d3H_A-WE1&_65)a3keY9>O_E%SAcLj+yj6%@)|p12 z1LO~q2A~#YOcW0rrCJ|MV4Hy`?-INKyzk~rbcLJdqN*)|Q!VS%u0qawLv0tu=zak` zM8D`U03|+v1c2QnMx`I9lNO7*LxW_&H3y&$`Owl-hba9!kA}A@ex_9*QL284Uy-9~ zzo^-0IZz#pV!gqw-WAwhN5;OkJl&?yMLs6>5dHK*~H7{)%ECQku?<2iIx2SYvBQz>l zjS65d?<&mUW4HxkKT)#NtjJWZYd)qpuIgg_Lz$$?)A=ips5VfAs!a7@7*{<|g9?Kh zQa=*PfWLr(npj{RaIWMQmRZ-jcb166urs9`rzpX1xdRBO4P}Lo3j~qI_TwC4;Mxe5|{785)aqxJ6)rc!cBn z$t3Jy(?!{4?675sJOrT zdJlFqcN2hMH)ibvr(quwwa{REP}ET94IUbt2j9ho-i62=e3M%Q`iSV-(Ov3C?`xVV z-A9Yfc3Cd%qB$*lLtP!Tti8F5D=t5#|J0Qu>HZ&fTJkky{_mg58 zvu2jeNi&Gh%MNpG-~lzGTkTUBa?pW9EJME{r-0*s-p ziK79B6B&6Bd{1o;c7gw-QoSP)U-FJy7+Ogx=e!h~Oxca$5_jW#lSZ<~@RB<&tuRCo zy=8;+MleZsTz6Y0k{fk_^(sZWR#R249HqH2J5c#tGdX9YLzJdxK2|SaFD6_A^sHN? z9n>>|pdZjmdad_F_&)W_Ed-rNxzDK(J+yae*d!)xLrq;Iu2xSjS#r!0g&U=qIS)AQ zIGZS>pXJ?**7|Pp(}q=*zZCQJhl)=r0o~14dqhOkE^c?}NdPdd={63CLCvtE)? zvUbZ}exsacUQ*eth&E}9g-R!5ZT3%9S3~5?WYsHu--I0XBkj3JPvDcr7_)D8xz&`D_AkLx1RZMyahqJrg zCLmU3Q0qaVc3!OAMHJd;H-MsN?N69v;)Qcku;G&O)&uHNsYgqSq)Gar=>l(~>}X>~ z#Xk8G`>~>B3WIHG_H-p|X-i$KQkb5^A5cR^&xo}^gMM~U9{8`8o^%u*z;(DeBNfcL z);MA8qLH?F!q*GZ_5X?H&(kw0;=Ili=p4zgj*+T|l3R165@+e!)_-|1GPY$)&9W$aHJnWOtQufzkDs6(XUU831maA60^fmZ{iaCm;!A4y(iHq^?HPUnF_0HqnM$PacNO;`I6*=- zLhKUWPi-Zhi<{#e(?z9a5yQxAd2Ap|wkZ?5R+5j@mE(3%VW7439!gbyvS*{Ks|OkO zp_gm?Sa+;vZHB`Gj;#aKmoNvQEggg%=U?WJ#|H|ARgT5;gr!A~@J`Y8>{9%u#66WF zhDn#kR}g9P$cVv&UGY4SChn`Yd+jEpfVtytknR1F~E4iMItECcRkk4=R`KDr!Pc%hzR(#l|bPq-JAs<>~k&Y@g~!L@#_a z5E*F1wP1_aHKGgL)3qN#BXrAt@Vtb!MM0yagY;XWO6dY-0<>Nhg4*G3va_nMj{c!k zG6UYF7{OB@V-){Za0sbHifWO^s)($wXtKI*>Qr>G`c?cCtP5~AqAMl<9|l?+XUQ&J z4{$jW=sLzBN{d@EfpvMZPF12>3}vfMi@!pDtCM&R z$y$A|A{jOV(~E-O@4&^Zb4WcXN_9rALPO#wpefLi@Neib*c52S@{u~Pr`UTm*VO}W z!qS?bs$0RyR-XC^SgLyrgo9?f1ZV*FBjb2-fgb$2B*DCJ;I&`Nv0r-WUa*Y2`qxZ_UfbY;BgAdC#En*jVx4k|AKqlq_#mEaNLLY6C(OYBZ*g&q;h z;!eUC(HwpV@g!`4Qsg;d_qvVlBN`oVSwOTkRVemoMws6!$8$@ysM5rRQeBnb+2b&$ zQnJ;`KUJUEt|FNlV;0r!0eUfsW&Z)K^s_>T(n;Gg&w>Z&sVP}dA@wM(8Tv{!hIhd0 zsA++@2uZ&3I*anjcGpQ*8JXPpSpMCx)vQ!h8-{5AP@K^#NUbtXcMe`%ZD@!6E1`k=qG`}$ap%z zYcJ|Vb-H?C?&P~hzC6boYx*KTXsOdMiV(Af?5kL3+72}+1C5Urqm+A%BZL-Jjv=My zk?N%$DVwA=>Q)yd0sh(>nXbTPZO<9IKtvN1H_{_gahGpzW>` z(U(+H!(G|2#;qo~Jg%Wt(^bC5juI9{jExVCR2;RI%NHvPEEPg0WOZ5%rwuG z<+SxTK9gN;S;^t@qUKa$p!|O0H*l^((y&`TN!itI5iC=-+sbQts-{{el@_V)n&0Jf zYO`rmh75=?@@KdJZw(`28^JZYT~p6Os5UF$2rT4ocyWlFEpVNHYUtDU6VjiZ!;RH4 zs-0%P%X_tb#4Ym1RypXRaBKNfwqCKVX@$T~nbDY9-K>0Xe^ByT)oBCrUDdO!-7W?o*d>UyIgy-?k1Z%K9r3az?n zYryel{?tL>Z)0pg1oTEf$ZIP6Msvy41^L1@*;mn1O7|K?w0n79&Wj#iQG{Qk2Uo5K zae8odFZpQtubNsxJ3XZKRP|?iI4`C&ly>3o%uk{{1qm5_=!wGn$uK=m+&pbHJzQEA zwx0e=9yb}GM<|mf4x`=FwJxo65ZGj|BtKQ|HhPfHs%|>OiTl;R@K*9p?RapS!;;RB z-6KEoNI@Odo4=#_7&WH;d+8u5P?((WNhOFDr@yCCB!4B>Q>oJCX^W|ua{sUmRJ!8M zWGz*!T0hZ`QUY^ac2jd8n|(O3qjsm^2Jv^@TDFte#aoPL5xe=Fz;EJ6eTQr*@jx(F z5J+|tF0XDU$BA~AJR@Vp*YkTiEa@KU*GZ`?F}a9@@(+5P*p!66Om=O8a12u~z9SK!|OVrOTYLTe7e9+wflUmDN6Yu%e>Gh*vAe zb2AKL>FL1*k-~9+&XzFQ48IiSVycuMvfckP%O)BUCn-Lh{l6Rhh^fbn@(z$St%e&k3D?>eDBnQuLqc57B?nFVm{Af!Nfr z^_URTOx}uJ!yZhe@EkmS++_SRe$cWBiYCYF^bnu;NS8pZ#4*$hI!-K6y@mfIILQ_` zg%I$o;6@^^@-uv!NSF;GK15LN83ZRh)9xeZi3!s;pdN&W!xkh7&#+eX8R0c~IaW)A zOvJEHMB2Du+)St}Az&@LR5uW8VrNi~!NbfOWE=RMX;V>96q6`PhV=9&-Vf*oy`r)S z_NPl7mb8{0mJ7iTs55EJ$TZ3{U5s>4snhx)KdF9UGE_>Qo@_;bkc|`N4pF*rTo|50 zx|sI?EA&pUtqDV3L+p^#@Iwui~rV9gUJ_gTlGDmA+6jCo0|neP&SY*AT1W&%RHa%fw(b(n6rqHei4>{_Mqz~%h37M?TOV`I+Zyt0`E^AG$#Yy&6IW= z5N_H>eg>4LF7Q!cvoT)T3j8+c#I<07{w&YUAxe*|I0TKrcmwi^48Y6{h`a%zj2QPrciA$|1%6 zvYvs{fF#Rhr7O^8ek}R`yf^)>+YDx#MphJoJB+?X@1XI9X*tzUlRjfsJKRH8oYV{} zwdFAd@N-T1lqZOS%bgsEj$~sdPDd{?F5{+QbE$8p0Ck_1Zkl20tfoE0XLV;I0i9F7 zXh>140AlT)A`F;o`>Spm@Z0jM{3TdtzF%Yjub2+zbcbdd=go?T_8X*0)8SD4)R^J$ zM(ux7HX+fPb(04mkGcGbA?PCJ^SC&NZE7>FQ*EB}f@@U$Y6A(CdS+`MC`&!JdAY(z z{i!KW)D6gP99nxE*ls^xj)1N9>50Sq$H5137O7`Dz@mLt+ZVXk)9HW>=sCISy!au8fwyVMO>Vmcp@-^xWt%y(q z3~cFFn+2$v+RJ+Z_ZyuHw}1uq&g^LLf^G0jGn8pjCN6}|m<~kKaIUf2l&SDjeWd>m z#HJNa?1N6{9I<$;C$rUXKoz#|Pj;RPn=iyc)xUEufSKxHom1pKYEgTKu$TID+wYpI zKxpgivL;|@^M=CdpmXEztZQIrL+s4Skh4vgm1>SE!{S63DC-wY* zI%!JAO-C=YNycHCQ>AM-PmQkZHxZ;stjK`^G(9TUD%`nK)xAZ1xH&ca+N)e$?YVNA z3*jXe4dr_94`i#^%Yw9-R(8Jdbz&}CE?yLMn;k0^geEYz<+J=yX1+4hL(f#G#baq^ zJlJlyz_nLu*e6_G)oT0`H@x~B_?f*^^Hctk-B{-ns8me5myz8;5Y$67u1tV5aP3`xx7`cSr%=_=@n z%jheD8z4k)7H*OQbgRe#8quU!RGUKAOLEG4(s|N^!nJgwEF#;34wT2t%%?{wvJ=Co z4@xlVjzg5*5L!X4RR8eXLRrBYkJ}UkJsSItszK!X&-4JXH#>-WB0h&tpbko4(4Sf^ zogfdO?9xj@ABvReY6np=d0g2=s#?*#kfDkcr?b0L>B>bjW2k8=F43FvRI8$HQvCpR zD3AOO8vPEE@1R{CAIRrOSC;|g12jiJpZqBgVs?=i<*Tvtd*@nkiyFL4Z627*yn$Ra2vR7e)UBmK^h z#YjKTKglX|n9BrGf=$xL6W`SFOgV8*ZNorflll&zBNhMwvIb%fpcAw>TMXGZ=^ZL14k1hYt`S$!0?#4DTg=5J zlPx!36yszYn)#!|{{wHoyx%1YeegI-cJJG58V0qb3+Xj^vdMz|SK3f~)v7 z}IU_pMji85@VrxRhoU0h;`o%nLy|D9;Z z?*sddA4FGTAMn-ceb^hkU3w0Chg<7!VLxzf^%cAiPL=M!|KD-b;odlx6@bU$#?&^v z5N}NQ8&~2Bq88$f_{Pve{BQiI-zEGd{=(Clm_&?mNhKsij@EH9WTw(1u|WC*ItBBh z*QzrduCzc}gL%_F^(riydRwi)@~Ca4g_wvU3j8rWm6CZITS1L^yt zXt4HWi4`r#XA!x@RrwwP%?Xl0h|VP8dm}523#vkpbB1#zUdT87 z@BD$Nm)wP5^O^sr;H{c-dcw>D%a7SI0UC&N0K!yfA9!!I{Va`$i>%IN3VH9Y~bweBI7 z!0TEB&_#H6^H{|%_*CO{u{->@VL2}j>0>7=vyot1(d;aw(mE+G5ot4j&KQMUFzuYN z869Ml#ScT%4I?6_qYb*vA(`l1ZHk{47QtQhxQZ=e%UtC6K>Ggwm8Onp!WXWaa|tSf zRc#`L1O097BU%nGXx>`TRc zpT(?HwPk`Y^A4Eryqu9hEv&zOXl-9yt6Npq8@!>5Woh0z9 zIHtKMOfEjHK}CYxe>9%r)^v(HEjcse4^AQ*85h9ymMbE>*|~~)!97`jRkhDQ%wC|! zgbZdTxYzkCeFf&2x7vIBJ$R~?t8WDtIzCe&f2}z$%n}7@a8YQTLgOnQTQQfrEg4X} zg0o6{Re>8>7sRn9ST%H3bi6AoXLPi_9=EXM&hs4_$Zu zOeZ5kW|iiMBd5?qBNI;pOEga6_wu#eamfndXAYHC)`AVqz<7eObNF~ zroeV`b+RwuP;Q7Em)Eif6}^O;SxC`Z`-2TqPO1oG-YO3kr!s3*)wx-WP~A6uGUKh@ zm3)GB%*l)!O|Jz*BLZnL)GgSH_J$t$e4!q~4=2p0+L5nggQ+xZ4AX}_=;%c8Sg~p+ zaF89N9xfljoKdTVJVvj+Ub~f<0mPNRW%>c@iwDzZK(|~sx)JP5?@Jd#PRSeS@zA2U zKGbK}JHnSb3hxQ_qgs(_-)>YnI)1`NY6ALgY%2K-yGgHNENF z{sMgy{zd-{``1>}1k5jAN+-iBicZmg!7p+iQ@4=dv}@FAgi6*^1adz1Emeq)k8pJ; z)mX3(H4uI1+naoXRZjSa+=IOzTTC|NW;&LBf~`Xr(o3)ChT&E(g(*f#C}pl z-XhN#h9L2zUC*eNlX<$2(q4|!Y+XH{_^G{7eVn+V%_?&y_G*q6781)e(b;;!&TXC5 zN)X(n=|V!rcEt7|s@Y!QQlf~F1#Kbn>1#g6h*COw!d5~^Ef{MiSgH$IOlDe!!AHoE z<{7HV!~+u{X(hH9ckzEX6t!Q~<%GhJT)K@Y(l-`-A(C|0vwewR-MCrtgr~N0x+~$L znK$h)F`7FU9!U&me+OBJVeBNIjf69kK4Bfa?~mS>&D9~pp|FYp_BUDA2{y3XRLT3pxa z!@J-&HAql7eut~~X~Lhf*%KDy-48XQmzRFwRdR@eYG*S|~nWA2f9zzFhk} z$Oqq`ndc*O=+$WxTJV$1x3Men+w>CrB@sQRFEpDN*t%PB41d@n6^Gz^o4t4o@r8{q ztNP=bh8-nhTw$m3_u%!m*sR`owdKdmP`tvtHpvICFwKm4hSwV|PDOA~pAq;G=d{Ot zQgOS+!Rg?0*&Smy;j0*LoWZ|!&IDiL$J*a2a`Cxy8b$x$P+JJk3!mL`rLql=Z3bum z#3wfm&CkYt8kT1+#V6TAQm^4&*6oQ`@JSZ8n07qOB%2z9&oXQb{2O=d8$RxMjW%S0 z3Kw(Au{&`!vk@<{^(|chzPFZ_eNnKM?G+i~0hU3P>v%S^uzFxsU(?ANWr@_}U3)Ko zyHU%Vo%!7GfPXLby&+PdOx&usi$=zH=pKnrhgItWrL6%Ltx~R@G+(n^=_qeOw@iZ@_B*PfO$MZW4?e*s} zFY12_eNrFjD@4tS>vel117m!3Bc<(OfL0(63s|Gss(3l+5cgK~kNZB(4Or&1mQ8~? zv7hF>HM_wkvq$Ydg{R3{+XYegxUd-P6X zH1&c`E7_H}T>DbiJ7%(WmYf%+)ht(B3;37&sw$uKn2Q0ryFX=7aIe!1W(%yrj+vVH z*@6$|n6)(Up>3`ZV$B{4(7k$?41`x^U^`)cx9Z zvS*3&v`&h?F##G*85!2VJyqcW*SUE0MenX`6PP@{FLN6@@BAm@j_@&!kr8zP$2mml zo$?(9saPeNp?A#Vs=KPMl6uwnSD!FEHmauXs^gCGPT-lMRe*K%^9UfVuL10 zH7drB+pQiEHiz>CCImcZTfphwqnS^TcKjqJ6~684P0w>ABccqnbRTeC|3#J|SLsEv z`=Y+O$8xZ4uC7Y)SEaA^p<=`AHQE|wZvJ4+W7Qv-wHlskb7~X!Of62NIS~*LGm-rW zd<*MjVem}AC&%ai?d{7{!TZN2&_|Ga&NCe=e1G(ZUZ*qzE&4x{ALaf!ST$RCNc&lJ zw60JKs>3Q?YksQN%~og_U}D}KO>baDW)x=w{ZrN4NN`u8h+PiNjB#S4p*vx7m`gAc z@SUkZCV7X`w~;U73+YVsx-*a3iycFk>*j(u5UXa;-b$Q`bkc8j@5j z)`UPiX8Upb;U0Ml9intWW-s;zydgD}6(ao-XR+N7eoRkh5%O=Cg^5GG0>07L(N6C; zx(4$a&!e7U$DA?B5d=jew7XD~`kFQ#ZI-DuC(-%BL7IGYP3;=)5xS=$g;S%~im!3K z(XV;g>|AVg#(p*ki%IRroW$x9M>6%8E&4Vy2-_B>a3mV<1-zgO@ZsJu)O);Syp+=7 zTb=FHXkr9%QuBdqaP*=arDR!eO>Z(ou#9UXgK85wZ*oleWA-5VXEDr{5btvTW4;pi zGK5SkaUo?76Gj|PxIo_~wns0g^~92}R63Ms4meNUC2Zcol#XZ`ub_O11tG;ok*m!9h=g6%ILfKc$#+s`v$JCc&th-~r{cz?WeK&UwQ%ldy7|isei&OaY zR(f=Tj;^3CMf2&uC{vh|!^Dga*h~TBFYocxBy!Jqg=2@MoHvnHG7)~tt#o|nS}siY zht!ijrq$LTVav1wYXr;>O?&xJX0FDwXd@HPZONTVzu=~&Z>L+?Qz^sfB(@+Siu%gj ziT0+}Gi6g=Q9Sx)Km+AU7kU3h-k`3Imy^v@p7VY(gS_DAEln~Nss^)rj7ub2*<8cB z`aF8C;hImxM~~S+7MpKr(^AIg+r;g zw%*wrsil@jX)~x&^FPT)C})!~eh_)zI6caR+^By(r8i0G=*joV8QL+EmXbZWZR4|u zf7lG?PlSYi3eIQFwhvJxGWBieL@$^gtrlK0y}Tv0Dv?fa?o;xO`qp@?U?sJ>fz3{( zs_Y@No>MN?`^ggWu7!@0y-m?EhzZ-5;Wb)41e^hR$YO%Xl4=K&DrPsg(zpQZW^K7$a0gso~ z&wP;oF1gBdU!aWxj6X$wk@?dR#t_HF5Aym3wg_JUu%|DNo^jr4@v{A1w4;}`xZT4o~<@-t3NjDo9VvL zD>>iPN7NFhGWtq}M$RzIlr9W;t*?^D`qMgr;`7Ac+6vWCw`xr?uwl$UoF}vd_^+X| z29#g4+iMxoTHF3QI}f*h;4P_|VV%hTr*x2|y#9K@A#ZtNf>drdSP^{ADT$Gx;lK=QwA4xNYf(#ghz6L~%W~_U znO@00SMDT#@#!`eS6C)(1Eo zI$omy7x~}hRzpo*AbS>G>h_g+f}C^`&|k1WfDx7i#c|muGpURco-^%LZmL^l>Y|!d zNgHRVmX{P6TGSH@-1QID>#{EE1Ay>ZxDEwQCU?^w1FPdwwPV2Fk!czg)Dh~=orb6R zKj22dU%j+!Dst9+5aZ})IT>jSwq1R~TmrPpR+tt6w}htnta}mfL{|BrUE%mZ7 zXVEY2ZcGnssnaq#6faSinivF=O*5WGT7+(n8rjCWE{1yKY{fnO9^~Wf{rX|3bAGc< zf~IEWYLB9b!xnT$H%wouA+&fW86m+OD`Jx5wiq448_E>+IIaqVoC+7?@3IaU7@QXyz{4O4-*qJe`!64 zi8FU=^n~~HBJLI8AG?>!B?2NIvImI3kUOkD5$Jz_X(ocb4D>r9-aV8~CMujRQXPa< zr7;d-CQJE-Qu@0fRlk8gQtPYlK{r+WrK_gPi@#})(7t(Bw7%5$%!Qg}>cq@y?l;vr zy%#5-M6m#SjS7!g&8AXaL$)$|$m9Mi7;m!8i=Z1wk$bqq$4qp3O68GbRih0m&3}@g z`lFiW`aAlunxL8kI*2=8zEXRGlNUE>he8ZYIi2forY}-YyySEog}O&mYpKCb->Ko`a^-UUN5eLWSwG7#s$Q;J zp(knzb-i@Y%j2~o-R$B3%~kEiyy2Q8?aYi9oMW0`>fhWXO=yykUC1q)_KF?N4TzY^ zw6VgFGG+vG*xwPXVqCnc=`J+n9z%(#^UnRrYm~RrU(Z>pB%^c>%)0t+x_Hy(>Q~xj z#;0XBv;&PkMJF|=0n6ROz0{x1uyE!2QK?nz6%Y~MHq&yhOs3go zepOVhnPeW3o59UB#b)?(LyVQFeOQZuNIb>%(>F~kWh}am@LkL(ttI#)y;uYKkEO?R z)n36=3!CLWh3dk@IQx^ew6`KnccZydY%*n__BAgqagYblOlR%a9!o(D(I&gLlw!?LoiS)MpLsHG<-C z!S4R#Y38+a4CzO&kl)vJw0;ns)P8C?&D){PYF<&bRI{y#E^X7eHO?-yam@`=au}|^ zy??rp)mg8njMUzqW7;T)gR`J%aP-OK*t9cj5&kyG`c`Eccy(w9x!t49?+ZroZ!=bUQTSo<-3 zzI_Q#nNn@D^M5D)X@vxBF+59=D0V7so+ADlBr)}o9`emHoRly2^w6u7bH_c=4OFik zJzp~qJSLBC8&>gFq-^-DLm4!pXZdb*?xdupKi46 zsy~>LW8Eb5O!#hDEONZXY?O?gYBvd`D}$`Y1bM12Zs@D*>RF)Mqq^woq%8+_jlRTv zht|qnT7K0eh|-#$)aLSlri*pFs;0&xJfd_-!(o0$;d1-Q`rQsU=c3?Yy4reCG$JLz zazUJ(@ZNk<(h*Z<+AaMsb)Iptyf|p3fmB@dU93-2Rd_PGK0q(mnVRk3;W53q64)&J z-TZ_%SLEGv#W4diyYW~(RwZdTB#@Po_G7~OLXGXJs5S?+J`wZMOD%6Cs+2(UXQ@8n ziRp`MMNEzHwfyqbWrmx|zk>eJI}+c%`*f}9y`C$yC1AKKpcxJwA2XFbhSbQ8H9Zja z5&qYBQFz5Map1UUX;qy4h*({kZ#ySRDXg$Ql)B{1w)`jko*r-MA-kL6V*X2hD&dA{ zwBle)g>kg<@YL0Yp{jF1C-gnkuY50SpMg%EM>M;kI@fs|1s@qx$Ht&RGDG7tiB9-W z!*%K8x|jAd(rZR?zTCF+`}dKUXx@ zZMHp7j;g$3eWaXU^1+cP^epUY>8IM1)8Fi=j!W-qidJ8l@!FUTC=+%W>cC+!8G01l zId!?N1u74^s9ggO@O`b>2%q(Q!!1BIxL##Rbm^F_OaZn`HlSgMdXF&LJ`fmIC$J3! z#FcjINZ?q>28$0kvEZ0_2588>Wa5Kw)6N+=sCdQ^hf{DOVV-_B91|0%y9RHaI!F5* z@eDet9fd4#yk#Ql?fI3PfSz`J$qvJ~F&F7y_;l$ChfT%^ZrP&Y^>qWS3GmIzsg^9H zZ%K(+h&ZAFCM%-KHW;@e>(k7JtH>*dE%+VvOW^CrqVkwQx;S*(R8Y%DKL>5sn6Yr* zN8D1(==sC3qrGwc!EC@Q$GoFg#}5b1(e%YH`d;FE@MoUi z*m$C^TW=Yw~nca?k{e@WMuJR5D(Mv&{K4$}xoZIFtaLzesQXZMpao}ZZej(`B7s`C{V-?S|rW#^GU+2xyf7a(XDm;O@zh_?4NpvHpf6%Ves^dOu zUTaQAoznPe#)K(3DOVKuo!!7T_{y25%n8qn%uwb}H#a(*j&`c0iYY+sX?|$y%C9ng zvpQ9Gnub{-%FY|7nk$R{H1JKWdBOU{#><(rb@z0_@1to$L;q@j z;}QFaGM?e7ZG2IuVU#s2_q0COl9};U*J`etIZk`kq@F%q>ujXr3N_URI%*oXMlYZ8 znf@lojy zLr24%q9FZA`=#7EeIMJ=jAmV$b*rOV)oAHRdZW2+#^ZWx#+z~?|Kp^_;ZruVhx9uG zbJ*d!OrLj5p60cuo~F1)w~3m*JXGq!isuS(!D~ihe=b)AU z6oG9K)#Gzlwf3#CW*lq2Ue`7CdQ%6FNxI*dTR$lFcta1tsz}JTR+Jn1$eJn{7%<6v zOM1h*+EgmvHNk2)s@&qTQa?_;-(hk|!K=c#olOy2ezL z)0$X&CfC$5iZ?uCW7A81P3qCcE%p18E;azdiLnRl0iupb#ClsYHuOIWDcu~9W*#mt z@NP18DhE$EY#5-r?{ZHk1&)q-uQ>s27lIx7nvr!s+P!LhtAMt1bqS?+TRC1?QD93N zpU4$Ad(-I9dl)=1dZNY~ZV;BYz2ksDK?CUBWdXHNwz2kYHimwl-HGQ03QhR+LuS+H4kk7QSpMlXT7XZ;X@N z&Zungl`jR{m}~G`&&esXeuQtPudqBtTvMK#FC+Cy6HSMZ9kDZw z8_-^nk%ksDKlGy>MwbL=bp_}rpFUb2EYkz#K4SaFU1m4q(PKOr5q_|KU-LLDyf&rr z8kSvgrlB1ZmQ1(TV_d;{TLv~arER3q1lKp@xUGC%f)d|*c@VjL(6g@(&~$vaPmma|dZCOb&X+O=YNO8(^(^9e|(PmskrcK*#=p~ z(Q&n17_Mz5X`41|Y}>XqNyfI$*!D~^lZo9nwQbvYYuk3e`Tn1^_By}zxu54g*A*D$ z(9sANgcd6xf`9%W<(Y!Fjwv33N6WrS{t9j`f{L#RE}=hK-bl0I0`s7BK6s)DE_v7> zF~&&NR=+cNN~q;UdQVAI(SXiZ{5|)R)=#`UGgT8H7N)S&0phfHrOH!0DhjKdFWM0r zq8KA0_&=1r5_x&^q!)yD-Ns5b32lr1pZ-eGdJ9-_2)@|tuBdMwG`^I7Zg4jok?X5X z`mJ)`vQN70vSURB+Wj&}ZlC6e^moP=^+~BUB~o=x8W>-sJT2K9m8du*Ne`VN-zq-h ze@-?a&hiGNTG0u&84l-H-XgoWOn3(EXMUgwgl#viQ9Gi7k*|7Jzt7O5s;&;!S13=E zwdiV<$%P-ZFva%Vd<|CNmocoS$vcwYsyOnA@iUb?87gX$f+O7<@8Sl?rM?_iXe!g`nywo?wWsQ<4HLA}t4`_uXllyBbmP^l z3(eZusy{g&HH%ca8RhEbN^|m{Dok-D?wm44F)8xAB0=sKvP~W@%kVeJLZnUJ4N?~g z&&^5vTim|rjN`s_J1XCtXNrQ}F- zkt2Pl8V8}^-iYRLO*6WOz=*n)T^^7#Rm(fJ!Xioy?X~cpf~Rdik#ln*Y-&_%I@dZI z6UtT3N!r=8Gp=-X0OOj|f68RwB@)+r z73wo?k1efeTEjNiF=@k|hsW8c6o=#@Ot}^N< z>mn@rAJlKb4Z2EtiQhEMO2$sl9@SeQcBzxnhjrRnDhF{8$XQ)Guvw5}onbilMtb{Z z{HVJ5_Eh}6%3j+e!hup3tCiSaAaBhhsj^>KK9bq#*=8$+lB6?*QfaXxhFvsCM5jK4 z-W`nBwlR+TEmwa7#&{l5<*+K3W+?RRUCujX=efR!2b~8AT8Of5?DD^yyH{f?z=N z2uSmbRhP2zJntwsu(37fA0@D4BHyQsW2@FD}Hg=_c36LJquiXgz3g&9!S-btx zR6E!P&-Y3%4ri%SF6VMx=1cGM*uz?AF`69Z7C&o=t7rY8~g*I2EwJvtV9X+)=XE_+2VQ$5Iu^h;OybFO&) zRt$5MOZ(&tcttJ=QZauqVsd*Ra0Yz3Z3}B5NN!7GL2Dyh@3J;jTyD{^{+56(1?+O|!J%fakmz1fG;S9#|wGTM|BBUB2we4#(FL?5#$+}_Q=GX#lCGTEDv-&%KQgEfp%n$dASNihdUNhu-`F%^z$%^<- zU1XBmf?#+_+XcZM@LY#^^KipOhk5fvjif~+I91_gi4mMA-eDdiIGCSibUc%F(qI$p zOe@tl3w9(P)wv6{$Ij8*5NwHvQu76CgA-Lt1jByeirs<%uX*xX!O+r&(yxM*F2fR& zU>)prTZ+WBd875H_)i15wL*Ne#@q5)ytVw4d7W5W+-%~C8}r^6i^RcMEW;x4n6wGH z7ouy4BJD=es+j*YI1w{qs(PU)CD>EBTQt$nPthbi@8v8XC$ug7EL|yVaycd`5H5#l zY@6lm=2B~_+^b=3%MIDZ>XR0Mj8{%FC(3+_Cz)nQujjQJ9!Vux{`$?*pwxXjndD|- zs%Y4W6XLiA((!D;9{Jc)7^-iCD|VIb6?ET^>s&3%g)`wouja zruVI6T(c(Hi4=MOc|%_-&q<}|0%d0s{%HQn z@?%iyL(&7`n^bJ6PtaqfuSD%PL4IBQ#%rETC@x+$Q|c{Rzi5p3lxQaOfc2*Ke3P=3 ztX*24-!e->sUBzEr#@A--XvE078M$6REFHAhH&NQOtF5FvOLvYdrz?;VVh=?Vq%Q1 znj=c!_4%Aj3}chVicFXTOv7haQOxst`p7D`@=3l}XF>qLBLp|#i0+T`3Cpub*s z-*QOrRMln1>T1jKO+ng@!dJ$Lnmf4){S(dNOkdqzbydnmtxcs(z-jQxUD40gv5Fhv z>B>Ly`#~zjPT5`GV{)AIp655&e95C_OC(#x&le?#OGIxVSF9v+LgR4DTN6-6wXlpE zt0K)VhDW7Ojdu)F3RQ+3dY@b$eY-9t`pN<#CI#Fy zp~y3r?=c`KAKW#%3fi+JIht_B8;6r?Ch!Zgc5o1Uv{5)P8ak@3un!DNs+!Sr3@$7^ z*d2{HUWn-2h?2(N$S~Qpn4>9+Z7*>_@g{2wekkg03z--cHri|?oeCUh?4kh6 zKk0jEk?toPSz!DUxmv({<`k`ju`WR51HG{GjkW!w;V!Sl20l>)>)@@3QrCSQr6dp`PR6scMA4;?W=AEZfE7X&S&_}QbY%YASzsFe?mm% z>}`XSDlr*{Lv3-rCmnee3cNSBN_T&eE(N*v76c6+&o^JB7e&1^`7mO`A`BR&e;`=b1^D@1(dy#{{ObD3NLI?x?Q{#tA49HiJQ8#@+I6HDFfX6nO&J8cVTiX4-*lOCLr*y=>T zoBYILW?14m<}u8|sMkg)FfA-vzY#bah|qpw*?feK+(hvZs_Hp#SByf$MLXxn`glz6 zitazOL6E2mO?RuU?zlnku3X+;$XHVHuI((Nt6-z8in$~Q+j@`Lm$9^^8SqWMWWEn< zizl0^S=mv~4f|L(!y@!^+4w-Hww^QIN38DVto6_-Z*tMDGQ~_@gfmT+!k-9k><$2C zgJL?n0C?@Jj@iJjiidVBYj(-nwplE60oB^gI+PRI>d$sfe`VRs<|PlCv;zPwcI>j(9_`9z9>KL&KE(U&+$DX^H-Kk!kvQ~*=N%6@ z!!-xmYdAM5ENxf0lS`Uyjoi2bpVkjtZ1&3*DYq+qrDYEHVlv9q%bOg((CE!ei|WN~` zH}YRrB(??fzm-h2?h%a2zuH9X@I7!e_Tj(M!8B{nd7F*_7(dZG+MSX^?iLz1)yd{AAJ%<@4f zlf-hzlvSdiOJ~dGiLfpe629aFdzI;iZ76{)fm4YiI^jmu+M z29?~b0&||?bDF2|wE~$m#$b?NkG-zMfb? zO~2HWQqLNDRh5aG44KNlSflQ-;&Ei9ma147I!66Y9u?4|g2?iHLKGLIP>)UWI0SC3Q7Hw$EYNr@C)P=TPGkmLBWJ@z7l#OaVpyw6dYpK$0%iUoat$mScGOgDx zNTnGoH7SW;{U0?VHe2UFiA2uRhAMR-C)8UMya1dkKo0Sls_2sCd-TdDNaL0UNf8oX zm+j)i;>C@aRhY(WbxA9BHAhs<8J-MoDSbRx3He^QwZ9kEl&k1{2|t-x(c_0qNOkV2 zL+wqt+rh)+#u)8-?Bj?27oNE%Y&qUA3{y2@B_` zUb3b%daVS557ho0-Uj(yd2MJaEUt9@Km(jtsOnpdIF<|V`Hq^K8Q-0VMx{>bq+m`a zTx{>gg~zDcw&PbvEVXVUMg?11ETrpxugrB6m3M$~F)hcfM0bVZycndBFuyOXQpK`v zf_|;2hpn%@I7!%T7MAp!#%{|E>t2F8oH?r#iNBWeq{(Y>kA{h?-4R`7j=aaLo)t$@JJph|Je_cT@yC7jiR{4aIGQK zpa_S5f|eV6$Q(we`llGDF;;u`>7Fn@xZTz4X5}sZr_!)DIn7qobC-eo2Lb%iT6X^} zg0ix*FM(K48s1|k%`J59`arsqJKjNV9?5vuZlv&1&bM8sRwt~pxzM6x_^l=Mr4eBk zEMr#iSrdvm#lOf<4$Si2ri*8Jx&6_&vWu7aDSvU)PPy`{+%q8LKriWTZC3v%vZQi( zZxhA0bXxar%B6zuU2~|soL3zPnqS5(`&Qbml*6`vbaTQgYZjx};l&m)XGO%Ahnd%d zFB{hbeg2jDl`NX~A#FFi+THO74ySlYk&?}=cH+swJQT>M{{n4p?bN<-T1&uyT zlAB$h=uZ+Uo_5S zNBCFhXRxn(AJb0fDBL~N)3|v{@X86iMNVsFe|eu8p7oDsf@@CoVwt-uHgsPA7M65$ zg#q*eV}}X2mZP)3VZ~=C+cH`GDLkv4JvD*S@|Mktsj)<}UqrZ?kes^ULk1S-lz)+q z%T4k=rIB#=xQD9@yzC_!MLX}7(@ohBpWe{Zm&;yNBkI}6eo%qzUdVANDeL5NDhhJj z?{W+|Ire1EnT#x37k6q(a_cv4K|*{>IoA;5V;|bsD{Ic}lw()#)^3PT{Uljk-a-820{od@#-x_|(SjRsXtkF~WPyJ)GT>fA0OKOu~ zfqRv5iy&mlarsR_o^!BltN>pBz2~23QT45Eyl6uC;m(`FPsJNMvV`~ZN9-Gg=d;(g zxd`{CZ?uYp8}-IlIv|BAJ#B zx6TvaOvblx#l!Jz^D{9eT42f+Cxnv>TJfmhV%>VtKL5pPy z^RP5jxU%k0_fB;{)#ffAm9}g}N1O6vQD6HcB{Xlajjec?wa)rRflu4h3X=awI%hc} z=f%A+$IC}Wk23bjc;VypZ>8^oe(RP<%l#f}k|pcBcc~DP$?hygix}d%Ty{vb)A@|# zgJ?!wbvI9Yuqv|$bYDbO+U6f?sq&}RN-sZ0M$||ELBw3;OlFI@KMRa?|1t`B^ z$A(R?OO5_3jfmw<-Hr^y0ncrVK%Ivu41X{l(4UHRSO%OfNWvXM{ATPS%tmPm_+$5W+3Kbdi5`n3T(H09qJcufM1Fs;J4BrVDA$y;FIuG#2>IU!ZmVa-AfXf zA}P%wf2KapMpE_khQw%kDC1MuQN}Hx&nE;>vO(L}jjOSyb&}>VT*a^)yc6%zErcfF zA6O}{Lxk1(MnndYDGx;+Bjxh$qw~lfbT;NJWgPA)9AlOxj-zpb-Y^F*jy=w20`o72w)H5;kF;j(wZ?0Pr$DzIjxVN z9Lf#dZTKWAR<;{qrH$j!QOjwA^o8hM^gNstTT1^4ZO46NY^$pxv@=B|M@a=heb#5n zBvw|!Md~GXZWx-rg44X*!C>Tyw$88LMZdc?1k}Lr8Omz><0$2nH+M3Qv^GGZna;Xq z=y@hZmJdfUFY;z0CpyUXyHP8EK3oW53Vb3g)5+&@;texaaPHaLq zHJbA}ESc`X-L!l!a{*7XrLqphnYl*TFqPvmI0V|kiRnDp1mzUBTm?_!V6=}RBOIml z2CRrP!X1MD;9R6Rz7jb9us6_k+%U)=>=G`j_9*T(x3?sjaDsa;i$~hX^Gm=|MtG{Q zg|v;lpUe9h`}mB_=Gs|8{OZ$n=Y=T){~ENy`JKxf9XB;CSxw&rr?rjX0l{i%9kf)S z=Z3&13YgTlh(iJxmWGlFDj@xsNbbQYGWkG7JwoQvp#%j=H(53|zLFMdE;RQ^UP~@R>Lq&4MwqK4 zoXUhh72n2AL~a+$AW3MeIH1-UD-xY6UWw<3$eDkLYLQFA1o9T)$rt)=1PR``6V~sy*7DH+ZP{mW7~~%A16xmF)yyMLm+uui1@pbWw||agQViGj|flN#@00B4>&bp=+o;qOHr3j0vJ~8|9T- zjCWR@t5zF+_s_0vGAwG(s}Ix1n7N=ax=QuR#-1RI|OnXZ4|!G0+{lspvKE^_mCZ zF~}|IwblR7$5f=EE!dYz-^_4=zx;YUj%1e6L($ZE(uvD+=)+>mhLVcV4&2I)s%iEw zy^pG&w+Zd;wMT5TP4)HNt*cc=5YSR2TH91@e$QF~PBm%CT4=b@2b~NL)$eKUMyBY} ztB25ynteqGtU(=+@ezMVsf~{(mB?>~=2Gs;T$abtIg;}A1Lgg_q7}a?dEHxk3#)58 z-?z2aM*n;G?_X|udQ(YRaaL|Y=BSx|9nr=s_4Dl})zpShwqGLu#v4`y%fI<}%W?8T z$UciV>N4z*iQ62HxM?_0U5Fm5`&8tNP17#O*o^N`2gFYyEmQ`F`cc-&J(dU3%cV2d z&ntHwid;dc_%#sRbExXSzL2)*H9LC}jYV}GT}?`51Fyp@T-}JY9|1Nr*V|r^1|e0} znJ6TzsbzW7e+Yrux9SUOvvGdmSBFsL+-i%1Rzb-9qHF2wYN5~sWjdI!YdGv`g(Yo-m$hBLB zxfOF)3%d_jeq5Dr8&`d8#XLj0BN_bX05YxU?-Hy9*?a4ldzxCirxW*qZ5?}&X6T5$ zp=km9y7f*~7Rs$9p)e97Hp$Yz;=b#T#BqsA&6kk#lwg(1@=3G@@|aEgkckZ|M&6+i zjV_L)p>NW6#ACLCM_Wp-JUXeE4$nDU2A=tbT;ufwYMq;ohUJu@4h`F`g6y5rXAod`Nw`r(h)cH33|cOU)#{ zExJz|CYv&o7}FeetqLYU4+;$cUNZf>AF~@-ksGUElMtD!I^ku=?7q8*4pd(IJme#E zp*a=phbdMgFeGfLn1bDbt7U`mWAG$O7@?40LSG?v6AyrwkiL^g)l8;)Kk=* znX~9g^r-j%#%{*t(8)jwpz=P-I>|2F&Lb^NGHhG@CoD%Xd$7CGT3~HG?BWp`UIIp+fziPe4;SceuYO!ygT}^AbZOjQP9+-c8J( z{NlBD8h#7Phie+uf{^aB%?kvx9Grk|{#Qd6)Q$hZVPx*&J2oC-3I8AfLayNNA~m6- z_*+nZnDhLNP2D(vzoBXyA%wr7P(u99-;gn%e2%{{4n*C-KNJF|Z{ol5E@W;I1g`a{ zKOo&cv;qW@R(5+gewTcy z%tX=IrbO&Mk+ceo7mFebY0PT z8wyqQE+Ocx@^6wkq5N!LWR}nOP5iBOga3B5g7NS%@x>zG}RN zohhxWnuxn6Ib86Ta8Tlx-bvaeHph*oTonBZ@u80r!MuZ+3Bt3huhi}^ln;XH8}#DN z2cS{9jjf=@0quR=mF6_@%n1V;m~fs`So#a^TkC6bWIw5?*Tw>xu^$htGh%$p( zj)U%)3Up~r5@Vul7ueen%wGh#pkKzQg2{EW2rNXVb`%1QnyG%(I0^kyb+)nudr3K5 zP=-INV5To2K9VQK_ETJ?--2J!Xp$A)^BFh9Wh0$6F?RESf9>P8(2i~OChO&vI8ah6 zN4u->MN6P8s@ZIQ#_NEjnfmCrpnr{(gbDD&hKcZh$X4BE&n24vV zaB265HpTN;D*2AQBKRyVQM%cC3S*6A`l^4`{oS&DYi)Vw#SY*4aUGtP%?(@aQf+i2 z%=S&Xt$B`>!*c~6ZyiO4L6sH`-U=@>eT3VPe#T}{HhO`6U&R1+zHVCnZoIE1KdqNo zt>VR|lZO<&!Ru+$WLv%dGQ^T2t60@;0}K0S*IeoIYagmp^kiB>8{)h0n(d&s9V611 zre6CG?f|&9Er<3N>S|Tvm&4w)9EXP>PMLo;yg=?x7#Zi?zc zbA6jXbqHxJ$X>e|w-aWt8DOUn>IaBQ% zOr>s940*j^gvtC@c2(uCF7NZH{<$jAzOi;>g_k+1zHw-(dKYMZ|2s)?0K&6%ufae!Ln03Ua^s#`J42o@J9i)na1?=DS!(Jk8 z46tDJUo;dPJ1EBxA&jm`SPpb^D<7wXz1Ka*w<7%Id4zr>ly4?(L=7@_k`AN45Kfb? zW6KdEly|uOjUlvogrv$EdIs@)K{SI$W~AL??xse^rm{xS7Y8HR1L{0}QV0AO{ z68zb~2h>}{>rO6aGV)`~FRVA}hmM3xL;sWA!8c*Y@X?-&2fNu@jgkAU`;kHI{Y6tB}p+&ic zS~jFlS3)2{Prl=J{H4#8!?47sNoVm=(iYL2 zxRjV-hhzF<&?hF7r}T1Ig!q0d{hMF%V|t%LGWkpGUC>_sG;==e2Y^A;r*iF1Ae@26qFu->y|3-@7d*{y}U*$)pzNSj}c`;(T zLv0y!i#dnC*=ru~LNI0pw`seC*_#A*leoA2hN#6COdFx^#S(QhyhNNRnTl8~`pxM? zO%?573cfV3~m` zK@G{~U~gmGrERd$*b(WH`YZT!Nq2cM;f2H{PeSSwx1Fj~FH9JGSbD4OT_ z2fz#4hC@L6bjP~CH-+dX+V(U%9Kl8wWS&N?3WLhjXGG6opVc$ias*mcLivY$p)_Ng z(GujmXQ(z7pzXs=Qc|t8`atI{BC6SuzjBnl9M9n(i7e8=iuz4A+$dkhA(bqDEM{?lF5b{DAf)MTE@Nyv4jk-Bf>q zx?=#<=lUr)Z{_Q<9{fYa?c8_7P4c6ulgJ&iq3D~`l~PU+iGD$nj~A+^|gf?Mv1@ zYO3e~$w!)Rbtwcy2&7{V;0FC*e@og5*R{<<$0Eb5kzgb0SxZi>9ka!pSz3(iF#6;i zCD?V}Q)ZGzH2b5MQZB1Ffq&_<6#kxvnKjaD12gL2EB|%etbej%e9IsRF*HSoX?!&> zO&;1z>zl#<4*uISlerdZ?wU->f`_*MKwUuGY-;YLhX@7JkVuT#eZ^heZGz zMv~8w*t(#ox0Ds?m4WwZPnAxdyO=>T$uNSjr=hkdkf;WovArYJHl-Se$pOvVRppd1 zkRZ`d%5CT{TTa~p_oBR|wIDWPlj%fMHng5mf__!+#!SEtm1%(pJSBG>E0S21(#eh| zS4C%WvZ>s_?c7@W0nf=i0x);z1Aa;K_wIxEKaeP!ns66NGv*Kv!8R*@kwzSb`9ZP` z8O)BL$WSy&Kb3+$iTO=y#QH(q=*779y7!D+!s0S0vw*lMR}0jTK`CMumg*55&X&`s z2Cn9eFlTv=<6dBS4iT_zaAkKEPJuAmobU{!-EfD1Mhz-$#AftbQ4XmNvyF9+T!lSO zsic(Qo?&)SiwJWePiaNO!n%F*YEoNS3 zNJ=s7YF&v#EVsJ!Hf?~R%Uwpl#T29jGF<_A)Hi_0S{*23ongQ5_`(U|MhuJ~8>r~c z4itfUu~m*1(c<+;jFq-Zk&InKUnHE3J3==BC-L_f)5##hUxpF=A88rW1>#1|VXmp0 zM-c#-r6bfMz>}PB^m!~($_xg9T@rPld6g3s$Yf=6Jv`pA_wzgl<|AeS!JSi){s6S) z4=Nk5>z<(Dz(x5njGQ$?(1%^gDgq$5b1Vz#AO1V*E?P?TV26UQlB(It+Kc2a_Ty3t z_N)c~Fkcsyb+q)It38RV7LjDz zQ94m9zZZQ-G@ePq{1#p%Ex{!SSE07x1ws+niEvg}U+YKm7lxHQA-4<1+VijZugTRu8Io>{G|z;p4TYK+u*-V5bP{5zqxtd|>FCwc@1YK9HW7L; zOVy{5C0L2-Nz+B#N9FID@dT1`YVmX8Z^hhf6}dw`FX?mSnD%DBwm@3|EABWOSv&Q3-~zgg|tU?lIyK zcCz+J6AmZP^i;1ROi&YxZNx5BM0PqkNAW&sCv~cPF!B}cy(~ZAG2@-&rN@5Ymsr|; zscCn6r|l(pw%ytI7lO2PsV77CSpy`~;PYF~b0;Bi7BTG|@}xNuzXu&^yoX?6dJM{@ zDY!*?-|Axgh<0~zCb2-1ob{7DTD2{SM>(hTirh!rDVGPFWbBpR_1F%am4tMcH>GyJ zv*DWUUC~An_+y7$rH2;UABfGc6>TLP6JoUW0*#A=w3gtDQ0FYi5VJ9<=9tD2>>*=s z)mMCt{%g@E;#gfw)&|lM4K68(VprKB4K$TvTfka|QFhv61JEbA+4Z#XZvPAG&*q3e zC*xGGrYBD|AM&J2EOvpVb{yp_f_Jyip-n-KZ3E#RpeWXE_-gcvmYa=vSfXWWRTqA$ z$-AhRaL}+k3r^DN7AF3spf#f+n`j{Ay#NCPCg1F_5@1LrUE)UB@J_3~>Du5eLmSw) z|Ch2GBJ6V(4M6YoM6!qBNnOQMJ7Pr#6h}w7*op8=bc>A%dWCguB~@kO4q8fz3JD_9 ztSl!|m40919!k6x9vMgrQ;!RvIIM8(9z8(5%)i%<(%+D6kELdVwwp7kZ<~BI88lmS zmo$-{4w=sjp?`wv8Iu@8@L7c2%o;>DvI3Zfifq~e97W%%KFyM1ZN)Nn9v+$XgR_8G zloZQ(MJ|nu=ANXo{D1M*)Azae^XP7S;^n#*qjD3u_EG_dOGbZsZa1khooW}ad;`x8%xUrAB@8ni- zUAwkonUpr`3tSy_lF38qmiD_N%&&&b^33_Jjz*y zv2mDsk{M9-gm#{Jspt&-J|M`#F@CY)60Za9?8y;-S+(ph{;%1coFDFcI8S(!yZkXx z%m^zCyOc>awBaT*H!F|ezA?XvUgBQ>$($*Kdw`lYhj;_HiTg^r$_j-aAzxt`8k;CL zS)Zy_QQxr}iaO|X*(b8H8JV1<#Jx-r=UBvB;2am_|Ag($b93Lzk@KE*+R>9ae_F4j z|8d6|W@El`rz+F2@453uM8_?gC)oAH(>i?{_8^ZxU{#X7)GaZHKjZ|)q0d?|e0nvQbNclBcQ z8=*w86Z276DSV0jAq-$I#E%wEpav3V2%g}U5tj*0!v2w>1RFqW$(4fks_7K5Kw7kj zx?6zFd_o^5s7hoongt~h2bk9cmHua0%>sz~9`r+817(ti#X#jo@?G(=!n0I2(Xq^7 z8dZc(%%Np|nZoWkRW+C5*&* zbd2~~1eZBh9PYmg*dfxpZ)bCb&)Z|*Nd}}vjwsQawHJ{v-9GtT6j%FFP=dB=od6|f zrzVTM276yk$85&WR1H8^5;ByR8Yo1m;$Oul(glTgp)bWto|CzZs*%AH?$M`9xe?Wj zO%k<#3qTi}-8ZwdL~Zs#n5|`w`8#}*`Li|`an5v8W<)7+e>Grv>No~Dm0^H9QtvP}yY`r3zjik2l_*JNOOEj|ueb_vjv>)@)cp7~X z=V#DEuHrHJ;`;T3ZMw-7siZlY?FDKwM_rTAO8KgMn?Rtc72Jq%j6~UAe;6=D%5Y!B zdMtj~W`tTgubJM!c67vQGU4~^1JYLH#5O11ZB&@GkGTX5YK=)BD7^f0nYU#P?)|K75<{B{ zG`x44i4Lpo8KpiA=XMoIT@XVZeLN`gqWvFZ1?pcLg7_aM(s~;81xs&T4w-=4XA#uB zCCoLQDK`)~#_{=2NZ<6{8KWq2ZA|<-+H!Sz_%QvUGSfeRxj~-f-p*1+sG)W2gD8)NDV!4Y=?Wa@9#&nz z=Hl_7j12BSVov;RUMIOIJdB@B1^kNnzv=tkN(I}1`F0jPpjlwPOcV{uO2sTm;NxK1CcO#{nYLTucIM5t;~H&f1Q-T7QpSfh(xUW535=FKFT@i9kje z*NYqye~EjJIyyXzr=WfEtLGOmN4cQ{POL@tY1DnN17-%T5PnW`i*^TbTNXj5BH!@E zjM?Z>%#)0*n0cfxOb|90{SO$2tA%_7*5P&an^`r4ixuAN(Inr34E8YDnz58qNOg`s z!5vH693IL&!@&8$c>O?<8;37sFKO$cz|dpNQ>mlSw=`60H)gx^A}s}L<9pMd;*JvN`ne@q0PR40w1j z*O~dy56c_PV!CN~A308K-sJK4dQ%&DJz<+pXMB#|z?K0E!u>g9R)rst zi({{Hv+_vXDYliw&E#XIr6f2dQEetWQMO8ckvVusT~21yvd-u4Vy3Z+(+>cV>|60BRy2nhzL;IW_4gxiNZikEecVpo32Q&T zmVL+QMp(ptqv|5uWB-%PCJuAvb44U7Cxrf%oWUt0W>8!>EEJ3Ki8Bapr=I6rt<%sp zai^Du(!062`6`Bmt4{A`TDVu@*}x#rH+&}REYIwR;!NgGbL;08@(tG6xJ1E9gB3qT zz*Q~8|0k#rn+U4~;oONtvA~HAkU)abge&9({yWqx$};{Va13=4|G&C$>NozSvd6S{ z{EPXy^f&yg=>*0%{;l{LU?Kl;*bi0(|EC{_y+YvXc$bNSqE;a`MT#;^#?6sVR~m3n zC7Z?5@%tqpj)KrFnL_(b6pMEgKqQ=)i0mQPi@lrAQHn(OYcEoBMV)0jT7jr8e-yo3 zv% z0^p3tUy{G0@d^9oDEuShCfP%zKWRkP*xXEBCB0RPrL2?Im4#AwOK#+CqMer1q(n%PVjhfEFXN?cp-ZGY;!ZP; zN#2Cnfl!H)pFeAtIMJ<#QzmM%TtfXcN9&O2`=(&|56nrUpHPC`X7FQA!mZRtP?h)| zT_)}ep-WqZ$RYM?;7uCRIyI$cn0#2pD-}>4C`EY-X=4?lG%7tp&WUSd2xZtXI`gBn z#LtN(lmxnUaa_gYEUAuE(yzUPZf-4;(=f#?6NO{3Y336w4KCKyL7jmQH*#CA`AkMZt2>GX<1u3UUcijywM9}~a9rzltQjw9jfQLFb*?Mw4smV;|94A z@1bcxrS)>8pV8sn7x{9GZw?YpwBb7&d9egGEkXtLeSt_=4vadFT>QXq% zKppbC9DLIxA(gYIna%pkodteR6?0Kg4DJp0FzhWNoVN_YZ))aAP);=syeH^Gr8D?x zSWzyS--fSFwea5(Gh(HJ5^`Z^wBQgG@YyCzrXO79EIbT2TT)n+&H38rthHdJT+N;c zIWF{I!(mfcTi7SzCDde2IHC==k<*KO3IEHTj?QfgeOpF)!bV`wjZP6`Z+U&~^Io-7yhL*^S8LaPHx;WT+TupJ>4Twu8& z+gM1J8MU7BgFOj-5=UW+Ft6ap*}rfuO=CF#zPx4@=QE+BvNO;1PEPLA<(cFngxt*ltdf&KZfoxO0IMZ18{71-0n`N?X>N* zZJT8u`;^f}Z1aW_O*GNOHYUa-6Fay5)Ze{Uty<@NwfDE*M?Su#hPft3TVpXm{=RJv zFkQjnOcHHUJm>u4_y8^3j}2PjHSbg9c~HjxRrDDAC7hbQ6~aW(Dc;a0F&5hfb0t?o z0r;aV+Aj_1lKq2nT>dyXt->Hcu`xo zJW>+WFjRg?a<#Hd!Ii>A3@}>emE8!elD$g)3dYFy#-@Y!75$++$Ox$Yvf(_i)Aa}9 z0&TU|qeJ0a?JlAV(qzp;ajAL2Fd%*+eTk+^q_Q~qH|Z$ZV&Q7(68Ye^C|QKOxqgT2 zru=H<3^}YwElO87DvoD|nmx_TrwK=0fJ!&~6f_T9)* zWRq#Na5M0irb(0tJfNLKw}GF?EwLQ5m763}!6advWCtj1y(cXN*VR|bhJgQ6oRn>Z z9E(QDE1~A>L5eBR_ThBzSDA<0vL8UQQGv-?(1SLp z{}BeG3Dj!g4b%f^5TR!K)K=__P8M7dpGU{EDkLa6vhIh}4;@*-lRiTy6z-I*Kxbv` zkvE_&$ufmA8Wrb&VjF+(x+y zo|7%`3t<=OBwHS4h*X(c7ink<`3jh**QK9XD!L(3nL zQ6WzicZkb=Lx7(|lj{R06#rzu3}$i0u#)>-H$v6Q+o;Vaz4_Ie<5ZY_+ znXTlP+L5(sq5~Swva8~LbxuLJ1XM{f>!nr7j(NGVcy@ElXt_UgJVdRCps)BI2DsD> z*Tdi+lTou zw%MVa`q@UxHke{!2R8Q3*_-mcCnHOerIPKZ=|5AV|>$Lp|9yiO_OM> zaY5N2(HlcqzD#^mKQ&{kuuY*WxKsT)I z+~PaTMNY?}ENn5iXu+RA8_#aOuQ-(Vs(%T0G=E>;#Kyk`o#x0nSSabbRq7I}_W zFV1V%rhk%T8c!yTkY?)NMm>_1=tc$i$>ka=-&u;yDmPay@SXLs-vH&%{`wfqt=v%= zgvC{~6V6z<`N=*S1FGec``G-NH@uD5k-FCAYV601u)bb^l1_qZJKaJU-gp2sXQ;7n;u19Kpu7_?N|F>8*?NyD=5VDq}IWm%A~k z412`SkGh4A6G z;!RVwqyX`3=J9+HVT+;Z9kRQ1XH5t4yX|F}C7Q-{&zGQ`JVk~Qy~w|rWOf#Wxlxy} zAkn?xxtK&k`&42_WGOCXxShh)E*7W2sk%gHRP#_a7D{Y>frmm=%Vp37I@WqfG9Dh= zww?PLF5;|f+6k}ZuBoYjKk>GdzDCmdf97W+O9gk$&CHjgiAkZTn>at}G}b_>w=7yBvz* zUvCmaU4k_=j?il%QMwmS7uDsv!t2G^8JWlsNoJBWk|nK-I)W^e5y3&oSNU!qG1?4_ zb0N^DpxjP@aiM>;Vg)U5V*tek!EOu!?1d?S1P}@Dh#P^EA}KcyoFleqatA5#_UZ@V zV@Xvh1EoqQ=ly`TO3!3?!82sNNfxkN&Wrj3zNIJ(4o1>}Y#$go2R6F2qg7CcoeupB zpVE$(mr6>Qsq&4IJ=hGz5UCYlp(vN);?at|(ifcXfSs(k@dm)k{;uu^-kNpkd0>V7 zMBXXzj3Osvq}g(OocIme3XoA-U^mb!I0W7ZKJ%fG1ZbbjLgXpD!frm=iS%lEWl@SP z^fK8(#cg!0>=!Ubu~uFOgo{?nPXl7k0!0Y0wGmgW0p3?916E*o$uFP-ROM;Gap3Lr zE1(JrPP`5chX$gSKx%koa3E|A6Fw^VC^FGyC4wLu?N*{OXuifm>Vde>PSS3~4xK9- zgjgyAaV1!b z9Ed`|x5(>YJII92_Cet^w83QsVu}9GZj)Kh9Hs7(1QET|TFDBc7WqRujqs5lm8$Vy z!V|Kg__?+dG7?|juv0z)2dmWPw^cz&n!*zI&wZ*`hfhgQ1R}6+iS@u~?4PJqunxNr z^c|Xr9rJ01Zea&p7QhFw-F7>WE!cMT9C0~2m6_D->x%t^Ec<`KW6CIL^0O+n|tMMRlT zI;0>*xU|Due2d*)M1Ys5wuzQ#iphiGS?UAuX>qsOS$0V>O=S?=lq^(!YkeqnS2oq( zl^-wB*IVAhIHoO9gwgE@mlZeZA(2-Ak`e_i2jj`xK0c5O8STQr zF2q8+Lr5V0Rb`$+8@k94ky^hOP8E;WJ(d-VdvwzT^^)1z^j5xPyGC2zDos_Ns?3l+ zRgWxoFdNhHITz&tN+c~!ewW>zpjWWW^T_!?5Mvo61Afu*J{I6Js@?^FUXqC2U+_<& zOUVkUj_t%M(S-K-&=JvmlTvy^>~2K(FU9-J{pBx`Y<*|lC&@G2s*1Z(wRT(49+|&p ze~wsoUwt(7qr6>pBq3H2t=tz`s`$)q3W^7gGX36fz^yds(hBXQ3hYk9CrD5Bx6rCb zPdEtIbtgeFqOh)E(h|{`jx&6&xVC*|3nKnv($ry+PNSfLBMmlW6lF;t>0NRr%X+on zQy0iHG)Lm!Du$@>$f=4yRRKX`fe!Y8_X&_-lrF^(Lp$4@gS*K?tX6P*zz$z6EbRLe zJS=?DTQ9jG0(!>tUW+Go9cUROUfn6L8!k!g@GAc*d2PB?_>Xjyw^ z{)k^FKcx+iIHfSCw+5aC8dQ$ni@QqJLU|b>{`yI7E#mMOz{=uFK$DG2l<$v z8*r7pAT0GNAdiZbb2gBBq~~njlA9GSY!?|@^#s30denGA(@2|IiF6)0qwY^WkDSpk zu|-E(HdfblkTy+wD-@(di=$bWc5UsG{57@D0d8%#~Zo;3|UjWbHj~iY~hvAQ##`Asg=godC1^Abi zlDb-AbgQ}|ldx<%TI59da>rz!C1QCMsdI@0{?YghB1RY;;Y~z}&IP(7g2_ZOzd4~-&5qK1~vRMK$*p`-7$sz1OYc1~`cCM|m#mYPpDX;UwesF6l z?D6S5Vc}0akgv(!g69Y}r`qAw!l&_Zc(d3q!W-vHi~*l;P&Uk~9q*9$IS<9R14*{k z_+4la(~VZNwPH_E0cS2a7iBoVBocH!_ix^MbUSZB^9}R@pI`e9eJe;VH>WKKJqj;j z!J--2Myx_SA$24MN~XpKV(n7*2oG$PEH~gMc0_LST7tb&ymq$4?Z8@F6fcGzF(HT# zzY)_RNrEB3Eu=!QMB;!bgt5GGq*wT)xgFUjGSqHC{t<_lZ$rn4|0`UMhDbJNx1tSF zdCF_FU6vN_jP8}YN4TI*6f*;dU^4(~uZ>t980qYd^+HnHKI}fcm%a;661QP~u!s0I zfWyg>Eb(!;S#pdw0yapanq%NUq_&92}mq=hEsqAg>Ende9ayK9|Ox$mLQ|RV{spmbZBmb1=0hp z4;+R(hyA?{pegWq=M;21g4^!HoX{}p3-BI#h30{?p>K+{U_Lxb^aWJGv$(O~A=syh z27kh-H9Me4xVh{sgu~i`8_;ohTh@MfGJG$k7Oq4l#vOw}UX?_$MXE0HR!+BpSzkBMwA zqwSc98l?!L z^%V$c@SgFNC_uD**&Ng)H=v!t064&S6Lik!|}NwZKo2V`Wcu2VsufQ#Vw0 zS}xWm3!UT#G@WgLVv_n%!)Zm6I;?tv;)trfGy<5Te3D-Q=#}A_6~I^4kn9RJGq+>+ zf>#)qa62f4<_DBOXQ*vnaSfdJ6$nr-$s7W#QXQIi1+-M&j1_|h_GQ>Z zXf*RNzzyo8e|SZ}0n|@tXZQ*E)%F{*jCha#kbdowLyfYu&TQ!!+2Rf_fw$~a`;=CV zJj*ny{(*dhaa5IuV!UBgNxedvU9Jf4RGSJWqCoxyC?lCZ_#1tov( zd#IWX^O^+@VFo+TfVWde>;@xTlEfM5ir(koYw4$+4bmK0YPW#DO}4Zvv~_~)N9U;e zW_elroywi^gQhLT9~ACJD9=N&*btYI2+YzCn&$@eYPZB(1!rn9!ivGgs(W+)fc%vL z&v(!j=9lvbn5I>BqmfW*GCoyWJUs!eWlx6o8wsgqW_YAB1S3a(5 zQ)P@?+?iWU$se}A$vvznHL24dDXtr5BwYZ?^c^t_@KF0PY$7Poq|HU35vpaLC!sa$ zmpLEdd?wv)GBTW6gzHq+^tOsfW3Wy(=CpX#r(f7MB<99g#h zdr20X)M(0UWJfjoW#lm@S{@{QXXv&SG5Jgw7YdcrulP;=Bt2iq^Jt^ziTj*_s5jEP z)^DgDg+E@xp0C;q-eKEohDj^fyxMI3C3a@rlGa$}e*K5~wamK4w94;{tZ99THxt`3 zBQK4a*s9Nnrf+gAlHSrwxa(r_=}LYy}acD(YnG=$32LsgKw8PwPK$_tfe0H8aR~g0NvOfCSHO|vqqa4Y zZt?^N<-aKamvk$5jf+BL17=-DI@jcJ3?EO?V;w3uz&`m~@o*EWRHz zkGLxN5!yp+lezmJC)(vakBfv@anb1yq7clsF%qHXv$;KSm$w<%K^*26N#+r21cQ0! z38P?BOD>@hR@R**YDJSPgNaPhsbYqR5bJV}5MGjk^qYjO)HP`{F;O-=CXyH|dllM) ze~>@$KaW3Ayz=-5zX8s0x`_V`mDy~<55gPG-tY;L9%#U~h{uaR;{9SH55*bDl$N2m zMABbJ;|)@WiVt|PbaQbUo+(Sn?Zgvg_tUrIk#ahz8;?+U$9Un}nGusMzwAp%r@?U(fc!?7*0C*mG#xjcmDXx63m&Fe9f{BfNhrc}gK ztj0*ilH%bQ35>`s!YDwLPGJo2D2a!uz|xqhSQmIHl)+X(`Tm!&J2RE|3Oa8^UR-VwHMJHkh~%T-36^K+J^3f zZsv|b|AMEaN1>PC?4$tnAJ`QA6#W8U2yMb9BeVREU_J=f;~kcdTz2}35onstYiuie z8ex#j=re^KauvNP+J#(4&vH|do9MBoTgZL%Ky54X0^L>q0{Mh)FUm)UquX<~p|j9E zX|GXV^kCvEG!;D_y#Z}RFN8**D)gTJYIFr}GM{x873$#|bXfLB_$PHOEee@Ki4(JtENViuI|7lb zLarg}$teG7Wr zMh!gCjH5Zh?#lnlw!+EEnnDk_jXj&)4|lWSsW0Fo%&NrU@Ox%l)Cj70UlMbAi>46QY@N~dgg4X<#Tf3&f}vvlcfl=4sJCf@AgwOBaTc^jOIGiM zj%kjUC7Ffm@dd}=@#>81a5!A0OYMYt%D)nJ!7JD=QLEs)tbIr;V#CDwPekhIIu9MP zlcJoSp_9oawtlFH*b7YuFL%hK`@#R(TLiVx1XFd}aLChG)36dsHwdc}AdX&FdIK`* z))Z7ge`xn*-Gp9g&ZLIHv(?uVn&2wc)u>8%vGT7F5BL$g$?q=W%k+3~%;!AB={9ng zsZAl1#Q-vh4b3ahFFPj(I|`3k;oKbo(CW}EP=sZfP6 zB!z(%=pV%&h92n9$YZd#=2!4?m{1G-`0yp=bC21GGh6R;2!ZIkwt2{Tsu&dM7MG8c z2J2iaX#N-N*2-V4TeM--P(!umNX_@^(VC1psC1F~O2hDi>FUbHMOm=wNppP4ZWXuX zQ~dvwZ`$@nRw@PD&YHK?_*G`ReNAMm zSwnUY`pG1V4*Ej0`M#gqZ8}93Hrs(ZsBp9{qWr-bz&X{;#to8Q71^}Wyj$YlyrtDj zd7))j{Z*yB^>CFzY1MYVG=Tk+^CbTO+rS&16~vC?d#9+Gt%BP4g-o_^bz~d;T=YG7 zG>u4Vd`3z4aRF52;ok&>$fz~9s0Q?3J%8jjAk}PF;n+M-gIfgT;@a+%&Cs>$b?Z8GOGExBf2n^SK|=Zk+=KA^`+x=Q+~ zBT}FIPzsh_$UH*@$-tBd^1a+Go=fhO--|3FaYaw?WHJpX@!dvF1pVFT5+@15vWxPU_mqSbk z1=+$aA%l?}&Fn|;ah7UtcmdzP54{+I2Voy)@4>!eFKuG5_t*p3A;N>fg?;#M#)HGhFVWYUlJIpjQ)`ct zbU^t}yo`EObPf-px^wloB{eT27W++pop&93My`(w$F7j25e?X3a(Gaa*_ht!n`t(t z%iS%o&BSZTo>|s6raAH9PQ;O26`M>=C=W zs0};DM(28C8pZQ6df-TfcFZaX{wWMe`R-?LW|SG!%2C`+bLFuGA&B7nlb%arSo1k4&`@V&McU z&B4~1)PexaXzXsAj!6xx8opp<`a{*XuxQ=OvO}1I&Y^H2HcDHR(}sT0u;~HlOZC>I zf6#}jtFcw)4gY_`SD{bX&w(e<*UT56Yv>31yW1J`D?NDjM)Vgo!Uo5NkYmj=qukCv z`G>Kfj@_**Fzfbh4N7c?X-hQ+eQMlQmXH2zI93>d?$lq%8HujfT}iu!F4JB~GN21J zf5%#&i`4%MuSVCY*gy^XhceS=C3=Lla@&OdMZcQ896d=LurZ>iNs~l`4ehzc&qE(| zA8nnB9`D-HFb&<%*GSGOm*wcTHhL8L>LxaIWq;JRwTwwyr}?W@lN6@0<;;xHtA*TMVg0H<`HcYz z)hMBx_dI2Sc$n)DRv;bZu$nQ+r&`6*Yk?q%uVG{D9e$``a{cJmD7~~Hs3B5!v$3T* zSQp*2q|8gZs`*x-g?3!4ZT20Fu#J<}r@qQLn&hnx;km_VRGs`qVat>+giZklWvFPM zcN2?BxUTNZ4q2qbW%_}_*-Au@2Cc=v^ot-Ii@W*lc*_8~REy`Dy?p7BQ}IjHgk@iDA&mrx(JMmb&dBw#ViljM4njM+_d zO=AYjWez{+aKO)M2_*!-iTCL8S}yZWYLB(fjZd+J?OS6u{R^8FqafPL~)!n?R zf>Y`-{QT^tDol`@)~fslxbG0GddLF6S$eZ2LrE%mvs$Z<`=dZ3>t+o(wOV_D%!oY$>s*$3h z*|3rn-AK!1e~5P{{$k~l#W51*ozxJvh7ri9fX(zPIqSWgu23v-MW_S7ZO15TI^=Kl zj%OP1PJ6jEQV8W3HsYE;b>i%a>+Udhyg7UfLIAKBS#pR_5> zgPkd}PJGM^$gaiIGY;~eu*LKiMUlBm6yWH+j{4J_+@+`dp^=V7WIsIHdMfc9$rRmC zyGl-RU#T`q1Dk)U{H4q4$0>KqEUGM(kutW_g*_tsRp82|$ieIx%mw-P)E`WVf=WC@ zKLRGkq|jnuQJ8@q3wi{sq?UnynydIiEv~)fHrU6pmGnTyS^E)7kWV6s%1^O|t5P0P zJZYY<%m$q6Hn4YrmdgDs2iRJ2p7{d&$iK?4U|QB`W;(bibvL~l{FKPh;ZRP@4C*4Z zF|65aeU1#!QxoAv@5SV5_=;;E8G@8LDu|=VJL@ze1ML!xR2IP)*NS}s_cnX8D7?8Y zmYocrs4Qew!jDS0OeiuqUqPQk9I`}o6OxeHKo3FM5<{s)NLTb#Dhk;j7C=5k{t0L$ zmFPHcE$N0vyLJ*MP|}eg8qiDD^~7MzQ`pY-;u)OPtS27Xw4XVQd)8iLN^qNs7xX)P zT*+`+kNwD>M!RFLv!+s~um`DQDK2&`;R$7dor%_ye_)40zmav={{mvj3D`<+5wQnb z>Z&H{v6YT0Vj{N58pQv=_6cV)Z|HFxKSoKNZAxS8srK4>dLNZnfzzebjN(PqH}ZDg z|ICX;OEPy+31nl+cJg1+E1{blAl^qOk;%l4&_l!*LKHBaSVwq!7ZK(78&{N=gzs?F z<417R+Jr0cWWge)T)Dq(KmAQPukkiLz+S5PMu)R?6|<$zM6fIuFC--a4H~jZMZf(pFucyq&nBS`)vKFex8L zDTr+4^w5dKbT()%gWqAYy#B=((;U|%yoA!5zp;tr7VFd45#l00iMDJ%*V;xMH2u@i zPBj?6R_~-H7%j@~l7AQiibj%6`m$Via;A>Rh$qf!7bTYwYR&HW3L;s3GRlV-uR0y_ zH-1yOYi=sOhVA$I6X!Cds|W5%3mjG0Evm-)E~X)KdAF(4U8h?IQJl`p4Rff89k;7f z$(`*F%EV-y=~3Y#(!%&C=NNI^a4-EIg3w=19!f;(j>eB825Z+vKEY3G;1D&wSUq>{ z1YE4V?`6WH*e=)c*jFaUk-*l_ldRujIphW2iSEwwajni>ODmKOXy>-7q1DGbPF7Q8 zzuKSIjwo_&pIoQQ4Ksx{SZ72SOB(-3_BBYG>*I&$^{qCMeY(YMPlBIlH*){7+1E{_;(Xp z_{cbdLPlal_ zMBv<|94m{p?`J+LJS@BD8=wO(yJK9#x0dzoeobQ<2AfKnt*Xu{uq-9_*x#LW592`8Kze)RV@$ku$F5woDk$Tzt{g1e67#b2MCu{t7^gFJ4K%g!?Z)h%W{O8EJ<#9pL#$# zdfraeBk75_T`F&xE|O3J@`8}@>`8^4zmT1#c<*_XDFE)e45n%Dxq~aU8Xj*IPaZ|0 zxKa9Z;t$Qs_2VVN`eC{Z$(yQ5ZHJU!x><8q`d`6Ujjv3S6QrhO-_sjZSLEt>29=A# zI<8*{DE=4O#{Lat1pj88fXDtV3;;5oN9m(bh|5@d2t3Lmk_v|3Sk;qF$OFzT-9!1d z<~W_JVp!cOtw2#&HB_@tv97dOGZ7eEuv*;=lx9Cy9R_x!+p4U=Y4fs_auAHGWiNvN zL`Jh-P+IUEMgwg%JAhAM8_#`oINawliBcl&4q49v`F_2Wm93n)q7!yFGMu7B6!ZtX8o`S#jdrjf^K2Mr_jT>D? zkY#w2gNQhZ$5~w_LU7wQTg_B9s?nfsW**i2P#t2l74<56CZPB)rHX!#Kb!qR8?$6= z8y%H;h`CR_Od81)QT?&O^m!^iB7#mLp9Nb`dr8LcIOR*)d2-3+#6IVr2%6}-gs3sTJC~cG-d52h&(mHD&Y9=Z;0z;qJtSfCAL}*!}kz& ztx@VZx^E5tQ_;HE8fWEWt){$FnXb82^qt+O3CgQr{nZ1RyO|BDpQ+;+S5--3ExlU# zznEpTt8!%cE@~~C6||W0VpzWxax=Zl(~b0`UOC?)mQr&ZFk&)UWo?C{#6Zhg)g|MX zhD4Q@(X;vwrPfeg?#}+F?=9+LYjhWKzcM#tuWsX>5t=|5iSv{$1aZ3aWZRZ)eqr za=#v4wXSGwcWLdEywP0+bpx3No!JfUDP0}Wji(Y$wYxP-qaT>Yw)%t}GUrKr3#``f z;9m8)u7mi;+>^C_!qZM0)R)Eo+6`7U%Uow=vL6)umLYvttAgtH_O7b#tZwOnYyK-A z)?HDTTSRtc*YC`I)|u02of+Pd+oVn*+EZGFC+sstv}&Sn7~MFwVMh$3xkm!Uy1RV9 z=cjg!FwPy&G>Gk;9;z**)^?%Ft#Y4P44VpMHs9;rR?DsL?orjPu8!-juYXu}yDPoX zzNn%zv8g8aL`Qn_icE+0{FZ-HxTgHJgoKU89L|d93x*`#KA0=dNi_Z41gAZYt%Jrxcn~J@g3+4UN3x(I@ovf(c>AbOpk?K)p6tbi&t9GeHvV zepq!|dTLIXa*%W5DmtOS*;4@9ML<@>^b2ed>&8eOzYf@NIigWYHeT zxt&X!Qn=SMo*FB8H&UVu9RA}3on9{Z8hucQh-QWz(aOZ}fnAyg3FsTEPLUpRzol}L z+sr9r-z$3Ujxc*bS4(>uf{r!UcU7B{%cgfGaviGl~uI6@GccTG5PX0=f#hbP$j~Bf$@!>POFw6{>ekCt$yc=} z<@O2DnkVuD(FV<6MOD~Z^-N%Bpho2ltny7#MuXYzAK7`(_&FU+0`$b*nhu38TP~p7 zkuy!r?WFAQIu{ct_p7{bRLFZuH3m}tz0g6wKvA8uMt4|oEW=Uz3h+$s)=mcc5=Ll( z!O_tb>M{@sBUAwRE|6uZk~H-RfE5yK|%QK`FrFXUKoOm`Ei%*ob`g8I_WYD1w*$w`_<*dl(vx*INtwo)B} z2f|X6PvLuk@yZd1gReE~fHb;4VZxD}a~9DB=v4azv&)HE-Y04F-^M=UH{?Zaso?`U zx^lYy6Y5;@mwp5qT~KFMICHcA(^jE1=@LyZDwy}5`frqs=ctFGeNpFC!RW@YG0J-M zaNrbHiC*^k#jHl3x?iVHqQB=Xq5j3}?enQAShD4BG8B_GdKq(wx|(-}Y@)bgi@uP^ zD5=r45zz&MwcUhAcBkenF*$vfW*q);-U9VJ{C2#xipGycsgwuJy7X!G9o`x6h_%8c zK6jZ|Jm38!-HM0ISxoif_V#t;F?_6*4e=iP)NsPElfF`;*B_w!D$;dl>C)og+84A{ z{xa<>>UMUprjS~Yc39m)}*V4WXaV=6_7SY zbxrv;X@SaCSrg}{Y-Z0z?qIjDA)!;44@`GJD&tT8&;s69fC zB&S#jhybF#9@onaq1EX+O0O@s(Dvx=7oE}U(ap_MsNZVYtZC|C&5hLcDoW#)6t6s^ z7RTOGTB`O&wz7@NZy^_$O-he|iS!3H*(Zn&V#?i%C?PGIBOsSkJ@zWAusOy8=m>Vi#4Wy`g(Mq^QfrpjqY-Fe>wZs{8t&)HIc!t}^kN64jEiTK24F zS)?6ntKJi0U|LoC=U$|DDcAZ8q6e{BcW)|+k<2M21$2WwLG)22Rx9y+WLCZVqG{!W zt7k0OR-r2UI=`T5Qqf-nL#q4pqitE$eD*xs~`&XFywEY+d_rPG7_+(?Q4AaUZn1l<=G~yhAARuK4JNwZ{5~`KN_m5=Jz)@ z;$=vmP1CZXjGo)g$MQyYZ)$m-xxUNLHa<1Blg9~3Jlhe^<;CW=Pv>ojIBYhoKZlGm zbPIFmI_iT&`@OBTrzBx+gEU3bQ?pO1Zp$UMfU*P#m}SFU1b@{T`d2o0R7v}aTe`{$ zdM#Reik!Qzw)N#c=-R-UpQ-IMaTlaIcSv~46L+^~@pr@~np_3vBK8`-2tS03((e*G z&-KvNOX|J7wZo*_+$`0LB4AU1?u`+l8ux-Ym|xGUuML z+^)iJT^D(>+&?Uu3}eN=LPqMYNQck$ z)pp3tDR7!Zxu=__>aC*8X@s&JSYvyNb%VytBI*4wP&d7|i*HdiyeFG~rSw&|g`mIi zug<%|@?2fVUXgWXO#4dFjg)65lX!lj+9;9K$IdYnN&_O+>F3JEgbde>kUg5~tGOuu z(>q+d}w<(}?V@wC#_UH;;2g>2_= zNmg!7`%}sD%(14cQZi+a@wm(;vDUCvb})9de!jdhqECk@CI^4hmMIR-b<=nP9Pc32 zXVBg)Sh){;>g3Eq&~dw=OaQ!h)^+LyvZuDW`-JpIWlfh+CMeD8ESEhh4DJY!7v+v@ zx0avGIA)rx2u+b2M=3TWy6C?FvtzF4UIB|ExZ0bbZSZx?K5)_8N$L*B+S^T411)h2 zRQkexPJYZ!_?n$HeFD+W`bBl1$+aW9egd+}p`FKo<0YRvy1*F)ciZ`3L(UdcIk-7P zWGn_hrnngjp~!?g`fNxY)2>T{ZbbNMV_^5-l^S;#@_(fs3ZM3#s=S8Sy7{sj5zr}& zQ6Z1)=F;_Ok>zYE20dT1qAL-;SFyBn0y3$jr~M8RU4WVPBF#DZ#*K(3W0qkvvO4*a zeg|?iL8se^JdR1!?mPe8p&f3nY#6%7DVDKEpV=kT zqcJPXBy%!yZcR+*B0Rhzs-qQ8ED3Cn!qW@vP4;+Z_7~#}JT3j8!48i}M)mG^KthDh z4|k9LrVYbqhp*N|;a0(Z>HvI_f3Io=KE&%E_5=3b&5pT&J$0H#AII+6l~cR1tCr2= z2JCG0-OeFYd-r_+m71ItXGXJn~F=dgxz_5UPl-{IYP3}l`*X<;k_}AJ)WPbEE z%{kI3yh(kXco+0tb&1&P&r|LvP_Gm0QXosww;KV-+HL^x%sCY$l3YNSKT zUMq*%3t4%w*c8Fe%C9kcGY7IG4ZciM`dEDwGi}~QT{e94NXAp4>4}eY@Pk^hRA= zJl=R!bv5s&;iRfC>$d)k@=n@Dvy#~~Poe$5zKu`Pj$|d#vozMsr?9(fFGd_htNiK5 z{$rIMbcz?uT2rfBA2JiE5l&(BQ1fw;CO;9UEl&{72(PMv_G7wbWlK#9b>oYc8WAm! z*JI#l9%cc0fhILAL&uo6+pM&U)zjkMXm+S1Q76=Al;^{GR1cJMf)bT4S&rW&_9e5# zD}(um{@e8seUtj>lKC;>JsksBZsLH0DHKvw%m~su9iUN$Th6#B-hROP- ztm*pcx*e&nbl%#pN&B_w+Mu`|O@l@dC0FC>Rbd&bKGmh5VanCYH-3HWM)rf36|;eP z<2s*SMZa~LMfFi%?b=D28frD1;F6Q7)Jva~Pc73e5mXEmsTcjIa>!FG)K#y`%3t7I zJ2%a4;6&Ztq&xi$4Hcx*Pl;eT^J zWN;A;aX6yOmCUx@p%KZ7XY{MO6kS#EOHNhIC`(^lS`8FsE__vUJ1>3#Sr?z>Jl~;y zL+bbb6OGf84)?V-sp9b7K`o=ALb^?@9icb6rf}Q>^E(LcW#5&itNad+>qaYKo%0uc zi8#sOw^lEWvwo-EEzg;8P5BSdQZ;k&mRjpFhee5X6-7=9?$j^IvziYyJjog|VB6%K zdaLhbvm$9tudwA*9H(bOn@f~i*D8)a^kSz!Z*X8?`)a<@cb93f(B9*#AyxFZ%N(6S zqHzq+oRL-8xT}XL=FOa<3TRRB-kZYdrJDZ zW3^_ATwzn8Dgr`gX0ZLx;L1A-mCaG54;Q$!Bo;m#IMte-d%a)KmY;dJ&z4i3y0qs4 zw=PNEP4WbBiCvz2Rn&xzi-I+w$J)8Vi-Bdvcj7U==M3eNY)@z1PU%XQ2JJ-oI7dd^ zpwOFTtDT_3Oqv}A9jx5C0B@@=-8p|YC%tf2|8Z_W?v_3Q&n9zGuN{AQD&2jB|2nC< zOD(t&7tk3bJR3Ev{l4gQ=php+J{MSSoFuv9dquC2zW4OkewI&jQD~|asg6rkM}he^ z+my4RK{IzVC@icL&!>3CQe?oLUs6c*T@pCu61|w<-%M$bzi?-2P1j2il9bT7Ogt~n zt|Lz}EAmVGP|1Ix`;F_QX9Ft?ak8zx*L5f5J)S|@L2#~ms^aD~lfb}@8y z<}+q2GNsaOpjUXaG^9UKgcZj3eiYBiP3l=AK9w2MT_fS8`gPe#Cns5U+?4K&{ng$s ztBrhUN|sxMZZ&+C9}g_n_b5=`%evV>pyyo8D&V~frFH|4I38A-b2@F_vhMKmS<~n> z$f}B~{kx>GCC~a=r3VW>_1eoKbAEPTm+j5?ZjOHoPWi91RQ|ujdmYvazu1$eTZ-+G z+l>Q2Sg6jB3mgrM(0u_jegD*|!0Vo_nu$=83#}S}hdQ2Ddca$4$1(d5?yP7!6ZNe? z`(7&+m$dgTReUd4)>8}=<*e_X3T(+(-+2ujk+Py=DOi%&)7}PdjAcxI(1=K>;VV=V zTA<$p9SoePYk+NhmukMkfafeV1>Z1NnU2Ic-eI>RJI&@)A?iP?nfiuqt8nZKhW;xF z>Ujx!79@8shZ}P8x@zEojDk)#_+(0U`&alwVv^|;Viy}~>_svneGE+q8ah?)jcg9M zp?!ed_k}eIbdu*t^$0Z1g;Xv@<&H1dRCKFtEd33AGs{SAz$)`#n*dMv9$b` z-NUf*?5~}FVJ+#uIyx~y@{jgrOq%e~6paBfuZEF*d%R_I|u7SgZFBJu+y+VO;sZ-9CWG0fw> zDh0pl0rU;SIm0bf zEp65jb3g4sKfZCDW~J9315K0WrqjEsUrhb2y{)!Zn3_kLRVAw$FPn!K9Bb${wdUNb z7n%Gs-qa-<52k#o^)*%{{8uy7@NH2)$3KSJsIm6r2A_qKY)kdq=MS**^pU|gEz@+{ zW_6nm>-_wRoW{%!ukQvw?O3;Z-2;t&nwPd+{k39p(*av>NmQfCy1IbWkYJsWgV&F> zbY<9TUsxumG}W9j*C%vUZ!r&#UF&ExDIyQsG2@%C3$`L-Nyss)r(tXGGV@LSpIK6q zNgwYQW(?Pvz5dm|(_VDz(k<6co0g=l&@jqZG$|ZMiqALBbA;x9s()+m&G}#5cKe8o znYGn6e#-nB*m^W!Q8mdrIkwP|U|~gq_5kyiFpF)3>2rw5a@#aBSYU29Ce4aBNmYFX^OiP|BXSll8AsWl5$TkA zCZ$An{idHwnBU1`Qeu7EPqO<(PHJ=IY*_H2g~sE}TisME2oGA&uvFyczp8F2Fv(|l zO$X@VQRc8ivD2Gu5CTr>vjEt#@*B%A^8bpT_V%Md`5%^;sMoW9FCIpZ$r#pcWUNY= z(D{coF=0vvz_!MYYrDoB5jnVZKCgMfi)O80&b&2^JB4?H((6x)d;HJWo|V8pvDK%g z6&{WDzv1HPm#w={#-tzSJ-D=dSuch9t$6#Acl4bo@^-FIv6`Ln2-_GeyLL+l@!X=jLr{7%T}1;?;SBYXe5s%!WdJ>KzgAIghWl zR8;N~Zz;f|CgUcytY3N6k_7g^1=3T!oc_o ztqxIa%*~d8;>w5zO*o)gaHHYAWb?dj^#S0!Ak^7aiwf|pX2PvLo9qg7kf)oq6|=a2 z=1qj#$BM=Wp8?EW$I|@)@sj=`>F# zzD|B)Oq8?B|LVyUO)Q?&^-}aCKd4hL-kP1-F%8hAv)Wcl3X(0Y!Jt?C%H~br=a~IX z!P4^)mm1bV8y8%!cY_<}?XQ&~svu+aK2#8}!2SWF`&_im#D$&(779`8a?sQ)`+JI~ z;f!Kn*}-nAB&FzP*I>zqyiXmgz^?4^?TON~^aW1i=a*!13msY-uWxz*=g0ImijW}@ zhw48d2N#^L6QIg@duq;Ou|fLkN!Xu&PS%%gv*q^iwEx9-)>2A|={9ycuMj7E56WTDEFho%5dWqKy6t%Nu zMf2p<-Liv07510%fBG&-hTll`+ z$v?;&+g2j0&kAYzBfF4BYF;WInN-`fNS+vXsNt4e5&gCvlJ`Zp*Nu~ZU65GQs|cN! zRXtS!1x4Grimd@ttm_pYeI8o|Di?URnIuY?Yqa6H^2n5%dWLFbDbhhw#uhfWk5ZQA z?rPnw1TycpFqNjXQOzTiok@!tHz+s8!3{aeBhf4C-YIW{->t1zzF9E5CO|b{p1Wg( zYI2af?YnALz({L?D%$5C^I}zw=Ni*Q71_1I(5Mnm9iX48Qk9NqKclHA3~AG75_2nC zLo`8|mgdKrX{mdfsx`wB-#3P;zsLF4-%`Jhrq(Idcfz}CeblEzFIOK{_s#v_NKh{i z`pf2=JM#Z!d7}oLtK_QlJ$D&Ts6$*)L%Mq0)Ii-O)ti!*_B1`U;CS0W{j{8KEqirW zGXt9?y5>}Rlc$c7*x7JeH)qlHda3TO=!tdHwP(XiYmRAKLz}BvTFTsm_Sagk*|%+3 zn!Eo0Sb8-LKKIPSH7TA4jC%EV*J{HM^_rq|n5s{g0^KG$OJ(lyN9U>mIS z@;_#wY1jCiHJ#PYbFPx2>2qDGKcR`4D%WMG4;TO4R&P0<|GhQYlAklD`Mddg2B&GA znUu1;k!*UA@T6g^30>q@chEE-3aAwstqTv=xEn`@j&=NPu+7b~7wUh_mRpbLDgHec zKizJhZKg)uM9)6sXf5cvS#Q&vo4QOlTr;bf(UxJ)%WrP^Vmp<6x_PNBE@MDbo^^Xl zeB%JCPlCRFm!%>0be+KbGiq9`tC_M8s=i=4H2<`NFio91);`L}n4M(R8QT2C<~RCd zKJ_M&?u+L-!!@11>pp#swrc8W?MY43R#$5OvPWxkXo?DDublp+YHCL(Q$X6-F>}oJ{LdCd}n&SF;E4 zYKS^c8~+H%;ROo&^G5NHigKt`LXLP-X^D7}#5ZRca0c9xqycSEQRGIb0GTqk8<~NA z@e`q6@dumsRIKOJt)5uLA*@T*|&k|V-EWNNlqJP>tH8V9_<7Di@)_i*0aROlYD#cu}kL_T<9VW|Y% zyYfJJvNUAzjLIp}wXO21r;vB;Y4U2Q$1UFOD3| zrEqw~=*JSdAQD@gogxavD-y2*0fcX)3%F1=Vy-7tARp*A7=aW+H`ElrMsKbhR(c+r z-lHhrh>=>JR$8$owTnoV*kf}&#m718car)q&cp7~592$5Tg(<>B&U}R5iH7NE|oY| zGLK&&3&ArYe4{KVF0@W~|695i9(#Jrf=9`C-`zs>Usg#?Fc#KKwQ)B}IMz?~_iDB6k3i4i#SXP;-TRWBu%uy?7EViWg*Iypc^NY01 zga^X4nl}-ZfR6^9(*y3-^!NQ6CaLXf*X6BoWG_o7IArhXyi#<<_O7YCHxNOhlK zOT`Gs0p<9r@%E!gF4@m^N`zArt(RC5y3lf+G?lr+e5FXleq}n7sd73x_a=}82MoR8 zKgD#tY7PUOsmt@-2>sH!tpRf$w`}YkoWVZv9q+Z+S(X zN@1(y)n>vMNT!-eqJEV1jt8v%wEOnuRoj`~HcHV%HfkN48Nz*F-kIPb;F=1;&xu_O z59Xvw-sx$+E1*l-`>T)VM09Ri%FV0nn9?z*K;C9(XfA4R`DJ%2+0<;*cb6S%nywg9 zalK&+oL}{$K3-_0_|;xyHqmG`rB(Ti&5nzOd)edcQ5ny;7HfO_BZ0g5P52VgJ=457 zfs(xjo^Lm_TGzRHesz`%9;csN7S_60uCFL) z`3t&QrD!SRE?D;@Ob^_s%bELtrsBaidBdR{!2-)s98o+^SY+k(GJs3xYb z1FABNT@{;USlzq$TF$za%iDN)mzFQD`&#g=cd=DlJbg)vZbWHBk42^_r*y&6dzDQc z6@uBMOKmZXe5yyw|0)CN*2c31J6U7vc^N}En`%DB2lBZNINU`PZ+#nlN3y_7@-;wV zh7;?P7%R%2EI-KnTA}O?Wv5h4Y3|_ElQvd=ClHIGAOcp49Jw=q#wKmJhGRdj}c!buc|2zPT2u*Zp~ z@zY5ifJyMO=rlM$)SbCqY6iFoTzEW~6Mh3(29?hljYT6`UtjztwtMX*st29Ej81E1 zI6B`k#xQp@37Hz!eaBtaYtBGpJSUIqqparc;AJCw_+EUY=&GPbaEi50G(hA+$`xzG z+9I1|IPf!51$Ke0|-Nr*}K$6L(T)8coI< z+IW%vi}&4;%_R7*4To7D1b37!9KP@}Qp9~Bx+=o>T=7E|SMV12Mj9+qf-{OTfJstH zW~yW-v^in4l#WaZx52JxSMV9+As*rL7F$a^S;MS4DIC;0oJI@_UXLUZcAsn5Oxf$zn=8X5|1G6eJpl2JQDg)7qhTG@N2;{P(e^?8 z_2(E_kV&zKc^@7JSF>?=neZrgG7`f)#@mNHtbzp+)KPd!6oMsW+yMIFgX1?i_tZP# z(bA*D+29&@x9qCVVRVQ5#p-_Lhmns<_EsWjOgljGL#++d$d}RUwmvF?dF%7&?aeY-vS*hG*$*2l<`OLO6m5=11^(s=gJkjP)$&nZ7PEkM0x$<0kw_F8n zVHU{S1wYuM~CT4Ad?q-_*Ct7E-(P!P2#Kq3)z$Ff&=l zW(2eSb%QF0a9y=a3#@#1ZDjfh;XKWixQk+{MzXM3vPCm4*b8z~Z}X``TGXX00*aQ~ zZuI(q+32)3ai{K+1e4L7v^QeDoUT}n{)=P*_6*WG33S#^gfoz zIJBaR0~p8!K|GaybGl5hRNpVIQS?ZcwU8%C(>4VUm7dew_OT!|joWfZ;pW;m-Spzl z+R-iZOAR$aH9yOF)mi5KmB|jCh9FJ1>j^jVCtDY|n0m*$lRuJv)^eGi$~N}!ScDnIE{q1=>jwwDky^B$d^E^B&HZI>3m!MU>^f0&t#My- zcgeAax*C4jmU^CfZbegFs`^6}P&*6nAm`PL2ED0qjyJqkdW8Kf?JhIUwz~WpyTXd+ zE4T(rT-p=(?s6J?F+{NYYpU}%hFukA0HeUr0rOiS1_mjMAzIR|F*Q|Q6(NN z=c^x-jc+EK)|CHf`b#aUyxp(?cOf0Fuac~x?5Z2Y^QG;oSxbW$$E(xJ5!NgF?fjYC z88&iSJ6~eCzUZFQF`XRt6c8C#1@%c?41f86@GV_I@A3TRp484&h4o!EO@?Ae$DQi3 zQeAtf$+sM6U95UjS=2He>mkK8HAzA!(T#(+J7@{@QrbvHY3+^j$t+{dth~#dQx1Mw zF5l1AwP=a3+j2Z?7qHOuG^iVVWBBePfH&)h_2%S{>*aUODjc<>xoKeWke(xst0jNB zz8HJU-gVAb2`g^26WENZldbyzXI0kxm77An+7v`xL4V&+TDF@tvrd%f1lrZ`(+2Y% zIZ7ANg$!Fvm<+-_rYett;srTMKHJD;o_(=H3=ZL5Xt7dZR~~ zr00+XQqC1){msK*)G_1|@=lIvLQup!}*Nk4| zE1YfL97YmfwPpqJz$Vi}A2Lie04u9F*=4sEyK|umu=N(NwQ6XciNBlFZS@sgrvz#H zgzu?)WFg{_^c-m|5Xkt#zb(mPt)YJcVUD<>Pr8nmR^SbN6ojXzBT1sTxM;KkV1>QI zzJgnWctjcO;eA53AMIMPjpZPrJv#O=@{QIE&L3*H^B0~^YqMIAqQ6o&gTCs{UOq)yTfHbLEJQCKNuZ$8Jh)V1{LCq zou9&8vZ>hC6=NCK=>O|}&hlc!w(MpLn7mpgXDh4862kq=-lw_CpT~Jf2n7IdGTW9M5m9PTZFk{H4d5E7tHpcZL)FNHV6 zjdVA07_hbc53pGBB;N>*llrC|liDEpqD#&l;$>I|vI0ee0` zcCDrT68bk+GdRLmHB9Dt(R%YdcCc8i{=%^WbMVF7XTX1;A3sBKiq|680xqXr5qU~& z<@?1QkUBqG;_kf9&CUHDP65+M@!%1;hg1=-nqg=*n^kAx57th7U2*?QZ5rUqJH@k zfnX;miY>W;ZCPZH+VT1@GMq!y1^tD($yRymuut;Sy)dZ>8Pf?+rX%zwCbb*cRh>h3 zLr0tDGMb#WkP)oGPX5tpHjYh`usEMFCHDmn#a_@B3i{&}cC(nL`ESKN+#<3>FwxuVk=F28@oFbi(&1yVBp~&JLd#LYZmB!_C zNQSGNPF7hPmd_H&_5xqn|H+}yD*aqxwPO8ot$IvQN>y_W> zkDa8D(@dcX0WkI;6`ebZv($OR0pMk-63euLA*uy=vBE1Vzto-LEvo6U-zCdcQx<%e zZc};AJ_z4XE%fGLLseBvD$3UzO4V@@smjJBTgi>uehmAUYh-f% z!%8*Bn~t7Q`z+p8w!}(mJ5f<&xln(tYKSGycAK=_yitFHQemE^I8Xg<(jpt^-Nu(< zJ~PKy#2L>XW;jBza?Ts(ly2v((6{GS3vBwoQhh~w9X-|pv}unmSOZ?w`pvF~XKPH} zRZf=vKZ_@pZmY>{3n&*=>+7Q`{T#b&iB)&(FZD@et9_axjylhliulm(TVc^lMu(-B z-OeJLFH@#-=9>GJ=J6(&X6A+o1{>2;?utek*s(c)6B4pO4JvdsvkBM* z5G79=16mu)RyT~VTT{WW`(@o($BE@r9WF#b?)WIn_aDQDjkh~>mo>L4GMV-`B!~B97x?!H&pnOuBtiC+QZDR)|0c? z^Bs{T&D=oS)0|rVLaR08q;|C|h1{?80Te?y+7ct2N^5PZXa2)L8Xl9(EM~ob@dJ*y zwmkbKudy0R?h{`V7HR9e0$YwpiOHs6Lxqowg3EWGrSymRa zq|v;!VsOtf&B4l3U7v}wBx|Ra^aQ1#JzcPxHmjA#R4~Rg8%VyafsGBtM$V*qOLi+S zwU(AF7SvY<#CVGSwY>}Nm9Q-91Aj>0o0#4)h{-T@36=k=>|i@v@VO$tfh~Mf`NUo! zdP>3!3F5nyQA#i1Ds>6+Tyl;cBX)qN7%w<3(qpV{3IUzq&`XW*bzW?41oBZZH)Rbr zRTLHT3{M8=p-%}JxFv9%Y$xnKqeT7{?OJ@D7eQLo_L`qe?ydhW$fFLneG^vEIQkbN zF8!F|v>0J}B8`BKr5DFa>e>IXZ-7g;ItAG!exL<<5P_%eLJ zj9i%)aj0h+lg>Zcn#x)xsHqdP9}7#Y7OuO_(s#A4(+7$RfIA`<*Sd z`vBOojTs;rSo@SEm9$ugvG;(1IuFio@UT3H7b7i$J^5y z@ShSCIEEa|DFqjx=H!pkZ5TPG5{||*Lk-AvqBIb}6f(t(48mW&sp|!e4F|S((+|Sd z+H&R;__;;T5+bSEUiLm@m23}p7&;v4PP!W&jY6^vI&w--uICA~&D-CiLfRl?5@s2`0&Sq*kF`wyyY@c#!N}_I;pDK0mn; zjFGEiCQ3iZZ-y4adli|16=Uh|{IcCxpb<#<9_ZhB$zq4}72>MHtT<1@UPImJi@ zSF@T7H~1?#E`}mTJ$JSKT2(GTPG4MfRPa)FCF`YVu`WC5JiynUh*pDn+PL{|peoIt zz!`{16FMUjJD}d!zP@s|V@Km(qy+mfM>hG4J;7K{EwGtY=V;fhr|^M{66*-in|a5Q z!Jo+%njH)m?m*MU$|t;)rU^wfL8UP-t4ZW;uqIi=U-bK<(#4K;Lk8<&W`ts8 z+R1JDim;ZE^$#nrHSMv7lb}YxV4{p}2vVM=E~)#84W$RxUX#pa?5)|ti({2no9PMc zKaQ$O7w%SjVBvbc%=#|#qp;MnBJr0v!(143T~cEF;gm-i4ehfYz!&v%XUxR{w12n$ zDPPu+RZpr+Z98t;RrR7Z)i8`~ZoZ_ej+SzN^C{bE$BUtu=9*c#w5KRJ+8*%$=Vm%{LyObpl>uaGeo^{?Y-hrt+Yk z1$9p<&ULleqN_w5KlIh)5$$xvHOjKq%ji_v!j^O(jee==0GGie8Utw%dscmGMJ4A| z?YDxTyuWKwGIIre4rL-wbj-FYsuK9ea(4c7sh8=^tPRdb8Z;vSy{MnoI))0C^>pYbkQ%yPC`gpqogqjs_3w5}e4Wm0-O2gN9N+w&Hkx(3$-n#;r@EoA zU>Q$b$I19D0BR_S9wM_NHOdXxVhaj+1AerOnbioV7+=lsMVIT3c9e=wlx=P71==eT z9M2_!$}6TBU<`?)E|88U|HS3eyVQDTKD3@bmyg0)#$861lRxfskfR}-vZ54h5-%(( z4euwIl{6IpBw85NOuPqZAx~uA!Oa0b6eD2|&#y{vw7dPLXcB2`<9yLqveaP~-=JJI zo&)yK7ODqIy6LTWFsNmG0T)R*tV(_el*7Kv7zc-Oiz^QxUc8q@ZsspWWmtNq323&O0HK<13!Uh3mc({(#x5Ba3u61u>;{DW1}+B)o4!00qiHXKHvvI zB7D4Doo16YtvlE+#hdGcxPyT-+Y+7^@W?QPA0<&LX@Y!kI=WTJ1or^1MKURsH&EOH zjiC<(c0$K0ZcFaL#zL}m9Fm_|4P~P<6J>BKHacnn@(v#svIQ$AW(0i2FUg9%JY@y) z=2m}}8j7h~!Cnciu(@*1!DIAR?nf9`yyj1WpP*PwZ+YNebco#A4_wF)GRhNhA)3EJcsW8Uy~tOXLbKFXCSX)Kbfwi#eI8 zEDr8#tz&oL#rlDq3%FS!eoR}E>-peYdOwQVUBjT_C9N) z=7YR|{YkSEKFAH#$i!24geH!Y&p)9VMa6~F)pyDv5vJZy;3s~p)@0O3%GCu4JHbb4 z|H##lUOhCV1W8xD4LFSjsBSn{nW{S4w2gYn#Hxv>dl|o(PctM&yEcP)(C8vN!XE7; zzJzkh4D&^G&YkNZ`x3vuK8M<0K-E2R=C!AFNd7T#yl!Yl6mV4AkpO|E+S!pPG)!|a zBouzADG1n&-cUdD^1|<_Wlb)W*Y=4{s!pO!Xbz`$S&wP?=`Vp~{qcl(l7IA*BNL>{bhMChuuZ!v zU=>=YdFADauhaxKnkl}u>#AL;)|!2$jkGt_7d2swY{zqACv%NsAT)$Y^qXbP$2o^d7hTr;K)qenHl;QJZN8qQ0jS%d0p1Z&xXx;)lE&coW_FzfnwD)7s)Frpp2ZQ3`l4Z2~B3g95`^jp2$@d>(_^&d!B z*Hn8g`FTf^F@Rdy{y)_o+OgIqJdrV{#SJ{hY--vdSjg_z7{zSn5cNk%FS)RPL=fW=hu(V zT_<={o0L`}T3uZgcLp%n*%7WFWEIbgg>W+zun=i5O1)gLoqC`tPP)BpP4yLsToG=j zz{4s}Y97H`NR_fOq>TI?I)VJA8b$8ta=H(@0Lx=sq=@l;EK6A>zKxTc_lMx}=A;+M zTm`=IY}s|uf`~MEH$a{HMgfCc{mYeQu$RYrRRX%S@eDYr>R~kxJVlN-50I)U%QV$c z7;T7bB=j$x3#s5WjFZA!2#XcMo`8C@*HOaJr`&mE!PrXP$-LV*Pk^R}5dos8_(I}e zU~0r7*->!Z+&A*I&@6wJVhKX?IHK&vRy7s@X4?N8KP74OBvThSl7VXGf)|-C^FWpfEm;7$Nx`v4D6h zJvjG`>>0e&ACSL8dpvF^{@`mG?ubXQ;vFXOU3R=_Hn5SCpgu0qaFdB5uz;5-y(#tP z7YLK2{|Tt9I_RQMMm_;=6m^weKx)P3^QuumGBNEJmI;FKf%t6c(}-Dw2P~fZS{8s# z_Q&MO*bR>t3O=!-p;Bn&)i_3pgnW{*Pn<0XQkMb2!hZNW$w=WfsY3Exv_bF@JSKLq z(xhtuglvJDB<#`-SO+rm=m;o<)9#`as5O2PRtR5+aK|f9*SRkU0yFt5WowAx9{I=Lq>QMQPm;E`~m~8F&QdVo2c2 zuwvyGfgEeVmI~$A6);sq;FI{*#WI}4Oa-*~I?{59o#AKJ-B)U}dK zflbZeJrnk-BN;5w5%rX+E8^#>uO(9?6IHizr-GTPBdJ%V7S)D0=f1D%3hzOpRIPKD zpeI$W{yZF1wR%pJ%~G}24rRF-URtiOBMequC#S^Vuc+X{`jhBHUZb7`oZxTK58!bH zS9MG1Z$*FU5~}FpdD>^io17=Fnw-6o4chQj6Zl2*PuyfEOXJiJ!-q8qbG0Z{{jYx| z?ym0goI<=+r`Hh7NUPM6!lGL4=*F>4mPGkw&Ia>3q=tLdJON1I{V>V7Zv?)^Cv;3$ zVa%!gBx*M7DNYyPGR(}ONM`DrQWt`Re!!yZ(z`lIcq~lP-keKAM`)w`GqLNMcF$?V zcJ-I)=L}W#GxKid#_ADTvoi~yE6?GKu~#BvxY0I;_%x4WJ<5gnZI++(!Gg<{d6ibt zBr~_@xtM0^$r%dt884>10w){$EozY(3_;nFI*ET2)}gg{x)3T&S9Kx% zUPFvIkU6Q|Mf(>kr}nq(EZbP~3a;byRsSQ-;l6OZ<^1Bi*}u{n1ti;#N_XK(>+d3? z=!fNZ_C}!G{57Rha@h28QGqnlcx~Zz=%Qio+-Zni-|Fv=IW>`MGMx|s(P|M*(BfuYK@xo51)ii0&d!*E`-G3q$qR;YlC#Gv3Inrp4+P|B;8J=x5 z>K{x>t1oey)zy3$YGU7LVv0(+V;aYBM)5N1cTzX;o9oId(gZJRz7-x4rB&Bv{SfbT zOilhz5@M^1Jp>-I{9Z_gm}d5zW5`V7shLg(vBA&N6Mv=CII~B*+iF}v-_bQ*EoXe{ zXd^ON^V(gY$!t+;yYMb&b<0F{GxueajT*xbZ}?e$Pf%CSD#&?rn0TYP%R5f=qv5D?R50uSgoUW{s?PBSQPRL_9nc02vBfP*AvSN|IJNAU) zfGBife`OXxoAa0Q4Y=L!jH(0nbyui!(UsNvk>IMW=EcY^@Ixd4u_#?n3#!`Y5X3bB|wv1qYu0dGTgyX=^NoRTKb5zUO&%b(c!EHu@+sy zKFOVpC31(*`miUw!pb1r!e3v65VM7ovT@?Hs5xbxi~W~CHtDJ5%%HSg<0@v?lbW`M9llh`3IdO z7)m41!@`*r|6!0Qwc{Vv1xs%WFnB0r7Z!@{!V#hnPCNWk>^%7+OMqb& z2T+ovf6;Wz6&#v954$1tO!*79LwT`vL;~C#_E9z(c{gXO>=VYEInwFMeDD5L@zzP> z{sk0?{EY7<6GZ3K4@MqGRf z{6{SW>ZK&2Tv95%3B`eKP^ripd<5NMe~_+%snn$q1iyE_vI&T(a5FL)otbqH`G8(Y zK8l{ls$)gi4!k7nDZY`IJI94MAoH2&CVM2G?J-a>MzP5DRd@ioWB4L!M%`2&!~&Fx zKLz5@Rnp6n@#rVvUdaP2kzEUJ#@eWq?@@_MO5zOZl#M98^WRj^3DIGKttrunYbMS$7=p7D3MOmY|Y#Zd1<6c7vLaGM9eNESu-GvhsxEbgRfzC)O9n56BX(b z4=>qkwTtB*x7kU{c+9IY=PI7^&8Ac6V}Zt$D7i1hjR*L*L}Ftk^KUWFu#4Ob2n}In zB@#@(GygMa(T8W2Nqcnrll0JbU8=JyenWdPEDD{bEehU+88w&veiQ%GWO_Jltm-3{ z49+8mpRS1e)J|5Ac+YI@D3$-*dQ(CZ{9|?JQ-qH!&TLQg$lOQv6~8tQE`1C9GUewt zf|HCrnFFMuhF3`uP>~@lCdO%$)`pEj&guRRZpY%afBfF#SGDs!0%f>{XKT_m(ECT$hVt$Ry#K%?ba{yfQ6^Sg{S z;4O3i#MjV3)A;D0aHP>~!A+#v;1!HwL-lTc5Akl@c#pZVMD0Mc7yD>aoHm?uwQ;CC zi~F?x3R1!QShogX@PE|S@YsUiH5x{hXi&A36f2(UAeT-C7TDAB&Pyt7K^Zxq)jB+} zL%P>|KYBa#)3kQMVx-sz2Irzz40HX?<9yvqk5HMDpKn^nE@*wA*~ekG)X2_q@#aPF zO`f&!i})eGsbMSkxuBySr9Tv|sEepNDcWE2wPdyULG`9Qo@Athp8g43WFMB8B9&Q} zMDwAemf!^p#M`tx*cV-9jPcupN9y-`gcJ955vFL?tIi5d3j0s{PgxH4e{G#`G0(d- zTukE6Yre!~3nH7ibfz%9;b&EWh*fVXi4;5P#^((L_SZC|w@AKK4@meRjk1%YJs^j5 z;{sp!qxo0R4-__~`mMxA8frXZhz+_c=J)bZW$ijQc~M1>B1zs|dDvNlJ|~q*m`=~% z8$MN`qH39minDYN@-U@4<80|JCBic1M=H;9axw^&8*fe`pkfJTMhB`kh!%z(Q1=H| zL9S{RxZ5{deGLwD<7hI_jpl9wuQF=S5En?VU^`-|PcI42Wwu9KF^ z?(zaksq$RDIq$Rlh~P^`vSPMKmYAim0h6PrD_%-YhaOgzNLzwJmA7DnuUy4PtKHVC z2ja_3Pf!E1N;?_-!D7o3F$o()*w{0U8qnaC+&W%8eu1})QB5TC50ZGqLBX@qd9pas z0e|Z?NCNV<36r33SKgAenbLdfp4Gs>9Q@SC~eCw5muw8DaRU)Frgdy(S zGR;P032&k75julE6&Zmp=l=o%F>k>mUJ|xMc#@Hddx&<1EchhRPqqh|7Ck~94l_glmY+b51!XEkSfcN8r8j=Z?V0k4Ol=wj{Vn8Z65t%s zbeSB!A-VvsLug`?_%iYmh~s`lCBRSmZ}hulU)3AT44O+$;M1iQc`|$x6p{XtNP=f3 zjwh}me$oA9LNqM&xZG*K3@VbJ#k+jBD_V%JZoid0d6ltQN=O{)YtSfgyzGBa57-K) z!1JV@Vkzu&A8^}|A}E5s9(fL(ty+evVQz^Hb3sPtrDA)KzVxFw36&&#Ck9{(qW&W` z;=@Bv$oTlbK_qzu@xk|~Vw!xY`&eZ^#Vq4wFb2M&t^)5PzC@2yjo{Et$OE|`9s%t~ z1Go#}GSp1Zhrgh&s`8LFtgs{wjm3`U4MShzQR($qJH9{RCZ6F$=|3k15f4NECN9cM zK@8b;d4lgb`C7#|cQ?g4#S4R3GMreTx-RJ>N(e8IO%M=WI)UgBbvpf}$2sR9I`N+N z8Xh6@sQlo(rCC_~0ujopbN8U$vh(TL=mYtrgymS5oE>!(r^ye8ZX%);zCq=(P(_>X z1$m-!ihF>9tn4xL6aS}dR22g2l~%l0LQ`s_FThF8e}FG|QYqwAN(m*Mrh4)Vmjne8CF)S$BeELx1ov3^HPuJ`MA1A` zxRNURU_6Db5pOkCfNufV@LCuw2{S0zLdkc-C~7BoP~TIrRa&hNDcTFsbSHC`!BIL& zS`ji;`y>7m@=4ns<$~VQhKDY|ZfmXvjU)PLguZ>UBuzi}RC$}aNoNphZ7jugQKq#I z^A%6BjsgMk0}E5I9awEX!ul!!%)V5AFv?`DNS2N@z9}k}zB1~4iRX@Jcu+W<|mdZZS>BP^5S{zdwk2S<|8e#W=+ zd$Fm8OS3oQ+4{}CjYNme>Q0e;($X|<_&=Ht%aa7xo9d7b;g&|4Gdr+1EZ~QWsr6%- zIv}L>9r=)CSk0NTcam4ty@iv(YYr^iOM1$ll=2NaVjB|I4Ii@XiJXj_GPCD%(SJ;1 zXIpVM!**XC!PA$xvt@_0Uo{$jbGw)9f&grNg18B@Tb2VnQ9v`5w?o{oX$o_I_*%nh zauTqq9xo##&2?S{t3hMUiL8B6Wi>OU8&cXo#HGV}n;_yW(rCFoKM>tvPMb}|-WhlL zN{DcSm%B*j(Al+H)Z#L`B2aCt@I|}TU6lvH{_5?dazT;$9QgyQUHzPDrX14@p!=0y z*LX6n6<*RLu;cmZ(B0L6SYH3TlU7yh=wO!Xl~GGo+LT)eYwJ^0n$gY(e1$ z^#ab1Y_qd((vj++whQv&#Ok#o*9eRHB=A>=(?B8l6PT-UhrGPMYDy5At4-5@ZPvOf zV5*nAMX`!TMu#hI(Yqy5t^)_e1&Ged>G@YJVu_$ z{D$<&tJz~De)0<(KfYBlmK(=>tjOV2QhXIPd}Vo-;)-B>Aw}sbe46dF`HE+yd{!<8 zjB(}4+mg=_7S%W@5z?&6gC_+ds>R3^pJ3H@Y`yChbveBEAL(D&1LYOcmnlc&yP$`0(Te}0=q!WVNV+Z@ z#y3o6=9rn8nNbFrEy=PNhe<41X2*#`4l^@zvSH@zCJu8p8%{obfBMftbdx?}ABU~xD&b`5L zkoxl47~N70e`3Q(=`eqJb%iWW5MQ=Xwp8${;Jkda$xVDq&JY!*ESFyso5gtm3F4>W za$uL_Xut|EP`cG~F?djR*qIIG$)DP|LC--m#98!Oa8|ZeTqN`nMu-mzd)oF%oP~cF zff9|VrGBO4t;zK?PFg5VF3XV~6dx>5%jQd>vX;meNsgt6<+G&uasQL+q|d`!foU>T zzz{GXkMvv#h5{3u0q}3&nN2uk1h2q@!V}WTvH(%IG)8btv{5Q-OBdTq*SDMz8>Peb zvEn~6yQ&qEHd%G)XUP-UP(g^aQua2hNP1TulM*Vcl=sBmll>$AH#}3$1}XxKz(nAJ z=MrEKnD2~&Qt+uw1{4oru({wMm?YgM3;}Zlslv@*8T+cp!_?Uoin}t?HOzkV-3VgV-M&yrcOAd=p;=%c|#WmPJ znW^I6m?1e;vIL8Xoh6M!KZI?OzCworoMmg!63==$iB5190}^DnjQ|uO&EO(lv1&*X z#(Scg$-BzuDY4cD!AQlwO&jOH1c-dR?$ncFyo!Low%6v zO%g_=#axm+!HBccfYP%yp>m|J;BT4 zz1DnXz2M`TfF_x#g2-ye2-d33&=6sa%DniM@PV>1FIz+?wq|sS{iq*FE5(0Pu`xcBxk>&9tF_U9qtPxv!;LgXi+#Cw+7q}}coIV3ow&&scVU7JD1yO7)RKQS~_bPEuDP4N_=o30=i65i4#q+b^qG@gk+#F^@e(QhTwl(#~6OYSNb``1gi zQ;qXqnCOmP&ROza{G-hhpcmUIU&v|eeI$zDPU|_zxy9Ysy@tu*Wp^nW$MRlvR@LCcHUhWyuSElbtA>NQbA(Dgnl2P2>LL=Ci^pq*qZf&N2|TZ6D$Deg4mVB0BWmB%2XXIuI?V>RZ@5#8;q#j6 za@nLR~Gx0e2tuIy4Ki*TyOr59*Ep$m{lA`hMDoj{>TTGG}nlZYTc857M;;H zF7YHfm(vstqVu?SLPw!y{8~RIY9Sosu?4jkopQX6dP!DTJx8NtMtKb!-|Q!HgKJvc zI4@z4Var?&cQefzP3$4ojGC|Tp4Q0~YWOUBa`71WLEF4s7W{?dnZ5~`&do~PjyUt+ zXdx2JKO8z1Nfyrb6C>p!)Z+vqkW6#@fEcBFtfwHGoJU2Mp|jkhxe3q<-um=*crt%U;u6?Lur!(p7YR3n zj)6hZ89xMGCI01c1HLLPbee>Wk{vg3to(rl*>>P~+Z7=QyyZwZ@t_&khB*R^;ht#N z4c78lH4G5v&#IUV4)G5c^?+vu!rWQl2cdsD6S5Y5O(dZ#(aGovh!igj`2!u3z}e)f&HR2ML8z&uy4){&{}*jJq#?6bw8kJr#X z`9&umcouNTx*BGI`=l3Tu=tH&iR_3ZpsiItN+M$f%3~x)>p#oI(*IR&m9LeSme

-gz>mVfm<|hDe0Wy#0P$W3bDHK`@ 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

q@-&l5w__&2pIxQ!=aiAnH?L+;fVq_ms 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

N5VtU0!d+%4Im`8~B8d1=;k@+V)Lb6OEE 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

vKXHRcG-qB?_r{crIM{eX%v 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 z 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

rh5raGOLk1xpR_?0`&jb!6>X22G4*0!M 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

80{^nmH3D0*-o`jibr_1uu`|PdBRSd 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

|5Neyp?>9pWvp#_X$qqm0ffq(jyHQ&Q=tnjdkU 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%0D7+C*B^uqseacCg%+xPt*>CZqBASQNro}1D5 z5gMf3SYHkPtD9Cm4?Lq&Rh$5K>W-8=02k{=7QFFnp)%(usMgQT90hXq4^xMM1Venn zeX!Wzh?xgQ7@kF>fD?_CA;-W`#vOtA;8?)hX9GADaLgD31^{1(Gd+**X+*$6!{xdM zV61U$bqwfZ%&uU7V~wm5*!0_IDd;l2H?GWCV|r#hmeFr|=t)-|G~EU!B&;%B2GU~$ zro(_RVy0;)uqYu+U>YkM;S>bl6llBgS;oWFV|G?J(VJoM2jNdQzt_wVPg5 z9WkM%x8=V~I@6mHUz5!AvLM+cG(F9!F$qkMG6bIc_dLq8&~!V2`~SBwX(q1e{+v%H zhUwps6(+jrRbZ}(XL|2*)}%Lmo>6C-Yx+ruHhIAhu!~J2;A3?|fS>S&Dm?H7ZYl2u zKEwRteZVKUFyG^%!LzbI1D~M}88bYp@`;of(+FsBLYQeBB##+lnhcfBIc=H_`Gwd` ze&DCTWK$$~#^;798(c9%X=(rs_}zfTydE0{0A{eZ6YwNFF)sm?*{3`L(4hB<8vrBP zov#8=v^aYq;6}ct9|IO3Ymy%W>kw@Gzrb!}VDu^AD7<#gBH%Jy9l`+~LSF;Jfe+Af zpNFQw5N5_)(`4`~KF9dY*4VJq_|+Cz8({oueU1S|9{B`CGaiNHwF$^8k!_RQw8nfx4pG00TN~4j<@5j3EWUYWPdw4B!t~KTl!@6N+!tv> z(;vAfGDal->8fGPiWfOuoZ_grj$=Gqc)k6n;Qs85_F!>?-)CEv^wq3bYpG(H$K)cY zX@vUrV~zh{PqzKQFRn{$OC-{&?5)b?!1Dbq2T1pdueMC0^yJ@mvuW7uTdrM<(Dci$ zA*>O}JDpYRuklhx7jIznVEYw;Pq^AXUQ|E(mMvGZ+<%UhDW5WnZ2^_brgfVaYtjf4 z+Uf~6vF_G+O>phcmV2bIDqKq>`Am7I8>RA!x4ItC2j=g0$_{%0EzuldbFI8c#mo%W9@2aH#EOtA0FqQd>oZhSt+3Oq#2TtE=f6ntxUSY4` zAB~sWRteE)Z|i3do2{%AHnJ7oeI1EQu8pCVb(jePGk22hx>uB+U1d!b^U`IOJ(79Jpe03mA)amCBcB)@WX@3F!w;i> zDK`bzqZ#U%{^yYGnswgENU*Nfs|W5ejKJ-*_wf1~a_uwuCu;t-EfrkC#M?rJ*UPNd zt)fdse_J!eNAuoWj!F8n{Va9T{&bA_j_hzU$IO*qjK`t>DV|2pKuxMa;rr21>abue z(xqYe-$s12e|p!$tMzld4#ClemAEP!TjX1R*ZM^~s)p^!tG%zhY4w&|EK9U(m#!{S zTFPYdymjXL^4zR@X05_2eX4n);=klnbe-~Ad;*%SIv70=Ij7zdz7-*9)&^tY|Fqlv zU%+j;i{28?_6K>rg3cS$8y{FPQd7Ok@>Et)GsuF-Cs*n$e)5Z@*UbkNP?3+Bp$y8y znMWv(Wp$w&Rr0j+XuUcx$s?4hZ^REkdNp&SZXM`}#rL9|nSB5weiugS~G zMgG$qPD7B@+LEN52wr<3?g%naM~~V8_v(IzJK$*j;@}$SA45UF5UAJi#(NdiY+N!u z8wv($vA@i9n#j8O=AoLc)lukfZFI#JRG{rEor(tP1{5-po4WekwH}{(RpuL{TK79G z85yU?CrRK_`aj}au)*LR<$|XhT;WRS597e#N{DY%28@FejL*IQ@>tT8=`8Rc;40RE zw(0qG5onqIK-Cd+h+$ks1#-YpU-E}Xg>x5k=iya!zJz6SnbnljxDwwtinsmL>+yS5)$ z4eY5RAS~c|`C}v$cwf>0zcLLj*Z}V^P00BU8%;i$7`Vg~k-8k7VoFJT>9NU*;=G~d zrpBlgsLqqI%!US;tig%kVbh`jFR;h7#rruZHXWM29V|CpY*>L{AaiXcG7&;Bui>kZ zsoV%JhIGa6U@jyssDzUsLQWq%49d&+3SEQdq&7h7p$UnrAtU%D_5oA`-j5mv4Fpez z&j9y;yMlc|6SyQ`GFS(iy}yAGAaD8&a0FP{5Dy*~ zHQ3*0l4%39Wk!$*fwHk)J@@Kw)^6#3jXhMA+4T#zrhIqTID)Iht24NXS3v2=A?4@v zwi75p87JFBv>7R%S|Nsa;^>xERzmCmcMpdWamLllTM-I4JA?y*{5{sN%6F@sB^x_4 z%9gD-IJLtXt5!8!>lQceuT^%r@wTdwo$HCU<&KV1&C`qDw!b3%Q;^j@k5yXbBaI6yXf@les+T8M}nJlYP zxyNI2Ni`)63%iC8*4Jir#x&tE*E*U>L(7}n?c~kHC))m?))mZZeL{PaLu;MP=*(E! zlEW%Z`Nu8bj7l8n>g3*x9qHW2KNRu7u}A31^4a^vSAq<-e%V03Y1Tc8ikb5*TUDDq zCf6SAv<7y^(&iPlquc)>Ct}vLPof+tk8P`<)fO*pHPf%%tj{TMe_~zFaJVBl z!j#i425)r2cV{<$Z|oS)_KlGP?PtUdp*L;UqRptzG?&No5G&;Tu9;K{vK(wYgx>? zThrkF$?>lI>Lzd-%UWF9d9LCyt`Yp5`5Grqa5ejeW3}*GMzZ~*I6S4xULc_*+_AMu zm&FdZUX#6u9AllXNDKX9!6|!!E|`1Oqx?G1Ynp|#ve2oz;%Rpfy#7Z$$xY!+uNmb! z&rhh_;>r?Kl$AJF3rR(%oFhcc{7eT;OwV3v-z%YKjJ8jbic%Cdq0E$U(t21vH|D!_ ziegvfPzy6qb=lSQ3$`rz?vz~XDn(yHpLL|t9dpydP{9#z z%$wEv&}Zlu4G?q%#c138wj$efyJqQ;S^5`VnXt)m(fcX z*w@LUibCuG^5=QWZC#4_*;8#plx3dt3bkr*$^gqt)n5s0OOqOm*=RnXDT_GoX?mI& zdK1NI{|mZ~?ABfNJA_2)56zknZ#4Yj#fM{!TkDb?f$|?!FYH?tJ1gefGnMSp4BJuF z*uvAc64mm&3hPmIMAl(zo_c$FmgS%(FL{q8UvoJj*!-847o#!H(T#}Mh#GWTLr)_o zJS&#}cr57&ewW}~h9k4~z{N&`*Iei}kXU=(o~=%)vf8eyk5{DF_?o(s+t#0&i-l6F zLz|oX(KkOs~eW~&f=#NCdSnKri!fBSL`u^NL zi(3C9E5I_;ke;^M++#2%N14M7`{UQ6dyIo)CZS12OavF{GA<9@2){Od54r`j0gT^! z=m)T1)_>3<;EC5usL&Ks^VE9GD8g*8N{!3PdDiL1izTxx{l*^!do4J?FSpqI2FT7l zXGVagG=h01piBDKV{Z1u3p`HQj_A9{1>kB#9+F}j7}^cbH$?_rhP_O+e&3)iCaL!r zsNUqBJ{A0FT2rI8MuFv+Y|CwsT0Y3)07b>SE%BhDfMC7?Dso<$WuP=uYaRoN(ng`{ zL0-~gv;w5Y&q1Dnxaif060DDy3O@#GLsf7!SRHg2x&&7GeTM{Kfp-8j9!!{?1MUWW zYDQbu!5=EGTJZ3zviX(~@RQrKAzKx{)Crj`lE|sP3j&r53WwCN1nk! zanBGZ^eI||_;}iwAH%)SqR?u1G$aYy0Chsyo)laJG}1dBJPV$i&IJ`B-s zns1vWWpQSwnOOAFoNUg???LaIC*{PV4)jdM2{aA`QrXBYG%jf*Vn-gttwLguw&-~H zB$7U7D_jXb4h@1%!p@);r~!`ldj-CR&Uxp7Es$V(2N(m5thqWLSAPWq%_Cs{E)VV{ z;5HW@nA`o@tXJ&T!QIb>Fb`GaSC5>x;$%v1mx1D5_N6l+p#Lp24tjlozHBuvTj7YZ~Tu5saAeJ=>aM%Z_xPZhlxS z>bghnEEv}LjGB?t+VPBbCv$B3BZe*2+IF9nm-xN)4rf>#x#cqVX4D$@9{#cLYc9L+ zWbkVzM*J|~m1Bx*qR&~|ZAJ5pUh8tzfvK66cCAlMQBQO;r}9&G6X|!^vM!X;U0l?; zk`|JGr(+*|YmTt}2s1I`L)$6V{#1GEMGh|UP0K&rA8|zYKm5&68(qhRs_@6oCF1&! zVGf?OC~%xTU5@ejVw<3p&Ny!QSG~>?K0aC=ycQZm6nbRjD2M(9phLtiXOHXvX%Lk zHa_P_PCzTl8=JAKWiFqPTHvv;cO;&7{VkdnSLwVgMx!=6u1S7{f3}~H!6Bixor4{>}DYF)Jk6`budEthT&S zeeuPbzi3)#7Mq9YJkmGnuYX+~+dh=5t9akW=PfVY++B3ud~| z;rh$d97v^ce3h}~toUt3eoL9;YUyzILFul-qwWZqJJ0BX<=E_K=OItS^G(MO#rYJQ zBSYDpm~01CO|ds@{p#>2iuIBDcla603(c*NAoDBj!9b3AjBbN(x92*gZ{}vC$1s0d zKm5knQx)#MCl4uq?WW3~m27d{QmiTzxhj;nyin&l<@c=Hj!&wN^fpJPIwU36Zdd=E z@X_{Fi?bA^ml#NAr}n`XI@02jf82h;9g)V z=AwIqs;a!()uMVY?_`q&^3lzaTmfxdqte4~Bl2lx^&&TAH21ax4SE{|$g z9_2i%Jyrb4Nz^42>~q}Fb>|u#Rk~MM8TO6(th6uoA$oW6E*o8cCqZdFVu*^3wE7yL z$SW3#;bypJ6yBH@{0oJR>jM?YZeXtJ1E*3O9N{s)@oo)Zyn4S5|9%2-yt+S!VMae?zGvld*cx#>UbIb?Jb|5HnrTII6 z3$HSJ0X@Ob(MaHOpcvtrM*7}>kDC%^MZqa1f|nNBYyv7ZjwdF+vKoielvy0+m}A1_ z|Fj=4iE~cbb4_q&r)|BdBMomGXX;B1wCYUjQ1 z;Pc1>(}zGN5@7n~dl!aHUuTs-?@b@QJTr2px0MqeB4}UfE5~@~V9`g=BL_@q0@54+RBly z8TYJzBgay=Ta%EMB)erJ(hyIxgdo#nQqA+=87j7m(SnT*w!4>xf9{L~$ zM%(wKu8e}8_qBmVUDi3Y5uiPH4aK)H641}YZYE&7-Q3PPs@hC;bEb;f)K0F7GmgH9 zcc0S2+#+bk9c5n-{#U(=`&+!ageORsDzcx8V0mM5sN{wc8x<{2SBqxP@MNAl`o`Bx zWroa)t1D+^bW}9x*)>jl<4O(-ZNwkq>Wrlx)zhU4Zhp#NEqX>7DmcepObZbWri3zT z#6>s(%OUBjW^*peCY8+QhsqaaZx*_g=}BKDgH#`*M$62e^xY8Uc-^uk$(T>Pk-e;% z4Bqqh);f@X%CW9tzhDEhtMRSSVc1ESE@CMcHx-CWMIw?~lFE*zY?LOFkI-JpvT&1` zxpI897kiDuT6~x1t2~~q6s%BtC!G>!YTS{prH^!zgFY%c^{W=+D_)4c&z)OUBwpQi zwq~)U#{Q}9t>gzX4x21pW|)Oj$%>Q{3A<$Pgs+=^$=9&L{yK^}Tp5H(32XJB@!v^CPKGrt_?{D$e1)NHRMl<8 zQ+rlzv9cbf)vr_@(Ce_DRdW=2e7S0YkU?Cdp2o@`jZ`lpy`U&HQH?dUZ<=FO0%oU{ zUtGb-)6L5I#UHM_nWPpz({GO4FL_|-3VJ1fVuTlNDjlQE>;6%`P%CWB$AoC-*^Jd| zwa1|Kb+fb|b!Qu3U6A}{<8xi3;2fb@*Tvk{bVYZIBp~DU5!n0G|MW=JXvT8=!=fK- zj-fK^Ag|1DFsag`jO9kIlPHX5f?ms40^|j^i!lJDJF4_G(9oi*umh#my;a^oCiu2y z84#`WuAc??%9F6Yz+^!J-XEC43?yy_yqlkr%7HmpzQ<}w$E>Cw02+(dvlaq|EQC8B zSe_Il_!~GE*&`We8W8kW&Nn447*)6jVRTVSm`IR&TlpCHf#qA}W_SshQe6mRwIWa6 zcBHJmfdU=i&u{z*L5vPUA5_xJXs(4OH+-Z_2mi(7(cXgxiwMj|;G(P|&JR$N_+3!w zX^29^JHYUu_wr~*j{nk25PKCg7$z-XTV5!y|sabD6A$?b0 zW-jCp!G1wcGKS+fq4iCVn&ilX283LN$Sd#DGLTV4|6>-w%d$pslyE}gW&RE5uSkiQ z1r-N&MnKaNgr*aN;sx`Y%#VEB@XU=0UHxm-={8+RpvP$$Xf(!aB+XmP4 z7Ug{ENo!w}f4S?K<9*TQ&P8ZZDcsRyoL^DY9-(@J`PDW;l33H<@{G%Ff&2K5w1nL}aW8Fqh9BEn^_3dcI||7JIkV z<)trNWZzIYe!)y+Nb#e2a}BhzZN1|?NhMv6MLfN_qU#$6SC`N!p*peQ?av5(_@uU` zI-rT(aPBK_A`6NzeGq+GO99=*Qz*zeWa%$}KswC(E)>>*9=NS1oI9Gda2* z)25}I$0pewG#<_eZlnJo4A8roDDjR$#U9suOjyiWK|aD7%}b|VCavb*p?|@C7tUoR zR}B{PIh{r4q!|9_ETKF}xH9pAGEl;b9HO2kFAeh6j#XtYq7gH&PkTC=e&H6h?kDvV zFt$4sdD9^1Kbl|jcKral$77U#VX~<+1lQP8Xiu1{IL8%;)j@+aRcib~H+~>d-hGO2v*}&ykfv=Ui7k$V zkl#X$loF~~C#Maey^`zb2N?%X7W{?1Ng3Ha0(XL1*HTH?Nn32SH7%gO0QZvA z%w+8oN;wOaeW&@eZ}2}b25~bP*O~wFHa54juX;4oq1-FN{7`wVkv41kpy658#u%1{R5cacwg8@xjxL#T! zX&x^|W~89}8a_nR3rq|tLnYkPoWwGS2Q}>Acy@=%=e)hr>xB;m@8qn^4dNu_sKgYh zTXj03P5z%|b>JBlP1iaU7rcS+crF~SxzO}f_)<22 z^i*_>Kc4bIe4a6yK3Hj_PuHbel(hFDdUn=2Dov2oIH*uoml!g$Y zk=JQo1s+j8)Vt>qs+WrLI!@P~7rk?()_)iGnHMzrOKN}*_zdYlO+FDL-6AzK(_~Cu zFBy`D(EF$>~&!NkjHn7sQxWq=>+Qv6w}dH4UnP*sA}A%9HL%}zoyhnUJ`#RzwjoJ;#DSk z2t}Zt&@_&=O}(LhD|3RTx^g(%uK86M!1L1X$($hAr$ZBti*%bHS!}+NZQ76DZwA zwC+6J-?*}(lzGs2w!qHO8-Hgk<~IOI2?h}kP)0;Z^}xQs!-~zO;XT2nJ#cvI9~Bdz zul9V54LS-Rsr~_V7&7ZvP@{4~!*R$Q<4AGBXrULpe9kwC8~PD=Q85*&?;2ltwyns$s>IluYfUK+X~_kTRGx5G=rGkd z7ejuz_8+HBSk^#wY+X$YIWHwO@Ylc_^5^ilPEo;wE{l6`@x0D5OHWx@$0TsJXN}>hcDpA{a8{mE zJHF+RV1NA*_a0VoW1(vk8ODEcuEO;<9dsOdK;8u2mmh#nbX+P3=#{#dMVIGJGk+>Yx(}GF6?3{I+Uc0Xo#C=gHMQ-p z1&MV}+qN@TVdu6g$^XM+TT*dVO>Xf(z<5wF+{&GE zSiH@AKJcSlh$i>)>L-hPSeX?>X)$Mh0ZTrDKPjU?abCC~{;8@@BAnwX{*_k+ z?9ky4+t5cSv)ZjymedH%NuZekwvfV)UU0QVUfI#y;9%@lF=jBTC+4e`7wSe&8-= zw-N60vE1@Hi*P32ul%9top55oP{}p1Px=?xacN=vR>g67>zowT73HUZm71p-LC<0W zoxHEjzbTA5%)YewC9MvbMqWo>X0T8xjQ7gd^s%fwaW3O8_5zNIrQ;5!&gG=>)C4zg zDE~)Y7XO}bLHR1-DRF)NCGk;daQX%5S^0$cZjW~CJtsspQJo*qr^(l9yKmsC7=^7o z!W<^q_PJ>YO9$(kue0asKa-bpb}EHbCijx)3_YCpl{1#{O%O#*XPpxY3F(|IqCaat z^Oj2j%DIBo(#83GqJDWy`dZ07#m#uFe41+IoN1mmBuBtP^(LLMdpfp=bH8ObE|GiO zR!8vXZG|2-jpQ%T3!48GSQM|x{X&_DKJkrOL@q z5S&t%rq_w1HQe|N=^E{-@K1_3-Oqqt)lmbbb8)Rm*66OOcgrqVeq&dA=3kfN_Q_A` zvI&P2W%4Uc`xU2!4WvJn^{m~L0F*QJN9jx%g5I zMxH<c+qLs?JLpe!gKL(ul63O2Le65{i@Ys-U%e>pop6u- zSKN1r!mu>_s=Ult7cgHH1&r$$fcb9lbsnurH$skkBbv5XIL>%7?wa^H zFf071>=B>|SfqRn{L`LSsRf@qM^zmLkE3U5$ABvUYkeIEXi~5pV4d_n?gSXfR}+Vs zJ~KWwV@%gbZ1N&gf8$o_chjoseumsMztqAWWa`M9!QEhTrNIKV$q~0*EHb&ncgbv~ zu7G9A?WVn?m6ycNSjv9{{+RwwTXvLiRqyH*)~%8?*uSk_JPdxC1*YF68qM#U>CG?9 zZ0uo*0KHT7oIVn5D7nNufSk>>bGnhDv?+Wi{AZj{v}nAU%>q3x?q@F6YqSh!q?4pdP}A@ymHiY593PhZFCZ0kL^s;i{?bz7;Gc? zu(h(vPLo<3B>*$Sd^k6cGaLPxdY2!CCd8GCXb3mlCEW!t4A`UehR(J0mfYw#V`G=o z+SkGfl~39n`k&R*R+aMa+EXnuv8^H0t>b1jI$c)UJ;G3Ddy~4!5j_N}7feU%3Ms%%mRY7tCoY&2hN+ zI-|~x=KR5C*s@aPyf(|XxM9LO<~`vGX(_4+IHx#_B)Mh9iuo1R^wKxIU!b8CoVm+& z=P-ABsuYWAFkRn7lDeIpt2imx(2i>AbKH)$FT^flO>19+H)%vm9!5-g;JR9Dp+9lr zbENE1_A9BGydqme>?2{H<)3hlbc&f8a7)pPJnOVG$hGb6Am-M3u4Ny45Vim;=G5Q< zv?sU=2!rJXya&W5g7t!k=F7})LL>Q#M~b>YeTb`*L^Fog%$6==6_jq0M{<_t{!|>} z&rI`C@r66$hH7R?6k+?c*W_6LM*TchX~!*EF!rL$L2t*wmUzYwLY(O=i%EQ_C9+R8 zcgk*ZBguJ!VqPcZJ99DrC+!63nUKd=iTj`E9;>JNsf5E>R%(z9<(UkTy`s z5zAvgsH3Id!**x~E0*~;>29m>9Z}>T#F@_LRJduOxsN`KHn0Y)C^AKsSULUeT8W($C zRVoL=HffTSFa4>yIU07msreCEUpIzfYF)G;V+T2wKSf)yzBVsn zUzUuqTY80nLzjxat_WN_+uW{9D%r621n)!UZi zrb>UoX2K{LVjvMe%SWoBns3NE#GlFA6yDtRRJUR?y_halmN#8y;*=k-DeOwsimFC# zvzlBI!MAIIa*hd4Yd)u@N`kbvV^y*q-Q6&=(ntT<->BYdoaugE&sJqvZ(*e>2Xq3b zQC-(>AZXN~N=uVatrItr@anVNsgz<(2(6!%t+6-NGtxAl8ji3Ev;xd~4p;lE_&RTm zPMl*E4$u!s{hwH(?~BFD1{vzZbc&6}8UCo6WqjuLslBB8&pNgKnSLTP68lphtNVZ- zt*=sCAx_ZqMVp()>CGG!dAxo(ErdEtf0B5T9-;rmXB$C$qN1q}63K?lsXBLHKi~ z645Z?ZBq#QYV%vu7-}13EbyC{MT-MI)*oXqfH##HdSVhgmsL+d)67e2Gmw|2h4pM?k+u!nieThM+!pwUkVQBH zuV!a8y@9dRL1b@u3}Fwo20B@fp?5=$%GJz|P<7E^ju4ujy@@{ne3Bv(?gV$ne3A5k zp2|XbFNpPDq}m9EIlf`;+P0wIYi8PffNymT))vkC2A6f9>~Z5>i$-t>f8YF`wY_Pq zSwb<9O3@F59Euaw)!n1rMTb-tF{_c5A_@BzGBcaYGsA0A(uAHQV9Yg1HuQH`nY<3F z@n5CVf_EHEo-40mD5ZL;D+-|0Qk+ChQ~e4@yQ~g-$9_>zj2~;C#fl^r*!Yxz%}(n+ z{AuzH>$o}rjZu>aocvXDcP~+(uv~_^`RNaM^A=0DRN$x59zi{QQSmp+T!&yzXHr;fH@kDZ> zy}fQ6b%kwfg_99)JzaR(vvzYY>mK(H^V{U}f@S8xF`eSA=(MmHnJ2sIzgIO4&ap>S zcy*mY5;6SF)yDMd{ti@~SvRVkDa~xCZL8p?HFmV7FeC6cTY|~sniAcUaW6>oT|;Zv zQKvXQRK(D?+piTGSQ^{rtOeX=E1W!EfV1FZBx0F4AuLd~8lB*OSosWoZJSq4?`6Tu zE4R#zHf*T+(fwJqt+uSIU$V2ltJBE)1ADt8jj;(I-S&ypOYCahfisc@w;}2Vao*etJ*&6Q||P7 z6||REh{fnpz63W%StRHoj1~_R?jgS6tQFmDzC%luj3(b9?vZ9vUp7pXsTpH2Ve-FO z6-D2a{+zYh9V$0JF!_gOxUfHFjZCWb8NmgY@szR-q5Gml!BAvJ30m#GYT9%6=(<_n>IG?3UnGcCA7v zdY61jHCE~$GhKaDt_Y3NwkjX_h3P38x@!)#kL+a$qkBM^3l{MJfT5++P8QcM35eb0`SGbA^Y_bX?~ zF2#&jy->UijnSM{`}?Kn_Gy_;6L~G2XtsO)#nYMGv;)iqS_`9y^_N`1Ji>k=#IrNG zW7*-HKX}=c2fTE?iQwe_EjU>}Mp!SJU0EsmDDEvPlPs48W{>j-CC8FiD-sk)%utoD ziV_;D@z&t{3U#w}B*&%Z1MDs6b25R`2Yjb|;C5&S((F8o>^oxy-za#&Tq#hojN8lH--rg!^SfioC@Rd2QB9DOa&8xkXM;#YKNs($(ieW7S5j z#jj4gOkd%MA>I*8K&zVFf{Or$oF?qns40&`1euOz75fVmjM?I6EF$xWWH%+8EtGcQ zpL3_l40Qti_}3_ex@2 zIJaNrRpI6@QQa%tAY7>4k<}&Mtm#dzkzUufMqgFT(k%^*RB7}l`~;f2hM%^6oJg4f zKO>YVoyG}GzRE}HnC7>tY-u5Rk7^;mj;d4rU}ET*YAQL7IZAy2KZ4O)a`2NiT`5lkhfCQg!nf+i7EUM@h@*Z*<>r2=kIYtu~i^R1cT`&Ap(% zP&kbL-Y_R?iYUU+mh?q}7=A`Cmk%v3}5MvtU6EK}Lj`kFo(fFNl5Adrw!g>Nkls9mP0OTsxYdIan|O*_ae1+J#Ju4wgbN6!KBQ zhhF$?QZu1?bYIQb)?(mGT|!H}Hol?J&6IN+RW4At81HnhW}heaI<8SaH?Mb$ZW>2E zXV1m@QGeJp)l(Sh*3G3?So16o@+n*|%Y=--_}k6diQhyVR1`f_nuRP5d8UYk&-(3A z$3wr-@apo8R{*zG+lS1_;MVOT5d!_rjSL@ROGlI`Nv1aICZ$tvP6MvruD^K%kQwvGG@7a~7E{aABI zafLCh+)w$5)liV74&ZFd4AwO9BNFdwR|=0r5%ohQmf%$e5BlJ9-RQaAw!Y(n*gs$| z-U1xckjmdhn54o8E)ow*8ig;L<-B_FFtQ&bTM|IIL7F1Xr>$zdCu1|v8jHN0r7nA| zIL5ISOjixz?aCaZ#s~)|Uec@+3!;cRAL-ZNZTeM;6+Ul_Noukc&(;w3LN3m_rgZ&o z?(OD3l{fff$vNT|f^5n`?mMB7mP>yuT1-FPd_;VO$#0w|9mk$tQ!1>BK0aHdD?E{!@2LdDnNi!K|TK`k4S}Klq+?k}^gg$eBVV zDa*LcwC!RpZz<$)sC_0jv|v3K5@^(O4_q!`)XiS~`|rS>}`p1V8f6$j^yR zW!zL|OLishQ5}};j>2nLiW|Ylb>8YJzTWz~T9Up>vFqE9=5=Y-7@P zciNq1+O|<`lXF)W?yYLwR`OVok*iCzo0*n zJV@RTWSFMK{sZ0vn!;<~xlmTX1qX2`+cOF;z$$HoaGQKMeN61H=z$HCOi_FTilo1k z?{$n^ta`0juQ;lHBRZ`L(R^*Xpq{B4SaVd{TOU?FQ^y#Z3quV3j7zdO;}Ub1rBRu|F1LbYZXN{OCpZkmrvB--V=KGIt{ zP>~@o)8~rhilYY4CRmka>`{ZL4;ep}S8Gd5?+QNaUYq}9)fpCp-pMwT0;-L*0$K2q z@B(No+ArV^l7V%2)?qcoUsk`C1pR8t#y@RHMIH+IhD+vH(Ho;$OG{u=ANc|4Pt!`_ zd%4A2*3@4)9Ozo(toj?+Q{GLJ3EB$&)qaK=v%K`X;jH9l<7^};R&5@S=7y($^D$+> zJ@^>D$Ww{_A|6`IO;dmn@_I`m(19fK?*k5(jF1CMG>^p|;8uroU^C<+RLDx8>5ZN8 zxA4$v$Gagsqil{k2k|ec(GEZkW?t7lL3PQa4VSUt*gVr+>_d16*dIR?a2Kv7HhUuI zT5>seym0`sm<($=h+Kpxv_NQg<9~ucbgU*#cp25mCW~=&so=8IAHCPuO?DIWtM*e& z!Hi}8s#@$={##8X9++v>x#4q?o*CQ;r&w20GBH1V7+@ts18zXC$itp?R6>ckp!y|b zU!tv1O7@4IHV2Y}jOi`U$)W1mf~DjL=|@pL=_E*%j3nI~Ksc6 zH~wb67#234VUDPN@@FtprH#S{M#Nty_F>}cUrW9+LwG*2i}cIV1jSDJe7>h@8+{_< zs^$oNEJ>hyNgs{bWsIVahTSwTqmKrhgp%lEo{N#k^fBgPZK7=|o>T8;^8!~i{IqT` z^l7?b%~h#e)>$4(&Iw2h#}5=$SVq_DB~je@saW^GVaq7-nrq~&thYRr>595J6V#;Oln$i#cP!Is`J zjRu697`DjVn;Q|Z9o)js^4y7-SvPuab#o_!4XovjUjUHx1>+9rUN$Cnyj40}>m4ED zL;UXTrWSv}e{Fl}FwwoXK9wgWcUpNRU1cxrOY{0EN7&w{KU3G*(i7)tH(Mvh1R6pt zcfx4X5le8uS`gzP&!b2xdw?FwTQs>J%GJ!AG{pR&u5+T3PT0Us@K>B~(sw3^(^_iB z^O~0l^2QnKx{7i;Ix8C_rR{5q=gJgqCvw*-7PLM}pQ-v_|CU&%71#$xf7Cy*`GmEY zTCMQ`OTbJ^nde2MA19+GRDGCcMt)X5nW{9aYp+k?Yj4z_o?N9UXxuj`OSG%`--(gU zqxq{Qc+_?XH+BxJcqu+K?pJY~^jXKtT%IDh{X%-4YGT{k#1Yy-t*y~}^^5G{uw+w_ zEjwTa*xwr9c^7sNkGT8#!pc)*q9L5O2%cyhT7wvG89&rjYCKHm8X{!%=2cAt1uS4| z{@O4FRPaCXrbA_-E~NmREb-2FK_X@R3>zA%*qyi+8>1c@eH_ozo(r8tfQCu_W67;% zqx)XE3nb=b+M=4Z#7SLdT|FGAk8kK{Bn;t=o79JmKFuOoSJMdo5J8x^kKjZ@F3?pp zi&qK!mO!P+P#2jx?={>p>iF! znEUFghVew9M$pvX!A#dQk2ChuA^d^r20bH~BwcK1n-(-ARSpp>*I z@hz$j{&vViyU(7L z0xlU=CYm5WlPr22JRV34U55^bg8fNsJrd&)P1ItAjFWVaOpnc$cbCh-QHnf;NI$`0 zXA!Eps*b3n5`+4uTGw(;8>^w~|I&fl`Bf$Qow~y%B4aQ8&%6v1-&maf+FQ%+Ro zY0pdgs3z!Ew;0sd^%Lt3Y6A^qRWBWGgh~SS*G**JFk`NHR{9asVcH;#j-Ca6 zhLxe~;f=^W{~4$iQ+tSUA<=_+Co0mNKnd|qy%^{z4KQ@kb;u?d7AptJUmNqq6P4M< zk1btQ^Gz%2n$>^IlFGGO2@qU-OLr0YoqNep1wKrlZhQ>gN(?Z!!cU`H!3?xp=xW#* zi}7E8_Qi1z81G4(qA~@m%s$8!QJA^IEEBIcf7X7G1_KfWA)5=_5cgE{04tjrr4GDQ z`$Y8+YN|}r)I+a}QSB|bBbV0ek+JDj#vzW0!P=6Z{t=Gl@2j z4r~+IPR`@g2hQ0c^{ z<{S7nxPx!PPnbM~{qUQbjiQD4Yk96D9RDM{E!{>8Ym&;diNKm$id#fZMV<;K)I~Ei zp2VD-?b@rvrL^_>*`#*@W;BpQ^a!(@ycVhlJE$oCIq+F(vd0?CmwHcBHZ7xHLN8hh z=;KC~|B7C&aT0dYHrc=8Kw2s+lEN0=`5)S){Iqg4J)mf`T15BAsnG`0 zJ<^(Wzi5|)M8i`$G3u%5EnO2@1NzZA|LHJJ+dOuokLY>$`o@XY{!npqmSu(Ezm`vy zJV!`k3HMs&BP!(P3J!>0aAl1((n(zJ>f7=R_HcQF;v35q?o;h$^RvHern9bTgLN45 zJ^q&gU~Wh4G<7mNLz98?%ryT=uw$LT;}p7r$-)OTOm7IJm2Yy8`w4{V{wO;pD`#jyI_XNjbgkc_1i5^q8f~E zER~_%fE;e8KLc%Zyb{t!fQpFF;xUAfUHT30s(*Y z6Zam!wd%9v?zp|BGh}-^ju!YU*0rC>8n4>cc0T2T=637Zv7hv#>?a~W7-hC&AqUK- ztw;SeP_E^uXAk5Qca#iu?7Uor&znEKazt9pu+F%R8W<4N0Qj(19lBCL?Ta z>0Wab2@!Oy7>GO*4J?X8S4tvsJTbjYn|c{fRGf&fCkCmrBDWClw6}uqkWUTsd~Z^( z%%s}^`a7g24jRjA=D-@$sJdL!5YxT-kD68H6^%>eF@V0gO1K$}<9BcJg}Mm#)zHux z(WLTo@B)dk@DE~=DYE~E7Af%59hkpreS8t#TiYdaGyYw#3I31lZv5^0fpQ1dx;>%O zVJ%Upmo+p%|1)GY-ZU;UdN(&}QjNb`?#nJXETb}EgL#wib>k&qqL{8p01Z{j7vI7P8M`vMZEnvl8>9j2KXpNjR+?RA{e*TKH{Ly{Q8nL;4Sw))I>y%@7G#e zhC7_iIA3OT)*A)8)q4#R(I{Dyu|f#hU7xM)(s0jEs&T1S82Q?avd0eSuemVQJW;>tM$8kR_K#{II4ZKpyRLk`1R2^zsrBScZG)qTo zj%(BS_q6wP{ta!qZ~76{ZU!gAz_O*rB;(M+ekPg8Cp*Qw*qoP|2)qEe_&!h;cp-8% zycUiPaY93pt$uabZmi6mBWm!^=yZp->KVY7U)N34k5&%TCn{g6^7YRpYt@ip9$%-K zZ{#(2X^)yl@b>DynC_NU7=p|j3XT|Mz@+S+rcEH5It&;L*~Y#Had>XzQs@VAD8vn! zhIRLoVHtRx`)vFhu?=}G?P+o~?~~P-rt2ojmzjqsn-p)%6D9ttFko2AYqb=ZUB5!J z01V`nYVU*lN?+*xp_T%b0f75vZ8Khi52ai+ry-Na9s~BEy2uWw0;>xdj&#R)ep>V_ z(c-=y|C=--x#HINwjt?N7c@7J zrKPYg5*<{~MLz|#WHlMPW4%(wnJ8@D*h#3>;hHVoTrGRGU`Su7g7n8m(;@?JL~P54k|r=v~D0(lsQko zfND&BWsIjRV@H~vP&*=r0sm4jgRek~XeU24vYyU!e~F!?K}ahY#c{?`;Z!bG6DA(S zzLNKow6d#&&!pd3P17N{l8vsNuDHW?sccj=G8al*)c2Uhd55$*2G7ja4`gJ?OAOnY zhPc}%h^ZL!A5g~R1n-4Pn6Z9JM9z$M|AkFtMuF$}0d|dHtKeVT9rZj>f~`hIi}zY@ z3I)<+tF|dkw%OXhc9dd_WnIN1<#tPD@oIG*_cgCY^MG5N@lw~xRVUXP^0}dL3r&Bq z7sl*1|70fwFN1nG@&XdXpZVi42;(tZz_b=#M-M|F|G9(rGen5C&5?c<_i6noxFMO) z+SIsPHpqUVW|n-4y|6;9^tBx&G;j^_Jhk zD$r&r^{au`bMrmMppV#BKzh@MNub`lrDNjXs%`?G2^S?#gxfoR@lT11I(-|qNN$YJ zuAVE?jgyoUiXk1gBEE8G`|{i|YE#>>^t;-O*2hU2eUSZE>`kMeedL%xbC@k6xCkt? z7WtLJkVWBFm+J~V-7AwT1_<~_!d zrn&r)n(hwQw?Xy-@Ni`4PCM@6W1CiilO;=PVW>#vR2c#Hlg}%@37=Dj<;jqlstXx= zQITd+(tRvUr;ojc{V^yb*5LO{L=cZS53KjOMxKLyyX8|?&_-~s!A+<)%rv|eEm2Q0 z9uQxXai&Sqp+cRxK~~;W19-`&)rJGl6b~!jgIiT8#Veq8_0qf)Sfq8$ScIhO)+C)p zy$$uT=du1K?}&f#Za|kHG0_kFMM4a6;R1%f}j?ihPu?Ba2fh<$)WAKw#$~+B! z6jI?x<5eZu)XTJ5HM{1SnN#nn*bMMBZ;C-MO&63m3JTBz88+C-a5-rY@|USN_9W_Q zz7??vivqbI16~Ce`1bkp@Xw!utSo+cWm#@PaZZ<^!32YbxOI}7a5rGFW~XwXevNjO z@{?+dZidQn@7J^HV}i{FrDkj6T4S+xWz8&8lx}f_-t4YlUYrc{H*Cp$0d_TB$dEw2 z&0UgK!5%;WVjoEfndHNoA3NNg=K(boh2h{d|CCe!fez$mqkwx>Q&6QJFziqi(_ z5NVojw0^80Tkom=)|g`$X4qR3Z|r3pSK(snW)c;BF#R!S&Eo~_cNms-}Tr(pf-4`?=H8N3tP zTNqY$P~R8ZRk+!Zgm=&BG(vb)da`LB{%@iqk4<>Q`hqYqJE94CPmT%_BW>gy-z+SS z@^v4KzoXU}cS?_uqcqoKze#WTM@1|dA|9Yrk+Cg)s(EB;eX{x#na3;C{3a{P3U!I3 zurOPXlW5Lp!x3^;+C!5&xi1khGvv3J7r+N9B_b9gC^o1Z@ud#?W}^@2zV80`8oJ!* zB{|M~(PYW`Ge_hSc_}kpWKm3HRLzT&r~Kp>kI{tP_D4hsr^TbakcS!fNj#k~}dU|RKW#jb71Y7dFIb*Zda zdZKl(5R|*yZB26(8hh{By~?e&X_YtCJ#2m@?=;QUO$9x)d#qX6j{m;pO6nuSGK)50 zp()1FHzvh=nOhOQ1GI2CK?C7j_K|No>clecBJ3mUp;w9&(BrAMTm9d3em zGPE6Ta+05IyH=a3^lD40wO8ebYL2u`$ja1ZwjNK7(O+q+Z7lXt0DFqDj{j-A?>8)Q4>(mzeaUw^C)m3h%jeSLmm!k=};37~BC? z@Ks#{28g0%qLA(?TOB0YCs_q4`Xsz0qJp%ov zd6#+sTdHeKNWiNM3DIptKU0seUBprI^U){BcJPh&cB%py>^h5f!_xGNOt&Np98Rb< z={8v)5F@)FlmK_+@0#X2CSaKNoTH8fE1%9Sz&~zYi;>}tXvGM!qK;%*bUoS=7t2Y|TP-*!u2mWY7ae^rd z>yc+Lhhoi{6M-;nZ;Ay>!9(Ipp?Z8})F;?NM1&E@Zeq{qJ6IpG(#HcAlW*M0h@;d* z4W`oKJLQ+uX8fi&RDHL;1n&otCeY18g?Z?}xTSt#9AEMvrm|iv-_R;-w7aK3q{WC6^Ueo=P?*pUg zfn$$@<+N|qBxnYm9hL;Ya6}CEqh&Pb6Nnw6m%1^I{KH{&o?@ZJN47z^i#siHQeEMQ zW>Wo%I`2bvCk%sPqhjPzG z6G%I!^$Eawv7g+I;H@mFTqPSfeu30kzIA+WLA&DqIBnxsRo{+twIX$BM@Z!vO>sL{ z8lyF~T`icSTi6zo(_MeLl}>Lqj<8=$nr2eked1P{Z`d@EOTZfIw$O3VMQg9o0;JT^ z=;MQ)<~F>C-$-cd6lZJe9#FaJ|qso{#}|2W*d z#l?+V4lP$-BEQrm%TuW_wVy@HDOLUYmR{7-Mom4Ry4f7a+ei;>c~th5&KLYs6iBlo zdu}PcOJdHbroYO-B>D9uoA3-YkUw8&`y|A-v5BWtLQTUZgmpsW0pgLr$ zGvcYM3R|)h9jrp)4$*c^Yh)h%M7J?yE0bpU8qlAaW)^zoG0(u~E-)L9&}t`aQqwP4 z2llZ=E&PV__&1v+_y%EV-ASUC=rAvgsF5U>S%?+VeT7Gf-|~u_-;VC}ZH5cEMYS-w z7v-c;#_gs!U1?+q_0~`la*S>;$pZrDgTPWRH4_B=a9P4kLdB}h$RhzE3q|XMhlNYf zlj6b6!?8F?OC5{Nk^Wot3i~4OSyqTwDa3^{@U6CrujX7Je+uDE9pW@Z}UP({z+Xg6Z9RV5t7g<3% zIdXP)$xUEpKsl8MJ9(|7ZlD8PKG8~iu<|arO1?-cgW?o_3tmA7l&hL_aGvVl+L!PV z^^PhTlB_vcdIj06y;vB7M(N(A-v<`g_OpF_cZ3I_GX5ch*&2dUD;u4@D`=ONA z4e~Vh!!?PTK-d&FOndac61`cYzar=kI2qa+rviJ8$+ezfqw!tk3hLfC=fJ;{LJ$G6CN2<-N9B=SF*htcFZ@qX2-Ooe!IMW(qgt|`7`K8erIuLq{#SF&z`LSkrI9yFGyN*oV66ANSi zLHZE?8?y@SMmB^@#(I(a0~+y>RFL;TBAlA#x``~N`^xs}Jg9!6M%^E(fAjzJcc=mN z(+pdwLDiAQR%%%JSyLJ1S}ZlYPyu-#%x|dZEF3sWWv9LeS5OU!S8j_EPpf8md4nd@1y4?SPzr!8Tp3s39DvJK59y$9=8|CgaF^M$v= zaFe-M-e6qKY%2O}qM4a_ZRSRX$Z`Qv7-8xha5Phx@CEW>Qewm5U?yfvA(GC7g;b&q zOh`ZmhBBevF8D$w)b&5&I1?_Nu9<4REzHzft$5R0oyqE1H%s4SImt^iq*&;($Hu=b zNktapd+uAFyXh*oHFLWo*KAJB05)+^2`j;^?5CI)&{=lpnBnj{mJ10%N3bOUVOTut z;_Z$Ln3rzdiFRhUbg=qW>jS|)&4X5blTLft{=3dachEku>ac#5-M36*Xt(Vs8e-Ji z%5yiE>a9;QdFCQ3mhue9vkp$E19_IeWB!H^OMJw2cm;PR_z!Z4lL!2T{@{8#PVr@z zySWoZY`$c?x}xK#AVrhcQQY`io78@;cAhS*y`-u@@7#8{^pl}$TWsMx!>iUMx!Fd? z&P>KT(+xYB(rLb9d!Eo6_+%@OX@dN%8zbhxLhH!j3&?Z}>Hibm&%N??!=7^jx3R<^ z_KNJJ<-f`kBFJ)!x3e%s+U%`2|AOm9iZJ8T&*NYC=K zSVgs|atkHl5)ca`I}%f7nXdG9u)Ei*X9caYY}bYRWm*0;y!JR_nPWcfTxLO`-7+Z` zQsX3Y=E7^QHy`Js>Sx#Mxwyv0>H%C*vwQg>F0|u+SZX;Z zkyc4`BHPH;!{GyK%t5mSiAckU2y-^|MrpvUYT!!N&y>|3+K(~tcPesEsL^+!6S+o)$vbA=#1xMfsx1Rckp zR`-Bz5RTw=(1d7ec_=+cGOTDny;C|TFNeM(kIvlBbXQzY^(i4nX+R=6DX}BG|!cqr(Be|h&reCNbOJe)~rd$qjPlSF%|SgL%)cz^ljtq z;Qox4`MBRPrV+gEsbeM~JzV-Shp}|&AbhTv5?scgNd9e_L4->m*Hscmxht;^u~t!4 zww-vS>?~>~omFRZKa$lNw@i+lss&QMkk@oK6OyUXhKiUHYP|7!L?ZRY3`Ou53xrsG~GZz z;_YyfE{;DL-l+TCs6hPnM{47dX@+rC-H;!~^3vmIqiJ}d1>J4BnHz)on*Ygoh>Zid z6d3yfisEnM7*rC|pBM;dMmQ0xkeuMZ$V{}+zYBTIv3q%yQsZY`mQcS*H*pg9*7$%w z5;B_X4X>g1=AfDbu*Q6%auWOikd!te6~K=|cjN##E$0dv4~@;}KsQ47QUb9kcvAdq zY%fw2eFJYmhlT%-=z=~D?oF(~4*Pc_%keFq&&i*}W|za%Dsq=-53m#LXqgG-Lwy?v?Ke!pbTCf4WgJk6x5D~IHBLsPgMkQZBadcCBDK-L2jb4Q9 z#IA;4!j(80^qFuceEho**NCg0|08FTQ(c}=jTBFmWL|~_w2T5WP_X_Za1%XLeF`*S zgDRGQe=uGN0<~k)3v%HR*o~b2@N(QI{S1yruO27WNUCrT5Z(Q<4I(H6cIdqI2) zx{t3Sb$-7HBl+B`7nw%MTzgPMsb|9Vrai>!<|$?|aiksudK34ndB95Id&L+qgZ!(c zA9$IJ%)bjM$x_E_buUtv4#3OF$;lx|3AtnJe&i4NEP4#OoAM5~V;m(6I*FH1Oa1;w zxKq!(h7lj>a93aQ6b%dAjPDue=Dw!I^!K_?=2F_hRyTj8PnYijX4CtM=YlcxmV7gK zkzSjf3&He?bSK!GUXpYJzCbS+iy)o!f~e1EExj_l4hyEY2d%+=)5rZ@;g{(fj`Ywv z`n78Y+0JwqOfe=~a+_?%50)NvplJnngeNlBa^uSLfi7HKaWJry3(D^e7O=mv?t@?0 zb7?!F73`uU8|+xj8yk)UutiZ@ke{r7xF33p?H1IAZD$_%-NL6ZhrGNAEwj>`IV**^&H7hTiSn z8e0wf+g8*PMzpPv5TG*f%EWy&%Zv*bkwRw%kKXOA|XA>vc`9iX7K;@xks&yA{ zO+C`8t(j3RY4xjPE0XPx8`LG;>?@n9^LN+{Ed|+P`&dC`dN~7?58?8(X{bduXH;(>b8>`Frxo!Pw?CrkQ`nUP1Q@b@6x*`m+*HwRN4!1ki?ye8B z-LAJ*d)SsYmX!apq0J+T&)6zkZsd2`LIo?c(`-FNooUakw0I_1w7T+l7tgkK74q{1mKWjy*{+rglCx>2E!$-alEjup3NG%vrBg*l4YDwr z$zdTDlWuR|SWAnsw{L_c*$j9Lw+sS*JH6trAu|PpC8#OCX|iQt^SQc(+(&*bZy|SE zaIbtEcS0mDmUG+1ukxe0f24TUXKsPqC2c)7RdF<_g6mLO;*N7RO>NXrj?-m^C31`* zH?V>uOj6$pP75seNaiZx9?ouDC~6c$u$}zrP5G=<@V%~qB}9B)3k!;mmY1_SX-IJt ztB_5~@5M^xKeNuWQYD-=jg_l@C#A6(&9u1vtVx#|HH@|Ce}!eUbBq@QCG2AJ0beb< z0KDuWWM?7Wor~BBSO)(g{Y~_-@fZC|5?42z=_*Beu1qi4p7P<$07aLgU(7INR^AQ9 zMb(+LhVfHBPD7bUZB9}+lcC!bw~eVcL`4l@Y{q?I1LJ4MMV8&^@Ca;WwYHCHjG>H@V|xwq^*wMTWY=zydC{?1!QJ=XeVvD6P; zby__=Og}NniHI|3>mQH&^WFSVX;S0V?>D6eq$1eIQcFy@E<3%W1{>3+Hn;Nd; zk91>eeh|a;L#u`pS^EFV+zGQ`PmwFJ#MqfPn7C*XX8t65nNw4rlSzP2;vNzL2gOOr zT~N=+8`NO9XIL1eL52l_)Dbk&cMa`=DLpRII()P9JNh8mw{s;x;x(EIrW>E<}#92lS`ZTpcw#I{~y{8_OBU&UI6D;hGVW! zP-zAxgVq!lVjJOr+(Ntyyd*ORuS5b;Q}7kY`a~Z`WlW8IK(wIOBN5^dMu&bR!|{kf zKDiKo>bs3{B9?jlbl5F5F7dQ0HP$g{kAXkdYmj!Nxq23I*TGWUiN>G-B^S|gC{u9X zF;l&m^8`!5{4<_nb1^#Q3HA%Sm2eRk;i<6;@H6;7kvT*X;TL*|_?K7~SV4x6LB2c4 z4dlO`gQ-+1(WQ|(PaSL82J;+Npf~VFd}_59G6X+SQGgimUnMf+0ukl_qDB!7ITmyV z(Uvg|{Y~slnT)B3HwmrSP0~NM1ur9|kt6UE|od)_sZR9P3o2aejSKvd`uHye8q13^G!N?5ibdCqwjk=lPbRN{d_J8EirRQlt+Xk-ZHe*pca{kQ^0LO4y@aM&yA}Th_p=<&kA*Fk_Uuylx1}&$g;18h$!%yi?s)ucbR1`onSeQ{ z%wr_j4z73TXuOdjRSMW? zzgGr9J?%4#mO`98EAJ@W)%GCk32e1t>AjKuwh_r;$PDY+_&hYgniZ3S?zh|+2!6$u?+ zT}KJ8qgw+~z-yn|B+1EWk8f$r2xz+`kfn5Q(}=kEV{LsT8)Ag5t7Ts!PPNu4YeN2N z?XEr-P-9=DlX1C4%eYi)~wZljLcMnG?xTsq+D^L6I-54DSU2ik#pL;0`v?v3ds z?rlq&2Nw9Xm9$*Tac%t}Se?t%dRn=_{?y%BtiQmKUlA@sllkG*k@FGFcZM@z$cye+W@q(u}79E?Wkh_xbp9 z*TBo}LT)C~(`gDV_qo*aiBUcmvyNSQeA*FhT!`c){Atq7W0?4qOR%$+kmcp9+>mhP(gGs?j?ROIHED zwIPNzDP3w-vw5nJDreSPomd93-8K0|JDIoI^1SEFU0ro{KjwnIB|VrqY%nDkFx!ls z@s-S4(}tKa%v$sPh&#+~@UIXxa}H_w}9?{oKHw9sTiPV*8u1lNbUgKD=w3JfTN|U zJsdT6P+B|{0WV5YJ6=@d#&%Kwaxr=WH4DWeRMbu^A|#Z$ zhrJ27NB74!_)v5%fq4v|$B_ljljv)dTU|4;5jxL%MC^mBD}=;Z_-e^{;yzMZ7)bm? zcIIkGKh!OA8Ci}}srN_%eU{`*uEr|IW|6nBL(yeaKRhiWnTo+rgbbsEL_@$eY9{f@ z$4=cLDGy(I3^mMoE8RgYuf2!gba0c^_*=|W{+8&89Vw9zUii?014J%fo!gIq@u`^w z#9I79YCG|W7@l~NbR%lwevviA{OA$n1mbnX5b_XN6#Rw!POb|$MI};0d^)H$O79U* zJ*3V$-=;I@uv#JRNmf^Vz!ONcoWQHexy4^_nA}}Zhc6{>`C&<`(hiJs^V7cHdC4Z8XwA(<)q=2OkPGiKATBNxr*OoViE(0Kqe?o zMVJ`xs2Ri_#w&ak*^BWH{)en|Of2VZ8*;ldi+@y>E+=COPV7Qu#O}OBwAU)$ehym=?s49Zb z{u6E>HnUtXLH1)C0w_|%M*2YHZnn3Fh;m}?xD-$zv$3Wd>SsG$ITPjC)|C09c3Vf$ za&(`KpFa%iZu8I9VwKiw=?AgpRxZUI`)-X)An^b>)QTsRS8u5L(K)}iwY+WoyZV-ruH(xZV+tC^ z?Qia#GizK*%hik{9eV`pQ(ko>ismKuZ{I4J88@;$QnozuSKDgE>9GB6f$IK2vev~~ zvEPr@A^NLcsNG=Fx;}CkC%r}`+Wg_$wcg|R)~&DFJKnuPQSNAsn!Jn8bbM&OQ4rc; z`YoPW6EvKsj+fxNaW#`)V2~QOB+X}=v`Da@1Np|PB zwNkQ(jHXsE`KpvT_WjD}giCgj>R9YIdk>Au(H$?*d4=7xrRx6+T4ud&JnmoPsKtA| zURr*GSKXABaY&z0H!Ln#PEB}Qneap9qt?q}UfG1!7Ri=kU;BI6i2ND$$#QM>YkQJn ze@2Y$naU*vvrSfW343gbnoqH>tWR~u$ZyuE`rly>tU1P6L2E5v&Ds7^%TlnfcTY<` z^v-Pp_Z<1)G=OWvyHvlm-SOn(Wpmml2V86ePdj?f{J8_GUUT*8T zYV?v*GS?ShTRqRlDGZh6wm{|N((l%bstrX`tQPf|{6W?f&7*9S<(Kwj`bo=veeV=s z3uo|8AS_wN!q@|rUM3{+9(UIKZ`eg{F7P&JC8vUt{0&?@Jl}gX*AsPhJHuYU);X!z z1w=`8taXg~Y{e_fGtJr3IhG~bQ$=GfV%?#<9hOl2rfeVXyJ22B%bhZ|B|qTinaG4p zPHRSDCv&L)7P*rf3fjZAvk#$pL37!y@NR!2I~jTH9mkrmD7P1EE#B%hgH0x$@Q!ic z^^P{3+imDq8qHY@&kG%fUE|)o1}@Olo^^ujX)Z_)XCDLolBcnUfh+Mp*k$0-*h%LQZ%=xep#MU(9TRL?w;PMCfZ_PX>ay z+-Zyg{*yI`X+ex>BvXStNP5eZqxJDZrU<P9GxN$K0V05@eVM{V(wu5gz9 zpP{o1Yx3>Gu>S4t1nCax&XHrlV6g7)?%tmD#%S!uPV8<)F|g}3u@$>p-+kM+-*z0^ z?)$m#>pD+Um&sM9H!d~}ucP&a)6hS zXk-G_DZ7mpAR#f<*a1w7Ycehe21h+HZZLlgk2CH!-wkFMubYp}YBByaclvf05oQ3k z!hp9Cp%Mep2CvNa0nNeXUko%`M#(~h#5NTaXK1nB$=hq#ZJnPDG2F6Z()$d*t zoY2{@hBZ5Ondd2|J<{R{;!g;zch?Fx1delwC9IiyoDMn2XX!euno&Qv^$DV*=0xiZ zl(nODA9}l~t245_lj$o{fCdV4XTo~V zI`;k8o$fchpvcwkIDtMCaM48{0*jojQs>Oqjy;O3sVe(V)$6)5Ed;corm=Y?Hm?fY z^al3_{Glm_aIh3;Y$2&Yzr3#~@%bz-nEE=qw_zpyP{wSZ?|4y)-h*L#61v9oNIsTOIlXhXRCsyzOXfDtaW*f0;0TTwD%I}Pvuo_8pRHFH!Puz zERARwNq2)bd34M{`4R3nEL-+sH;FSjW1Q;>cT0-cRm4Xn>~d}tPKv$j93^@b+3#RU zPKEZ_Psol2?y(0dZu=>1TJ@-Df!3$m64*!YAc_Tgx_2#T2?TRtQhn=I9hXkA2{nDse`Bvb$tip&x8N z6oUe<+Stn1eoL%3)bFOjeLmkGu+5%D^hHpMXFB6eWvzQID-S%@J%#Nq+2xwg=>=7| z{CT7Dt~giltFkf9aKXa#2aa{Z_bG5koVYe&y}e7aKlZLYRTdZd)wWl@DfE{uS6LGH z!g@sY%kQYQNON|Y%hIdsgkjyKtP9X2*LC*4%7HEhH@^IY^9v7MB6d3XTF^-6RKXnI zioaI4BRkfSA-a=((B2~%m6B&iON$d0*`CR)u~%&p`MJms)<4RC(0|rOl_T(nHB2+Y zug`KwyKLG%3rU|}`_1{AH?8`rb1^>xveua=NGTUO?g_I?0v&o$M&S|1OmSQu!oF7$ zn)Swxl7^)_ZQo@vDHCmT>=wxB|q|o6{4COHpuc!eKBaH&;C5ae}Dy~ zlTPage(EpRwmL2d3DqKpR#XEicZ7>`%O}_`OM;6}+YOQ*g*(c&O zZ7Ou~JZrvsOu|&lEA_=#y+x;45!q`QrPYRw1diz7K?y*UKHeV$uni-omjmU7r!|Lc z$K?O3T5dBdfC{6}e;rm_--=Ru_u}zUjgu4+9OlYhhynh~5+g0wN7z{$yaZ;nZ}w`KystQ)PXk`cai? z?NZAtrdhS>SEX;Qz*$sWvEgl4Y0Hka@!5(B4avTd=ykWPeMHu5(Ky`SXg*+6PhVkPZ+r^9X`Q7TT-j~;tg8iY zv7FT{Ds8Z=&^;`ITQvHRf-x4Do|JRc60Kj9skHp3zm*mb+&4^0y65v&!)6-+vtdE( zBp|}@K5~KioDmW>+&tg7CJh7oN3zXzs($F>Yo1HJk#{Q>R-Suvjy@N*yn4~-2)bycat<{xP* z!OzXXwql4K8LwOTe!)ktN@O{rR;l){Z z+)a{(8OK}-IdzKMxl|Pg?VW>0s4LfXm{3@-p?wV|ue7Y~94-*_xAiUlcmBH8vBVoW zWi9dKeHjm$tEpR3rA;DwSK^n(M&^llqPLU%K6-P*7G8SzQ_l{;+>oK}o#HV8BV1k5 zl`}s3T#fZpwmNR8en6MB-$hqf>e?q`CxhW_5Zs;8;8p`+Ip}`NCK4sTvH2DyFej;b z5cNsMrKSY>{#0Hgk+~@GtJlqLif1*feLrk)DTR*8FK$#aZe@S>?q-oP);9cL ze@TTk!}NE0owP~ z^P7_ClOdMIl?-_~yKw^Za!I*Y!;S{cY`Do$=RIzS;~vRg<9p{LGZ^km0!nI_J5+cy z@tR96j*4eHk4hFs_c;g2CPjc8zQkOJ*WRd157=+Jqk2E{iY;DqZ0cp}eBGRCkoPGo z5t8U-v&H3p4KFyGi~luHxK9did3yO1^VWId1)13rw?|0H$acLHb)*h*Rfu~Nk2u#z z$HwCwzhvm>yN+V{+K6JiNBLjKT-$w>IN-9aK>cmzSL<4B%e0}^2z^}Dc=sqhvlt*=EiVkNdxjMziG6uTFNzSKSb(*C266ZPkWnbb8 z9AL$i=yUc~Wqw3~?UqU!vfP%g-WKrOxBXyN1T>pIjlcWW%HN>3oEo9f8l~M zzI%_TqZsXmh#iI5t}7CL-eeb4T9)q-)~nhZ5y3t? z_MDKNmU;TBz$i z>OZ+bjwu>`)-d~4&HvKg*lV=lm3;xCYw<-MVO$m7&j$@U3+EOCh^0 z=>}|I7Vyw;({DAf$OxT22p}1cLS8tIYkro$cC>5jd?ytY?RrqZBU1Y(|AEi!3C+1+ zx9b*V?zcziUZkzH_3G1-=h)PGQ-aavr#~Nux2`pWMW_F<;o`O&sBrk21PrYpv;1><`N;)8D8U7QA^{_yypkIXPr0P;br&gad=jnSS5RN6iV- z+sqboaK(E24B%4PF53&>b@5T#Zs2d>8JpQMB(K+2Vi}O#Ya0l>$vAI42wYD+Z&d>a zlX|Tgz>?YLEbjngY`3KyU_>3Vi~;Jyw*W=}6w(Mx0HOks=A*zs|8ZuW`GW6MA=f;2 z=|yyM&4+nC*nzNzEyr*d>#w@^5*8xPTXv90sPnq5lt9d7`7-Kb+#`XEzLhwLnau>q ziNxD%vM)B1&PCC$)j;`t7NR^}*v9#q-zVP3-<6J$J{B#B?^H~Xt_{1Y0x6C!xro>b zUo!VLY6%kE{1oFxPj$V;NihEdeiPuh<+`z?WCCA4jxvc<%>PLJM~P>gq`#$w6NSuv zMl$jS`xlD{{lyFCY$$s!Ao8c=uM#a1E=muVJdl*e%jGfhF<~2(YUR_#FY41VMRSKB z3b1FIgHh%9I#)KPn$QOzaaAO#R!#s@hRXm_xz9Hvrqt32jA|Nz8Au$&P_n)vm8=z< zS5O}#jsIWSa(=upEuSs4h|OuA#E+z};wxoU3UQcQaX>wIv0rUB;o%%`-AhtRlMFGM z>~bzZ#Zw-e4`U!SUk?*Uqi>LYBAA(eyjP^vtcCPzl#84QAFX~c_Y`s(6U?_j;q0YC zVi}tEpBR}RCy+{MX~#t$&2JD2k2nKCRu#sc_7$AIdE>i4bUB zmU;uqw=qB(}BunO5g z!`r#1ix=xuEg5B4ozA_wVyez;9bb7t=g{k+MqRt&B`i|6UQ}9tP1ntKAsclsCJ9l~T;aj`aMG?uU&BL3iBoPEg4S zm%-d!zTSC5TLp1DGi5ib8IDdtU2VBzB6C-LmfcMriA=Q*!Q^9#Y>v7-Jl8h5awut? zb$;;*+7N4a?j)wyvN;vTt+d3)F@$Nr`Y@U_4G3*-%v;{1X&hU)tWo1=DPH0=nyoHSUFjqqrX%M{ALqOM{F1y7r4ldAThSU*V&Zgq=)QOHoIqMkR z_Eo9lIh$;2<7x$mtZTw_k|&l`ZSgsa=I(3QlHWOpYEK5O?-*}9ShA(9M_pIGskKG& z1+u1v#%ruz-<-o3TYI8uGSO21%KHIz8x_-V0rnr(?O9dvi#W`!C^}17<0{ILGX%~t zDepN<`;EAAfz!4)%p*BurL^j@Pc8I%{^oTr2(sA=yXWmPPAcx1TdnFUJJ<0_f~mOB z?&bchy4RLKx77}6xlSBZpV@3gv5=0Y@Y=3vm)gxM@g$xU$)cVO;GDU^RCRV^op*2ex; zs-<0_Y;}j??EEw6AMm+t6R<~-K8rEli5_QLLafAGF+3rU#w}3AQO*z;Vmi%3%Hxbv!-OBXcB)yZo4E+I4>0@BTI^6?<+dwqeJUe zy~=0vUix|hV_FN*a_k(>Tr3hFWbMbN6Sf&Z#EB%Zav`ao{6zGSa+>PpjHGR*7gIwR z^O?hO6Ie~GzWO7a`J9_oF}%IJw^b(>fgMP z@GQdI76~$)_{x0~olGvaW@Fh^~t760{O?pDY*%vDbkYE!4i#J7Q0UNT6rKeRmspyoSOuDK}~F4 zTmP4aaQh=C)Ag1{bR=WFejqlTc}Jnd7qTY`ZxNxKdbS^##qFd(sCM2TER4QYz^R|g zd@FogIge8)ZZ7WQt(1aueha6_r={E%FH{bWZILCYM}$Tz&TIYWU}{z~?>0SvU1D8w z(dvJ(yMZUjFwRjO9Sz|gkw3%odB=rte7nysy^?rB@RHm|{w^Aa`9#YWm(-nPbVwR2 zq3mJOSH&y1O>$yRk04C(B4w|rSJfP=kvcWd&=5t1&aXpPE#oa}npm@kk9Dqv{SeFq zGT_;QKJ7`QO1M#8i{2*E2+m;NiD9g8Lb@c4EF_twVVGvhZCOwqpI#%6t^AMqP*GM) z;&_zuoOXVRYIjPzXp&}fEM4+N>j@2%_vuHsuk`g<^^IlK2gH9J&uV8#EM^~gM>DN7S1V?;6^2OQTcH%+C2DsL zfv42iQiMXDc5ZB)M6EmIo6@R(+jg-6uF!ckmFE>l9l_9I8|;iRw0qjDM}JM=v6&)Hh*ssIxQ-NEQ8o=3miy)_(2koG|Wo9XbUp z=+*nh7D|Tb--HIss|=@Gf0rw@%!W-Bf3<2mrpm7MmwIG0ty|DLrb+@;m?|OjB)bkXObp#=>fg@uuoc?R4W_$%48q#s$1IM5WPA z-;erWv=NiBi;NDGi-0t`YdgvD#^wru=4V`66wiz>p3UCMK^fm9|K;y7O^gi^=bExY z<7C%Ov=%~1qVr&hxJm0(VA(n`&u=9sw`-)#Up+VlUvfcE%0FAB|Q*%8(2eF ziCF+}P($!sAhmWj2?-3X2&Cf8eV{&u$$U6l#Xe(RkbIk8W|qfJ61_A-L(^oP=BVbS zMcu9jH@?*7d~Tf%E^?OXuR#7fwkW;TI~_q{a4o~Wkn^>EmVG9z8S&J%f-noc%QhR? zfLmZaT62Q9(24+`r0lkQ0L`U;ury_Z*!7mE(NtqD}xFS|F2}Uo^c0=^F>T z)|S+Jr&yTfkqxu;p%q^}2*s1CeeM>~`WmV0HiuG|?24rgMhtTr@k>z89XF8Euy-9v zHF&~%yBEx-gxLB)HFUYHJbN_jwY4*OKCi>_Gv>F5W~mLWl9d6gn{o;zt+CGU#dR%1 zfjwmjP5nA`#jwWvih|0^-WQ@V&{ppt_9YmmA&hE(XL(BTL8vG<2eBF(?`npABfy>8 zz*ESJ9G5{O=!5O=vu?3`sr6(H4`Pdn`6Pk8B>4SFQkGjv5A0_*{ zwiKSAEpj$xtzu<6M9Em*5PMzBYvD&*S}0%o(K^HXF8{{-t&X*zb@T9MT8VD%NNr>} zwBw-cCuCN;LU5<*Wm__9N6m?r4-|Rb-sUb`9O77$2!0y$K0@DB-|~0i-VO6EXySif^HMVT*)W^f!=b@fen%$SnEH9i6>F z_K1Hrd6D9_Xlo2Xbw|24WQgXW;&S^zd=-3dBZ%+@33YUn8qp)oC6su~In4>`d0d;U zn9e2;`P-NyNa@UBtnHK_5|l%t%|wfN(;3mQ8vb(@qT;3S5~s7MN_>_-Cfh2#AnZss z%O6MzW1z}k^3frKH2%sLZErB2&~LmZTo+dE7*F8h{+X7N!U-*!IP!f`r1T)QgWS!} zrq@t~jK$1J^hn}c)*Z%M^bF1x)*09!-dxVHiuHmyy#EzV7p)SG&BjTNhzZGbS-twJl_rwU(ldESsf!ug z7)f+BD~s5~#Inz$j&*X8d(1w4W>JgZCaMI}jkBkok+jTs=LYRSGSak>vpPin@B*1HbCio~gw zmU^Z5n4XMSD}gH$QCB7J!~-zDq^+Do_()kUjX)&IKH%Sw=gYSt;nc?py$@1Rsf2^k ztgEWb!hc-3Ixfp3h}EPd^@@IL;nDAISQvm#oz zuKtU{qA(*f6^}(opM$K5H>;A*)&5S}C?e}FMfXW1zTCn;MXO;&BdD@mQ|pYZUZD{KqiTL?TDARkaLq1x z4}7!czHl{auy!7Y~%Fnl#vKskmd+W6XQhHLWi^#z90 zg3pMJh6Ah~bbw(8#erR9*oDg^_!&;ZPm?woZbSX4IK!v%bVh`6X5lRM7$YR}4sV*# zki-yH8+S#YkgPR+3>l@EY)Wa^2_9$JU|#`oTkxjF>cN(A8fA?cI3**)egkF!1g-{h zSrN$J=6~e3m;`%^-B;Pl8dqS2*Iy=lw|uKECSSHJDKpY~EENR}%m=`?Ofh!`usSJJ;M)jAZ?s3xTH6L6A{=_=7b3bzcVybf%=??0s z!-Cn2mD}IclL`5DY}EsDjIF0Eik4x^C@5tzt!pxKIlHV=lLiSAERN_^;`6|_kVLr- zV7cd&@S45Wrt+ChMuV;5bfa3OuM&GL5^c@ch7~+LY^CQCLk3!1!)2%^c3b*))ky`A@A~(Tl~cmS-V_@@mU0*U#d% zc8KLoSwUNn{t@_9%P-}f%8uqo;(O5arkmUswU-Ix-j># z1y(AXvsPaOp57r)Wo6D%vM7?a@+|{f!?91oSQEQQ`+I5%7{7xEH!Q>MVjv zF`2fLG)8!hQB7XQwle#vHPl&bFMR}VBR7$81u=>DfVCBxD_F_dP#!AM@lF<8l`sUq zGm$c)xH@sUf-5~9bwFiTAc78Q)~miZ#^53dQ=EhGSBZS{Jt9ClrQJ$Sq(sOqln2yi zVLh#p_Ln`0QOM9z_A`I72IK14yV;B2-CQHLtojEZ%O73#N?0LyU$9mT5xvXIkm9Ao z6Px5#d2!S(v>RX2mqtErm54GWR~V_dV(M{i-CH??DJtR9U6k7u8e$%$#4D*_^UJogbRo${Xd zoBV}#T@Z?0%{U}P!V_8BMGLDnoMYk-Wk%j>DWRZR7$@lkA0XcB4C$EvibwF;bO zbkH@`UF`?Y7x+PLsqGtb189OUtm2CB^(w{0qq&W*YH>bUAr6WuT0Nxj~yG zkFM7-(&YcD?z0q%N2R|w*Oiy^AMtBdmoruhAE=)urbs;6DN*y~l{$RTb00^d%gw00 zCi7b5b>Cz!4FF<>yh7E0%8_sJdD74dKi*QDRME_sPiRq&B56r`lr0z-rB4-H{~vvZ z>R^?E>1z*{u3|saOwM1%Q){ke$b_NVm5Ecu?{u!HX4y@>G3cYR*I;n9)=W~TT2{l# z)D-=mdX2hOc^a`veL{Q-byxk1`wBZslTLq+FVJX+_lbJVN%Rr&LG8>sBW<)+TQz{8 z(>^LKVGq?I^AX%Fx*Hh<0=b@)@J&qCe~Yrq_=bf+f0Qm`sq;v+T7Mb1S+hrfPWKM> zUEiY|0?+dCL<5jIeGfMaeMo#9 z-kO(-e8oUBkJE~3HD}S*VfxKu2^;WG(`!^GagV7NrllmC_Eh@O&Y8B9*qK(-hP73q(`QFQYh8k$G!ynBulsZEvlxy8BIgE8n@JHGNQ=OC%p&+v7YYOs!9GhOrrl z#g2Ar1A44uIDQ$n$qpbl5~kYz)-ELNuo)o;s?7SY_$~uwwd7^6vDTsKBA(vjP0$Lv zEmNY%(%HcB;AF*GAlCK~tZM8t`9WTL-)c&#sop^{QO*Av!UfCgpdK{qhOeu%fI0}3 z=jy`;Va~dukm-1mQ(l`$8s#`%@r`oVF}m18zix-;erCP1ElSVjg;}2`;DmZ>Mik0N zsd5JwDpbH-n@_{i+HQ102DMn#T~+#Kwe$}3Ta!{S4kl|fu^{#Dy=@dDQq`~?w-PhX za}03;x6OUOriaLL{is+(sdkMm&Zgs>e!1IN4UWk45!|cx%!FKFrVSPamF%+$f@>7z z7O(YsS=XF!!?%j?j)Ce~l}pb?b4Rz%p>)o&35un#I7bDE)tDygdbPUwxWQ1+%u}EpZ3&sKUc_#ac)k#!L>T<3I2kwc6L;$#8;#XCMu>`bKHL! z9kmMU8)i=((r}0M2cDxk#Ysm-Nw#q%=vll)ye{lahKm0QALjEu=MoFhA4MKAr_LdM zOkG!bM_S7mR`OnUjpfL@sNiwK(#@)I{Jz=W)Tcz6=>B!!V6&35^?K)r6E@~vwx`4@VNJQY=4Aem&(sKtL>~#^@SIPiaWSDvFiv5gnq1 zFivr<(~q)7(e5*;Y#gDNJ({x*wS}{n7Y5_;#Qf!uH-cPYdP$OKrs!o}kz}M~cY3&N zjBL^Di;7sqib#l$$#6Vyvu2^@gQE?*fKh5bfiGtkYCjUbunH7@q&4gk5r~53)N|;x zQCtp9ML)sw5Ts0izZXSjg9IO8G2F4DT*z+TC-LHvL4ps`iFy9w!LsG)Bc;)b^4Z7a zOx2i3h;py`UEo2@ByFGl5qch{!yJG`bE#Ss-jA0eZy@yXM+py-*6{n;{S=|#5^Xpw zM|hMljxknr5H*tdP~2VH$38B(3(;_o$_5o*;@_3$<$e^6S2U!(mLQeyXK$13R4aT_ zBD9kO&#PDJj@rYKn}tJ6T(nELOtTS76XnWY;tNE5!cd~0*vrO~zDWwHcFIl3U;F~v zVd*L4a>iQO!de?^sayw%;p|Xwiko@Ql}%XL?lO72WJzs{a+|aeu29=UN0LjWBJQ=#CaT+bPxPN+L@XDZbpoE7dOp-a4<| zUp2+>5D}z;s$)=LDz{XMiB_HFAHv0}N3%u}64giwgp{dXgOmHnVgC?qG?Io^)55T6 zE>$2{r?lXrhujeD#oS!}0v#<)C`!-|n9Y~G(r=8+m!CFJ0)MJ@__EA5VNP9>A*g_i>YZQ&2W+|V6i;&48y1bH=axb7|XHaT8D7BPs5(r4B9%yfDI_#yM9 zeti+Z!RkNfeBwPfRHo$!I}EF5LnVCU(8zQ-(3dU}1k12{g692_C4qHx&GyMnGQ8>nL&{o}{LfSynINBF5(Q0DrI}g*D)gdlwr&?}fc2PiM17JKEhZ!A zmhap{sJWI-MlhzwQb*F^|Few194CT-ZukK59KZn$rhW#hz!Mo#zz=klJ=Xjohr{hL zpG&SMP^_Xcv5z61@V)K^jb*@U?$eL%)WeQeZxZ|hj6rAp$aAS}w z?YHRd=!bS3@gpwM_5=eWXl>$pH|e@?fQz){m)j{%b2H{-$`e zQ)hTuH#kDDMOrJ%6`#yJ3|W}F+Qbt!}!4b!Utvd}ZLYz1w$dvamB&(S$3o6MQ*_>ek|Ut@nBzd+=&J&znByTpWNuQbOue4}CO%D@F1j3kN_RtgGa%n!R=lwd zV)nxujD_s+$YQmYlY#o{W5UughxvDT^Kbxb2>+rlc@ZQWL-NB#i=dRjh-h&u^>58^ z$vwt6@D^DntDtC>d^x8r2cw+EADqfibqZVKQ#ECh;_xflzw$`|DE(38Co7G<6KytZ zWPHVrR=sCs;TA{}*k(c;pT#*(Ji%PZ^CuI0+%Pe9F!qYz4DAT~t|*M*fp&{sEIwEz z`N0u_KFJik_MAM$0Kv^vlybQ^H9kd+lkNz=uZ>ew2Z;5r#5+&M{Gg$gW z;!Mqu?~$qFE2Ah@<`%JoHj1^E`;~bvNx}q-l66~@TECTJ6PHx?@Ny*d@^yky(s`g7;b+;y>~0?=9+Wy(HcYuEK0uMG zhC~cdsWe{#&T3BSHk-a+cME1~C3vd9=Y=7T6$(W((jn0d&Mk^i)Jw0X1&O`Ho%EX$ z1ZF6+T^dNHV+Qh&r zV(TumW-G2#59W+lI?4y~Myo0c&j83DN#QA!+`)7}z1kOdY*yI4e@O zt}KjGt@~Cundj2uvTh0P>U&c}VweFGcV7C@us8g-VwW*BaI)HM++m1;ml)lumB>sZ zQyPGdH-h;~vEjxbc@UIGzX?9?^8e*dBCG`U>o0OjrLoHPj6>2o_ zi9Y}{30TQ&$IS$Yl-Yz}AP9GyG~3*dETMqSduxx-SY}sc2xFd^T8d>oGFKOHxn<^} zEI7a0oRu;`L^mhI&6CEN!^1x)CY${N)70b46LmXbeU9zQlKRh%N#g6s;dVWbgPvvk z%p8PGv>C~r_#)dBTs9GB-Hzxc1J=6Q3hEZi7swv^8_SZC->eEtO~FjgG0VuTNqhuw zCi$UgDBy_GN$vv0;jiSE&Hn;RRX5BBbrWk}cod3t_5ZooiSrQwuE*S)s2o>3LxDj# zJ>(fUxx@EeC(L&+5n$3;`>mQA)WP;@NDaN#cB*6<^MI{5|1786+MRis_ug8WyiK^> zaw(1|nPWkQKbE%xcLNcsOP2pD;*En9Z6<+3z=T;z@4G4Na+U@a>TCrX3 zGuRY-pL-1aJZYAzu7*ybIu}&DpdD~LE-7KAJL2Se9S3eXJT79{hvrTky9M%_x$gD{F=iuCcUIAe?A zhjEkd{&v15m^TjzmbLSjq5cR52sUE&u(`thI5qXMsF#pWh!sB}O+?8hf62dKJ7oUU zFO}Eig^XdP#}pz~R)Jo*hSQuiT-DDXoXpS^2|Hr#+TD`!Foix}K4Vsu;j;3VaVlGa zrl~tPt(cdxaoim^nZVAwK^VmP&i_MPNaYB_$!YjrQ61$0DoAXlErxL=dl{U{xzbN8 zXlb)Ng99%>D*&D;^ON$X;A}EMT_TQ)ZO~khZV7Yhgo^4}Ji`pt4+EP?$H%K)vKABU zQV#n%@e%(ecO1E#CE!6Qdnljy4q75!AUH+ejl3ut&8&olijk~Wl^Drd&Wci^Y#>iv zkR~Syl$rMw=R|GE2o+RvGPXtiPChAYfp(ozH_NRTs6Xfr(5d7pstQIIrC4&4`G>0H z)7e$DZOkv6h4gO}H}?gz96z3)!&-?n3EDZMYaa;Tb4``OVg!F!X|m*$U~|D_S-wb> z*&{zGAtqNTsWNPAyE?#!4qUJKpjtU=jqbAMp?)TnPoJ#ZMLWxwE~#ZiF@tz7nJ!im z)4_VduA+o6BA~HQfKvYK0hvoqkI+ot()e ziXLM7SkJ|~5mJsy(q8kOH$tj~Q21+Q;*xnnyj+{VL>!@5l&O#oRbEaGl>Jsuk8Mm8$e+s72!j>z!h6IW3N^<=rYg?*5=rBg;iRFogUSZXQ3hZ61I}QD ztAsTJ*$-8(E3DiNYGv_FzDqM9e~?hEIhi?FY}7iFu1iy=B{R<48YCRBnjgQFijo%K548*MO#chujwQ*s2 zE~psJIi-DD%;W9V)#hyw?AM*oI4ZiPMX(g0NoYSHquxB#qbS=nF+# z4Dr&-*h<4>fdxO=FqoZ1c%c77{X^QQ|4TSe(HI7yJ7`sg5%my8lwor9Pu567EV!Nh z!%$s3nEThD%0mby84hNcM1{t&Nm@yhkr10DA8K41c2>E}_#vP~12N^Q#HdmrTN;T8 zGC$=%#QrwVV=c$uGNY(m;&yWwA&=}eeMgU?@=Vu#u4II1PqmdqS`ij}D3`Zopu8aFoQLc%+t(X8OhN;6% zbZn>85C%I!@iD}Y_Eo6=$j|I?u#40eww+ar=^t!)v_;yPQI0%%jPv&hom1D ze6egu{3uphs$*WurU7rl4l6DLjR89KLLg4D2+sA!iU~+U1CkethIyQfx7c#`3GzvN zzIz;QB{AK_K$^(u&SNk*wa7WEDu#}9=*r$Pb@scUHuhmVD0e0=(6%WZ5G=IKNIWi% zvD#xUNv~TzhV50%wU7cD)l{HYF};3QGfs2^vD{a+o{ws5T*aVb0PlHn7S7P{73WVd zHiRPnk~|(*?QP0(H&D5ccEq)#%*A-`d=AQBgPb#S&T)4-py`EzBD*!QRn%wOAG1T+ zVf`GoU4gSE1}s&_S_GO-@toT0@_GrXZi{G;)F0l!*(`m6l+cl~ZD@41 zJPtlxnLw06TU4XT3&Af{@2Izm$Eoiz(((pruCcbKpVi*t)+DlZuLL8bFB>L`-h__z z5$Znrr<**=VZL992Hq;`7r~K*!e!z_)JIOOWISdQ?U&>$PDorSeLzUVg#A}%E=h#*M`c?$ZOw2X4RUMH)fS)s$^ za0UXbQqX+Qdb^bxPI>MrRXb0SzEQnba4eCbc_)sDey#&aH-+ZwS1X|YCgUX4U-cla z*k}7V#nTe91TMavxR6~TXd@4%jS()VXb4wDTdCjCUhzq~tv*q5j~P&XTKbdK?K5-) zaDXDO0?Z@iu2(7r#Pr3gE|DRTs{Sq65&cn1mk$p`==zjO|4su-{ZWNuTS%SKc+N8N zP5vM5LF#x`FYgYmp1PF(i@uy75&AQ}p)*7v7Ow8On9n{}?UpR&mVw7fukfxHG2{~k zmfU6qMpT#HsN5vUOC+f$%L<}@Y33-1pzYM%cI{aMXDY+FC&Fs&N zWh@*wgt?cR!K-K8BTV5tIHOQc1&6pLb<2f6c<$;NaVGzN5vg`Q6S-G4X<}<|7xc(pwKTN+ldtAx ziYgRfU9@+$y0`F2d6wxZPPi$2Ws+x+{hB`cd#=! z2iOYT^&Jd#B3W+p5CA>lV2g?IQeYf6Nx542y?L!FP;!TFr`jqwB4{`Nv$nQ;R^P8( zE2`HRE9Qz%Y9oqaX@d4mUaoAjZcSz%MFfDROi-QzYvSIibx^Un#~T9I1P23s5tVN* z@C!P_tq1;top%VsMiawz!p8O5)5?6VpUy)P*}PoG2weH@y2nk{f`vd~t(DLoI8rfO zG#5-M`Xq4&kLI42u7ip*rpP(atCRr6J9ui`Le)y7BI1n(LcN0r>l(3MKJP&Z_QY)^ zJQ2U(kc%EB4rzBaBtzE~OB#>EgTxcKg>XEdYQAX>7^D0acvB4}_ym6~Cxj*>xkxN_ zMkeJJO76-KO4U%*}mJH7Rn$hY8*!8@$#FtQmzf9%?(ta>Slg_@>Q8ku!X!;C=$xZt2tb;4|$1`A^AjJ zNFF4+NM4TJpg2f=jtEyBr#ynXG|#CbpACR7r8A$>PR(%WLcUVF)jMid>KDtl*EQ>% zg*zH-*_F+E8!xh)`jcD}^R4POFNWD(`jY>VAqw8LY-KXC--{?_c=`iL1^pmty)2DB z63bU`=*{8ZRrPdF5YOzGp5`-4cgM^*TmhxiN{7YBPP$g@RDGb;U*=lNm}sF_eTwmZ zb5LV1V^Mul(*eVcs`6&spez;flMKTObS=a5tFkHKBYk!{Dn81-ORA6_VCTkK$*-^# z;k%T7*)c&8nt0}%53QTVw7M;Y`ZCE5+Yplep_*Ftr-Lt@UbCltRm+?@tnCYTX+vUL zZr!$~KCSC3FK|z`I+VQNnJGT`zXS?XpX?FBd?TDbP8@IeFY&jOW01$3luPsw=^sYWj}f(K4!bf9Ef*Z9UzYSQprs z-N9Doa&6jAl!%%?w2#PV`B&O1vlh1OZC#YMLA2fUCULRktjR2xlJ_;Xg~uw34L1X? zsaF~NeX@09^^DtGa6Eg%;T|%S4blG9tuDH$_yNR~Y!QC|&X!H#KL+^~XyXN_ca^ke zC$y}FS3V6ctP>a2!@nBXTpMI%(;ChWw4wQRvM1&w$c}BoUJH+ftMEe-ZeRnkSZ?p_ zN{(0lb2&s&+CFwgGz3~}XK3b?!HVhH^a?-mB<AIji^f7znm*^;hAW#tb4-YfU}(}mT{=Vs(Yf zJ#v-K-wvU6K%=y-syS7I6|U;q>ZxL9jYF*q-&XUuZd>DM?fQn&nn60o>}UA|lyg@U zo(27Rikt;dKS4UD9C|DaO}Yi26eq<7AzP)2@LF`0d`}=BTdi{QPQmwT*0}s6?f@lr z3n>e@zh;JFMeRiSOr^f=KhaE8V?#M_mO7#FaKk)}9XGMMNAs)saM>d5J$`v%yY86a zYmNZeEL_6z0DHuOq;1fAX-2FyJWrkzUX1i8Wr0F;m%7Kh2z#Xc=Q@tC0=0I>$ujth z`lswdW2Ahrd{>iNWUJ`mnvL(u$<03+VpKH0pgLcz609vN)9{7Xg_+uVQFl&|u0}E@ z{U^{MU6nKgl*{X5`$7g~NO%_9qjnAyBUiLu-VNATAm23*=R#d}Z-{Nk9W_tdyZNKc zApOE0C|oLgC2(s#D1Y3-slTUqAkExTpICE>!g z!cb|vXmNA7EK9tg9+6i{ddxFWv2=6kZY3oVf7ZJefl!ZO;vSLwQj6> zRm@$WMLRk?3OuRn4phM5poce%tb%U2)?V#j9$A2{1M*E+z}lT zSPh;=0=y|0M4MenG!$EDFUG#%Ka}J68JabcEP+vLC%{{dX}g*>hz9BU*1i-M>exz8 z$qZmXNv-q=FeRUsM}b~h^A+R4LuozAw~#o|pl*U)V&XJU;s3(U>Imd)U<~MuZuZtf zud!vWM&vNQ)ZTzCBUUJw<}2VF@kYKI_?Z7lpo0RMEL)C4Xl;qe5jtHlO$@=##TO;l zU@Y&SEDpY#w|RG6&Kk zRVrUkS|zBJWn@@%wR$Ki40F?5BbNsp*KHx6cqf2sC`Z?c@Gh##{v3LVnj)LqC}jr- z?=?MP7V})18=0(z7T#^fqI#2{iau4|yX88)pr}w(OJliH#LwxfjB8Sa4oe*JanYkxQ)%nam*&KXL%;%E9qr=n4K~squH7&|``SN4r_zD4UJac_SeV^3)bP4_ z5_h6ORDXvzM*pSSpFdkay^L;g)u$I;6mDR@=h}-i+0_{$$z!%DrAOAq+QuJN6fpOr zj;XwuwV`V@V;MR?s~gUgc#i^Y7&r5838%l<|3NG16H>7LXq&laG-kK;ZXVEdu2o!L z+FWJ2Rke)w%v4$WM*tb`6_yD{7{xiuL>mmBIKLzn1}Y_0HpDP2UMjz#UmS%h59t#^ zmFoTM?SKsJF&6du4%}dEUAv%f%pQlas6Ept`A|Q!b6HDN!^BPtGnT}%qqgoecX9jn zDra6~`?yk!f1yoRaIHntdNn6VWZxRe=@OqY%}l;8?KJ+5A1fCaxlw`2M#IU_Ahp~O z8epZR^$Wc20<-iZTsxuNEbHKhJYc>mwD94gTjI5dXUTT{Gh}?(tR@@eO9fk-jS8xO z3JASbqb#0*mDNFcJ=pn%$(bwg{HA@WUHB!QMM5gU7c@j2A_oX>huV^xC7^!|)gTY{ z#OVI2(atozL2G4pa@BzTijm;qvKnz76kjo#Z-Ta0o^9L74gNTet7m)eCz`b1ogb6gpvLwq6$$^8 zEmylV920&}-)|bsi`UHKmNc|#Ynzu;-_Tj{Ez13Mmj%Ki5SZL@J9jNuDXPu51v!hK zrM!XOOZD*=;M4N>C()c-+;}~j`F;SPX-cQEQl$P(aw>af!vWN!XKKEndc$Ac;H%yuxKVA=kiwB=PqaCr;-V;>t$2Per29|uHDf(+Ojeb02JDfa zj^71MSC&UX@Lbim(Ba5B%_jeL^q{WQ^C5N(BwcLr|KM)Bbka;SmClpTZ5bx)rBDlp zHVc$lq7n5cRL)|X>HyU*iC@_i^$jVf@ITE?nJ70_J5N46gVgC2=TepdTGhDt1t6?e zMhT(WnmeHb;ZwRs|1RVw@XPZ*^Nn(+OBh~sOyw(7!sEk;xOr4sd)G0JI@c>}b>Z5YOUAlcC zKcPS1F#qvL8l?98iH?Uqxn!G3vt4#=gg+iG(MT%gy9F<#WeNeeURJCeU3WoVs61B1 zQ533Jb1gO#L<_xDC7O{rCUvdmG3UOfMY})6S&QkG#fRy7fca52zzcAB$So)qI^<8n zQ{Z==zmTtJuuC<@#Y}c9@Qe5}@m5Sx^W=UO|^tU#!a<6=tc3)|X z!bw+BuvQtVdzUj-RSZnx0BRK&mwZ4o6Z{bOP^s3z~IRds-21%cSZ%Q`G2STL<0g7N~ zfA$<@JsigAr~lY zyGM8k87{if;)n$B(}e{{edA^kiA<>V5^q2bRrE+6B0o#~WuwuE{PprI6v*~a7}4G7 zGn7ZM{z+feBeB}JP>l@R6Pc&Ig@=V?0af@`e-H2p5#xCW))V_&05p`$wEK&_Brl0# z1tal$ymc+P_>D$4VJm*MX1?eMe!0R`(hI*?yiS^kzs--5(Zs;4E%K9uYkH87a&5Eo-R{HI~AU)RFQ<;uva0afpPbCg<&ynm4|)Qspibly*#kQu3r&~1Y!NlLo1Sq$@NSy;^|-*#_`G^h3vJYt z%@tlZ4k`*0hZt7oo|JSOax?3s-}P@(Kg#*~iHVrvu0A5>sY=d1hzQfzvE9L9?M*h_ zuLGFL{PdgzRWqAiSj3r8+KbSajGy2s_gwp3u8il}UR?Ku54Rny(hDxN#gfpK=Coq}f^7t^i$XP6xRLcPZ@D%elkp`YYeXZD&| zdBTt_yU2xsAG1OBEV_o-Cmx7jF1pI6iJ>Li8ov=4WpisKgubG^;x=)liYmz?2iIWv z8_AM7V|E{Me#4UVT=I3(#UzMI=2^ySsl@^?BAT`nehj)r&ykGxl`_NS8Sc**K;`3< z&s^08*sNf~z%Roon6JcNH~h^y zMdUSoPPZr4HvdX0CC3PCV{1vHuq+~k8YEsD^w|79Ho;4Z!!OKf~ zhsy;4Nx}G2p?fTc5Q?KByov8pNzhAjf_#_n6v|oU;o(ND)@*T_MTY}TwteV5u)TSw zD5=KW+keGT+smVb><5H8-63~Dv~3NgK<>6Y(r(~hZ^m^wynYSi zbWiwE)oTG#peoM-En7AhJq6bZ|K`C^mbfhQ7xY}REe(X*WiE*~kTCh&*rCWfWkAGm z^swqm&|7S=W{U4N9MQ==_(V1+ar#M)gAKMU`2bzlqEY`4@SDeI7PV;Vw`g;OY;~FT zqiA;7H{BfZj-q)$s^nf?4DePqH1ilZSI$YxgK`yZi96wb${R7?;4A9bh~CH%&Dx-s z<|-NCyBpgAT=&r7bD??8V+j_aZC8*2Oe6SDRVyCOou#%Ef38neUy|Iddadb_o++EI z&6FJ~O4I(6@6Uaq+pRd7X`VwVFQ<(H<5aH_JD~oW;W1aB=h~F;*YINdC=HDGw0AB|A*>q&k+A`FG029tn4Cpm~ys!V%>g~P*GT=R);Cambt6{DjyWy z(416l$~9=Gt2;B@bs~){^#BmBEl*4X&6)g|+0alRFZ=*(12zO5M#3S+cOzN=Z}Tu< zBDAk_5^ls=Y~K+}@XP$u@*35ICPopa_O6Ro4p*B!9mzqCJWgk-Ok!g^0PodrBJaGSX}C=Tn5?{JW@>tC*{mj>!INpxf%qT zobpwxf$bBzbUJuL^k1MADGAR6*P#7_G|+wYgs%y)#kxI6v;l8&uE*98v35cDKhm0a zL9zpS*GNdW!r`?6vW+lS@kqW7K4R`suR=x?v?!M%^*LUuCCGZtUG)ldKuWh}1IkYb z)*eDnMz7O7#^S;Uf|l5UpbV%IFZPwg3-Ko&5b~KooyAxQInJ&C-%4)cMTj?Hv^j~q z3!7ZCRC);Osc4d&#`c!D%P(QK^WQ0M;JvfADj(rN9F6J|UY`=E8Hi7ezp3@aPewQE z%88+2XMmXmH^>%zP8{$pffGq*4+S!hG>iGLzT{&&8Lp(Vn%hO^$sY}c;``*$nsJi% zqj;uZ4IM0jF~VaCzz30XB2bj|I8Kt0DUf5pn6HKiFeo7)7?==w0xQg%g`O6!N84R2(9!T z0?nso9tFryTIx)p7%jD1j=iO&T&a*_45^P6aSa!$`->@qw(Oc@zQL|&m2|)UOdc(J zsMlweD+cQ0)4i3E?3bj!DiOOj?zp;#6-8+@-&vo~e>x%aE)W79GuwS0KsAiPBL+Ur zlsdC$0^@0S1Y1Y{XnNB!wQY3WcH!pMWmS67<<{7;Qt>y_xgsB_qj^)Yk1X5xDf6xz zF&fjhD>fJ{lcpnDn->sulsPS!5PFHV77Yp5%St3Y-gDU=d4rpbJ*7%<^k-jcGprx$ z`+;@5Jo0zhsK(LMxbhn{*QnsiMHSPi!fI7X2?f=r6%3KSjnjuFhsjhAZRvkVkka$;YYw0`K@- zDo^Mh)kDn{$Aw;?{z#Ppx9Dd10q;xnE|tIA7N(zOuOrRm08*P2rVI9M9*=d@5N1lk zi8_lKUwmMFYx!3^r*UlY0en)^iu?}zc5`xeHsQ^ClKz8$1v8Tu5vPUK@xi2nNUXXItQ|~Y28_hSjR}>fUcRWd5fJmFUbRu$}dkp%<&8_c&8=Bjy_3#ni$?`_T zPcWi52I+2T$RCY-5pK?Ugw~5kr!PfMNbuw)EK>R*zAv^$4n*O2sB&1S`F&TlGvF10 zYj7V6(pp#J_Lp1=);YeQ{9xSXIJFL4&22VM#tzhHf(He6tNoyemOtggpp_!mqW@qA zaY5b%c(R1dT4}ahZchgggX~vwJn~PG755r7D3?UZFbDOp(B0T|O>4jle4cKsPk*8m z*yBEe_ztx>^)ufn)n+GGC?;-trQ0JasJjlNiaAw>fa8*+vNd47G_hzJcuAI;M?sbH zysReZzM?)o5|$|8Q_R3RFn?H{_roOdk>xQAT^R{IRV=Rr>H_CyW5 z$;pu%i#FO?k*9H+rUq@a^g~^l_Pp$HRkj(sGrcTM_gkST@&_g>v-9jhAC*s5U+|E6 zMA{>$QT;b*AM{uADNYafXg@{SA&o%qP!2erG^OA5E^xSERGwZI_l;LN$eR_)<51~g+J zk_3Pllp?G&cv{8*?Nj)B>So;*gqyh3Tp!QH8o(wrEiw%< zr-MT7!|_;kKob&&efPeACgZc+y|8>D(MgIo6VGjz5(qiDfuqPrgK9WR8??3}Q~4Ba zE6GxAL3b47sSW76oKj5%=9N*d^~VIMg}VOOvczoN6YOPd7;p*?i}(ZX#oIzwKnL)5 z0kQB|qQQF?a-TTs?v8#X6P-|eG`Yd{7!gXj*Uy(Li1zAriZsGhZqC0F`r=c{4+NWk zO?8@R&wi+0PAud+*O-VsDKE5A;#$HhT^aE)<{pqvj*i#@rjcc zpht2*^?SNc3Z=P6eM{)jUZ7sbwCYZonTI0a3iT+&5Bx;E_P+(&Q9r#gB!wR2J_O~{ zHcnkw8|`N|0AE8V)^$oh>AR~I%P#AS%XY}O>c8s|k z$thzsHSF*NZ*3WKD<()+$*hX71!N2z`~sZG)cP-m&M`6GW$+)y*6j!CPXBS5f@RT< z?7VO(eX?$}G|yC1 z#~;<))*p|)rhTDThMxuo=xu@*gDLDWe-UJ0b>2Sk9@fk43G$S==QINw%*?dQ#Qm7k z2Ey>ZXkX2A!|IZC6)Oy?vPC5u4CxiK3sxAss%GR&GmNU4k)btM)GbR**MDd@kvL5M zxaoJy9{o)oCn80EORyq%y8g4s!|$|Vv}C{MSA&BbaUE?Kt*Ucy*8k9Qt=;vPK(N70 zZ&~`HCW*aMzO$m7-CjAqq?uh&%_}fJYvXgO*j4q`87b_VMvGKCc74;|gtzP#-td^^ z>~2ALL^OL`s12UWz7?PId!)CNalCBw?uwhP@p>2aREGk+olaq0p&tcR*PmpnDu-6T zViK$NR`h2AYLq25j8~m!fin|Oe=o<8Np75#F`TL5Hl=)IM?PSQRCL&91*Uzitz(>k$Nggd=hY8SkS zz9aYb@5R_COTF@$EcJ9(mXT@yI4owygS7Q_W(6|3-iG*5ud2=8lWJ{gfGiJgz&4r#2m{`iz@zO66{N8?U)I8{fd+Zgw-> zZyA{l5#xkP4oMV>uBG5arz9sqN1TwJh^Zup$jc%|k#&mC!3)TvsviI0RJcawC8MV5 z3S7`%=>TFlmrA6Rqk7sN?8wJHp06wNPFAdAGSiWVU+Bs=p?qY=_GS#Qt|*&|M0^s)SJ ziX|4WbWiZWrmIR~9I@}}&hR(5L~|*)4gaR|@b61Z1lqiGq#e}9^)fjJp6BR7S)m1H zYeqLdtQLS4NF6G7Lmy;)O8>wavR6gX@B;beJQ4g)u`jC|5hz!uuSd?Q<|m&(W7Mkqq6i_zB2S-NWeF zXg07voI-DbtAk@P7+U6c19yhkdx^|Ro+GZ$2oQbdSVDT@mNr|->qK4+uKS~LtvCh< zHAhN^0avtw!U8Z$*DtpXJgQrmxf{v{qSKy3w}8vZ!(kB!#)re-A&2NJqzgJ59*g?H zoxuap%Sf%?ZcL9xdo|!On2qZj{2SiOQAF&)-`m_Kjl`qsf7-ji)QTKk2{^RGTq8k4 z!F3=P{FyrrxDLrP3qTokH;sV?z(q;xpe68$xZ7|lk{#fn#hf+gDw$xm>Ql+Y!8wnO=NGsQZ$S#^YX<8lS^ISV$aM; z%LVu`D$zEZSV?tO6{s_)sIo2U8ffZFQ%7+XkPaa@hF)Mr~6 z(L#@@ylUon3@?jS`!Mf|=BSS{m-4=8#LVXG9IY)gjWb<)jR8~c>t-@liQYgh6B7%7 z&Wv^B4)6{AGxQO3n0^-c30^^8_x*%SrB8dlLUDSZ>s73g-sHFyPo;ZoClYS-tV*8p zso`|#5fy7#Smdg9GidV&^%+A-)@6;sFo5Hx&C(xD(dfqLnS{f-d-{Y}YhX3|J(3Sn z?B>uJP!p>S+ys}fQNDYT9Okd*VKkOG>UsooHYY9j;=Py(+fDdG#;Llg?R(Lh^6PDq zl4T{F))!?<3g)+x6^nE3v<6o7WcD(>t67m|Y1&-3BgxeSHQbH!G{thqM2#`^;WdQb zHr^H-4up&cL}@k)y)xT@0VKlvRsiF*vc>t<|%1Px%bf+VYtt zeN1VUpqUQyp}Hbxp>bzzRK_{ugnH}LH%4J&zocHq1nz*i;l@!syQqGKSAz7=8-}w& zQ=q}HQ~cO>jA6N~)?>0^yyCs{FGGWRjeUv1NoTT}t-lA!t8N-473a%~j037ViVqkz z*JKtr8Nj*$IYL8G{fUgZhVaJr)Pn|lZbjmKLvLPG>=*rWfluUD{dr+v=oS4wab94T zev4Uq;iF$Azv;0_KU3D9A6PVWk5R^j@i)z8ZQ>XBNrG*BN~ZLoW`tC-1X%!M^(2y z9x~@NpIms%IUvCP0&^D9TZb@bk!O`RX?;^hnSjpYE-89R`|^6{*U|QTe)dkJp3b(t<0 zGZrke|IB#9I%_Tyi!QHZ$e%n@*(mZV|9H`Ca$n29{4wMfVO};!?iNkr93wA^ucr8EnOeHIGLbp*8)zQF-)H+SRZw~cK`^@7j9RtjAnLvvng~J$n z3Q})9k=}*XR@xCz%fr$QgjV>kD3MT!9rE@PN=ah27lBC|%(Zxlj7~X4T$V3S983IB z+=y);JyoM3r;=srvd~531kKVwhPGM739cQ^#o!_j2x-EZ#W5qUp&5%S7DCJDk>CN<&Z~pW6su3sdxASa}RvJd|%0V zbeCdwp#Z(1)aAZI|Elt{cvz^~CH)wNsXwGRW2ZDn5)j-{yD;Vw&eJiGw)kd15}HU1 z2Ac!p2_Yo$bs|o|q{nA66q)O?ja-5rcMwvx*l(K=)Kns>d^e&{yOmTUlhpSLJ|f#S z({oAWnKn1`8|tn7p5BBqx&_Gx&__U4f)kbq{D`5jx!~G}bJ!n967mmkf+GS);75?L zz5|IsF|o%> z!VC9sIZc?&lMZvpB4V~p6Zw%WFWU{NpdH10XesPeFc7)}8*}EvPVmc2R~SaJ(x$_& zkfq7J5dqpOUWwdBo1^cbrRb4}1oSNCA7aEZvBd$~u>-iD&lNlY-|KN7KT71dTq5d- zOAecee`K{y8@ZgkR~8D+MIRPD0dHWo`4b@KZInWmx+V`6IMyg@!5pkCoMeA;%c(de9CZghr>}~D>cGq zJ=sL@OSc1;iCaZd(3W_gZvzU*{@J_0J)}cMHsno)r5=aYk~v9WS5IJ2H zdoUOV?KwiQ7yB#aKX5vGHqpFm%+87Z4V`8sQEjk*jS3%%^kMr26G#tp)BiS_ z$87cSN53(XJwR*$BXik~XE13F|KV>LYg;#B3jL;R!}#?@ON+&wA4_@)tU6Q5mgj8f zSYELv)3?L7YExQk`@Wi;Nmtsd>(0d6wtZ~)99`Ac!SxSk+q`&4a7XJI!9#y#D=BLB zj%f{+TDW~OU6Jo`nr-5#=Gz4tA88j_t}p`N@-jomo6=duu^pM^ME!i(XV;hIWTeR-wj*iZ3ZQ=QZBdwzZ<-uL1L&DkqW+}b+r+2j} zOa{0+8XqW!IGr@RmX6sq8Hei@Sl&0Rhp4iQ_Lz$4MSt5?SJmaOZS$&e&B<=vUi&KJ zbZbNX=Cp{`agBIVx9I}6Fz%VDjTadmVM-DBhpUahgb~3LjXT7Z{u7L_bc(mu7%hM0 zo?>{fs&V>kn5%hfH_?y`%(WV;{{WYkzGCylcdH8y!mk_jb#Esw5!ox7#%J$JP|tvPc*ER+WF5k!17RUlOa{fb#E{X zP_J~h)*sf5w%e`8z-d<5`gp{pbgrp)-Fh>_bY??jzPmB1aewwj!*gzK2G_8%`9|tZ z1H>05r5UmXf8$mfY=ujshw9&n%fsvRhox4*UHUn)xBm0>pyH7K+44L??ik$4GK$mw^I4s$&3g_j)Ks~%Y=thxxrj{w^X&(+ z4zSj018adUDf!0!Z2nw$l3l>h$eYZT2xewyv$id-IM0}GqPSEkb5Gov_>?&*c^FsE zY?P%%U1g@r*M-M2dZl|X&d{pm{s-8t@sN?FtVQ;C+ zTIb+2>XEL{UrT)hI=tKHQQ#%_iF6?B88pnoNC(^6&3B~)#owDZzbI3XJ z>$ys@O>rzMoTQZ-(jStos;MamxlpZ697yid6vxgaZ)jtq{K(Hb&#<-BD8N2=1QiH6 z`I{N^P?$GN3E&d>4qn)C+_Hu#&XC3fIjjr83Cq;2}v$AEa<9vlliH?54jS)=Ftvh=E~>_6f{XK0v4xOJjytvjnwRk%+X|0M+k#z(P097RE&MV5 zFD^nFV;19Uk@Jxm_-8aJ^Z^lrZVeI=AQtC$hggAK^-dyg;jsI7(vBGIypIHkCHA+? zuICWzcT^p@x9}c%2-%)@7QKWH%HD!LMXNKq(7))4v_i}u{hag{L$Jhna|#Y?iz&n% zuqzQiaRu%aIuGBAD}w@wzW8asjYKjL@9j?D#AbJfI7YfSA0@p=gZ*c6Ci%$5ni@kT z7D|y{`24(DbS!>0n}denzcO6V3c@?}IoeH>B+W&i5?FjTmPRaz{)x>dPDiZ4J`o>7 zEAd>?KJcU2E?wlu;*ZGb-n|KL^0B*$z$ka;qr?NM)?px7N=>jyBhOO%3jQIfRAufv zgimR+t|J{(Cua+?lbV}KB7dl5NzrHxwJz?B`5w0=dMV~a?TV;<;yqmSi62mXq zew+-%h3%g@7LhZ56E7o|na6Px&?M$ybUwPASrRcE8^O>aCoza=3oT}Lan zrkT2&stzREk8i6v6~CnOTit^gw@zN;kcinG7r7ZB|2k55(*m>Fw+a6F0_|ZUgJ*l& zda1i>dz+*Dp`)@DR~@!ZHl5QR8u!AK5AH2WpCBzI@}0Xjl(%Gm9{;0&fO6N^TSKVHGhdpF*>DmeRSCtdwFqpRah?fJB% z4q^R*q(QU~AVrbv0T=S|}=rPTG4 zu}rs=Q&XE1E86{ca6uF67)c9pjx&U1AamGLouH z<68E`_ck6CB}T6_YQ?7_@{KN1Zpb6UMR~u#YJ*;}(|400MumBP(!bXfyE*84bcs&B z`U)`D&PhK2)>!^wPoeV*j+oXqOLF&_5_zYyHX5%A{5jK%V9WSap3zhIGRe_!Uz`?q z-Y`e9IGQq)$%aP+8HUQ)kaPND%3gs5`c~C=-}U-JjkD)hy^Z#$n-BX4V4PCe6;PdB z8mmMKtwPvzEUQ3dOcu<@Z81D;xsg?Gm?*O5WEfJ#m8rIdUXsa)cl0Nvr{bpSr^x$9 z7wBsg`4NNkuF5$fTiDmCkAaEoc1@XY4{Ox!_x!|G0MTwitUq|vsgxZEOYQi~bJW9% z&s@Tu74$Vz`EKR*(@z%v%CgWGN^R5M>c`0c#If%&4eNs%#Tl2Bp{vtRW!_Aj$i{W$6vGg)&Zyqh7k7eewF zq3&Y98z$S#d{#5o;6Km%^lRA1Erz~}R6AkxBXpwO3i>^E#_BjTm>81p&j^*ZIpIu* zDj_q1>94j+_oZK}Kc)<$?`ifYJfN>?+1NGoIbB8+LLUc4hiB0Tf%_pt=w0BJfbH}e zXqImwt%s+0UZg9L8E%Pm7COsmB3*&auscb^_;jmp^hRQ8-Y&XIGa>sJ?W4`gI7<)F z{!2Sf{nKqoUQb&96$x#0f8cX$H9ZuZ5*0v?hN8lI(YDa-;4`!fYzi>Yj!2qs0QDEO z^gK-6KtH*qQm3(FPK&Agc(>gvdL&U{?MXL~HhI|;4_KL9L*;={83HQ7ymug=qRga} z5-I^Yoe)A5z?rdwC?0$`@-YP>DPeo5@yPz*ZfYKy9#BfnM{oEJrP?rq=N3wVN4cd@ zE%-gBO%y>u_JgUd#1#M6A2k=?+=ah5!dir1DdHfRnH|`iQkp33EgGiy@MXV)Y`f=!DViSEKXfpAXKH!g& zed&EZTyiXZ#50j}rO&txCS&OHP6tUoebin;E}_@i3?<*u?RmvhYKrD(r_mFKxcn(n!jKBbg|rf zZQD!fCJ&poD8)z@NvlC+beL;8ueG+>VzL5`j+N?eS*5%L&R48p>5Noj^2A( z|C1+r1hsOMfJ?h+p8BxEA>(&lfXyRg3AldTRl{S%J6F)siHVly6}-ofG9#5lL_66}rm+8!)ag&uEPAr1^`Z*!9T@(*jBAiL{*%=ATZ z*(1eNt-9y3)ObMi&*7=L7AM*cH_V2nS`IfvB9C*2cCKl7k>%Lw+@$9CcTD5{ObzN7 z#$%H_+uH^G<43m-Y-x@D($*>(5plV#zj$frx>l1kH3)0%XTIwNnOMaN?^DKKDwRip zQLD*u*=6`o7w6c|pagSlqxJ7$wPmJ$7CJ3wS4SN8N#@1&1H7{IN9`Q`p_JEcr(1#& zpSP6>XU5%Vy(jt|eXvy|5k#yseU#n|?KWZZ${?|+ui~D+r?E?=HlH$1Gt{HPFkidP z<&?o603BWQ2cRwu-zn9b1FM)#)*&Ym)YqToWnXl2~l=l--CcFrJft zi{=;srFBGt(Lt3O8f`ePRt0$)+wrvcF(<$70elFK9ZB=ps>m~BNQ zwyW4e^rhu1HVALYo@>gKR%Glo{+3D7ZW}ksvE&cN7KJh4x6x5)iv4G}t|Fp-8z!ou z@Slb(4HWXr&_|00zSr;6wfUXbHvx;iXR$BAGaiMk7VhQphV@2b93{+e6tF$d+`_h6 zIWed4582^HTSa?DmEn%kJa{rJP_-maHSpAV2}=#W8n4(j`j49LQ5*ETwWq?j==HiK zAv^U203K-00f6;>3)o{&p*P0n!Id5y<_aQo`OM5jHI59!V36%+MvbFZrHq1rvQ8QL zs5f#x>aS^_G;94zZESLsp3r_z$k&(X*2dQ8%~O{sKKmCO7_MM1gF8cDb~Pjk#8?t` z^HZ_$@LTVC<}h->BZkRCcf0&x9I@Gs(-UO=wl?5`JJ_bws5@J(XeA`IXlE`;Uw5T z@aOpX%x9!2W(RW*IURYPIf6!oJ!96Pi-UhNv#`N|!x;(#{j8W$?31?>{T6Tcu%!)z zyUP!{iCE*fmFAHycGa|m9B=iOhRF|^T}-XnKzD$tNB5g2_>I`$Bqyc`E05I}8XZVGjk zI&ED{J*FOJtfS2l&9v8M)AZ^TFFJy{nb=5&Q$OM+nGMlHqW9CWv`xe-I+1n>8%rnA zp26|v&)xyebOP;bwmgT^AzmA(_jHl_WoiMfbGc8kH0^kvGSQgbT519-QC??DvgAKU?AZB?X!Kb|AF^iGY7NahyxaDfbZO& z>-^R8_s$EQmpiX>{_T0U^T_id=Tpv?oNqZla{lxA?^Nfgu2S8j5~7l$(xG}w8}sokiN)FIR<)Fsp{)FafNslU^lqq$0R zk4A__jz)*(EsYxuk|u;Eg{Fk2g=U22GtGC}bF^1!@6ig;%F*i3zNK}eMbd`QrqGtq zw$P5yey06ScaH8V-90)XIypKWy0>(0bV#}ox)iz+x)!<-y3cgq>Ce$$rN2inL@!6L zL;sfEjUGuKLZ3okLf=9^LjRfmJHt7Ks|@!Tgc#%)bQs<;xG^9ZLK#vSN*P)iMj5^^ z>@%KcWM;h2D9k9&sLN>0=*|c-hBBrymNK?7jxv5>+`n-C0`rCY7lbd!U(mf^eZl<# zcp>yc>V?t^trtcwe7UgCbe@Ts={}P%lRT3ylQok&6UY?Gl*&}f)XFr<^o42v;`xir z7w=ybz9@fD_oDSh_lw}g(2J=TOE0!w9KHDE;{K)cmzXczza)G~{*vw`>r3vJz)PW* zQZJQWYP~di>C2`4%jYjMU%r1?__F+E-OJXO-7kZeLocUZF1_4(dGzv^%llW(Utzv- z|BCPx`763ttgpCV0k4E!Nxf2frS;0_l`mKJub#ikeD(fS;j8jjb+1}qb-xN;4ZWIr zwe)K1)zPb8uI@9RXJ%%;&n(O=&#cRA&Fsz$GKVs!GM6&9GLJHUVcuss&%(@dpGBBO zo<*0%n#G+3WC>+SWhrH8Wf^7p!m@wu{59rl_pb?GlfR~W&H9@AHSk*KwbW~+*IKWQ zUi)%wpY=Q|GwXd;VODuoT~=#WcUF)!lr@#Ll(m(0l=TbiKHGUVX14om!ff(vx@^{L z?rb1iC|fF9DO)SsDBBmd{p;tiGhe@dUHH2Eb=~XM*WIs!*F&$TUN61gdVTczm+SjC z&fj3ZasP(!4fz|oH>_{C-vDof-blSsdZYEm=#4Ko_HUlQ$$azvP2rpJH+64X-*mqT z-VD8&db9Lq>&?-dUvBQRpJ!)gzt1krF3+yZZq4q_4zh=`r?Ho@x3Q10FS8%qqPoR$ zi|dxityj17ZrR-OxP`hEb}Q{x*{!x)W4D%X9dJ-_uyAm3h;Y2((BrV-`0v1mBa9=B zql}}CV~k^&t? z8N0K5=in~YU6#9CcSY{Lx~q5B=B~$G)ZMVVX?M%+w%r}OyL|WH9@RaTdtCQK?!CIF zchBaY$34`&uzP9u%I>w@8@soB@8CYweU|%N_eJi%y03TN=Dx>$)cvsgY4^+Sx7{DR zzkL6Ii;9bdi;GKy>lK$CmkpN(7m6#4D~+p+tBq@nYnkid0o4PR2V4(C9=v*>_rT_X z#{<-Zum@=m${w^m7<;h%;DDQon}wT;TZH=+w;s0*w+AM_i9Y9=&>`_sHgv$0O9Eut#Z+${w{n8hf<-=-@HcW0uEU zk3}B8daU=@=CQ|P)Z?(nX^+buw>=(vy!`m!3DpypCtOcNp1gXZ_r&Ij#}m|(uqSCx z%AT}68GEw)yeqsv_^A1=@jc*s#;3ri&u7c$$%p0(=S$}+=WFL1 z=Ud_X!B5S9jsF4vGkyhreSTYhPkuCiIDa~SIe$CUyep>m;ip>d%Vp&!E3!q%eF7P%(!K;)T-f{4C|t%#=xS|nT~U8G#3U1VHjMdZgb>Sx!UJ$Ux) znZh&uXSUBgpP`?HKTCgB{;d7k__LK~Kb})RzxMpW^JmW$p6frieeU@j{XG16`t$PV z?a#-buRQ|4RY%P7hi$h?#BlEKJC$YjV=$aKg|$gIj7%F@WP%5uv-msONC zkbNiXC5w@bkj;>-knND2kX@BMd`a_?^(FVq=Pwms8oYe>((5JWWyH&jmlZENUQWDR zeR(KHBgZPoE%#hbQO-c_ot&2(MlM1wL#{%uLvBKDRqjxpMxIrkTmHGcqP&6pJ9#g8 zjC_QAhJ1y5hx~;6s{G+Anpdo^xL-YgrTEI=)w@?-uQ0D7US+(hc-8T0;??S_Lj@WI zRt0W_=L(7n1`6*Kyc94B5egX!6$%{+6AG&ehl(_ctcu)<&lMFF4HVxgdMRQQBNQ_f zD-=5vClpr|50z+?Se3Yyo+~LT87RF|@>0SmMJQz`RVZ~RO(?A@9V*i(vnq2dKUY>% zHc)=2?4^uRj!@1}u2Ak!o={#@K2)JmVO8N)d9I?UVxaO)#Y+XF5}}fzQlZkJGNH1n za;Qq9%Bsq(`dn2})j;)~s+TH8H9|E*wL-N+bwYJj^-zsQja7|X?YWwwnt|FoH7_-c zT7+7LT7_DN+JxGw+Mzm)I;%Rj`g3(fbp!Qx>R##?^$7J0^$PV4^$GP=^+OFB4OR_q zjprJQ8U`BgG`uu08W9>98WkEH8WS3;8i$%Rnyi}Kn$I;AH4QZ1X?kg5G$S-KG%GYa zG$%FJG>^1swb-e%Uc>tJ;vbux7-bvkt>b=Gu_bZK?jba`}N=ql+N>e}gg>tb~y zbu)De=ae>tXdG^)mG;^*Z$?_15%`^lA0k^m+7O z=qu?P>f7mi>tppJ^)vM=^*i+^_1E-|3}_A540sG)7$_MS8rT_l8($v{vs-Rhy0sFkvnk(Irbj}^`; z$|}pM%Bst1%4*%}=Nq~=*WWyRBl<@9jnNzXH$HE0Z=&90y{UTB^=9hL`kS9`>E2#{ z`|z#kTjjS#Z|&duyv4nZdYkpO>TTECskiHIe_GR7U$=f}Eo!Z7ZDeh4?PHCzj3BYS&$ zAA6jAlzoNH>PX{`O>kbbcL>-hJj2!G8d>n8NQ4U!SRSsPaQx5A6Ki|{6 zzyAK=d(rpG?~UHuzxR2Mdmr^a>pz96>;2UG_4hv==^U>+K6DgyRCY9Sw0HDz#5qPe zW;s?lb~#Qtt~>s8qI0_L^w3GvN!iKB$==Dw3Fj2$l;u?A)a5kgwC?ot1Ko$~A0B=X z{h<88=!5+SpAWbXQ6I8CRDJ0BF!f>m!%t^A=j+Z7okg9MosFFBoqe2f&QZ=;&Q;D` z&Qs3o&Ocq~T&}x3bP;t?b}@3Xckyw-xkR~Sxm3AyxlFmNyZm&ebG`2R&{fn`+11F^ z-qpty=Njdjt zFE@I(8*Y!>#N1TejNKgEeBJPF(Qesp)o$Hx({3AXzuf8FZ@52l7jsu}H+FY$_jSj+ zN4sacSG#w+PrGlp|MH;sxZ&~0L(D_P!}vdm+1CT_5$%!fQSH&~G3~M8@ynCm^M>am zPccswPh(F9PhU^GXS8RwXZ3%@+G)=X&tG2jUN^iRd5L+cco};+c=>way`sIcy{f&s zy{5f3yncDpd*ASWY~O0%Zr^F&4c}jW z^nN$|9{Gv+srVWDIr#bd;r*ihvi++4y8Wj8HvE41)BE4>f8;Obui|g)@8Iw2kN1!E z&-SnO@AjYe-|+thp@-anJc5WpR3OF>2Z%2O4~d3kL#iR&kZH&U8V${cRztg?)6k9o3P<#?8?Z+(F_;R>80G-;h2de*uxwa0tQ$5B+kpLo z)5CATAHl`oDsW@C1KbymheyM+;nnbN_%wV2{tKW7Zv3}MivcQtG2j6B0(c<$zjts| z1Kq$humSu+&?9aj9wEdKDhOkQ1Hu=9M?@pC5!Hxp#PomvefWiBK;A??Mv5aNMd=L781TY570c*e>a0c82f1?;sH&KsK z;wV*=3F2gPuWe zqJLu;FgG!eG2$3ij0xsF#t%cl#9(qTHJBdE3}zGa8_R&biG7R}$EspYu6{l+ukZ{i>0#qp|m z6a0I;AD)1Z!RO#>@ICk${3iZ4fq`(7@R%S@P$ifU-V^)?1VRiUhfqW4Azp@MhrSK=DA;K$F1tfqsF6z?i_Cz?#6Gz?s0!z~4a( zK{taQ2Z;x%2AKrC5Aq8l1jPj91l0uf1kD6(2K^3Z2)-HoI9NPbHP|HheXw6JAvh*D zC%7iKCwL}!Gx&E1L&(jL$06b&sv#yJ??e1T2q7^cIUzM6Js~q8n<2kL8A91ZpM*+; zs)d?{I)?g(5<_D{b3<%ZRU^@h!c zZH4^_XAEZ#e-bVct`=?@?ilVLP7IF?&ke5)?+u>~-wOW|!5G0F@gzbbLM`IIR9%FB z1Ti8uA~&KoqBmkTVk_cLBx59d#`ea}#%{&_iDQgok9!g)5vLYs8s`}2A4iOfjmwRzjq8n@joXU* z6VDjW9{(g>B3>=tG~O}ZKb{yL8=o6r8{Zp08^0C*CxJ16J>f}$M1op^X@X;de*!Tf zHX%2mHla6RHeoB_PaA?8#4(C6d*WO_Lpy{ga8wvB|l~waLB7 zv&mb@e^MAz*i)XQNTjHxn5HdCO{-iRdvZp>tl}J@fHBEI) z^-m?H#-`?`)~5ER&Zcgq{z+p@V^4dUCYh$5W|roZ21z5O#iiw?)ur{N&82-!J5Ill zek=WHx@5X~x>>qYIwYNx9+#e%UYFjNK9~MA{W#-7#;uH}8Il?58D<$y8ITN8MqEZ- zMqNf<#$3kNjN{A;nYS{ZW=dwNXPRX?WkNDZnQ@tUnRS_cnRA(6Gmo<_WZlYonkAW~ zo@JKhlm*EmWyNLXWz}W%WzA)M%{tD$kbNurX|`mxdbU}%Q#K@N#dPPC1YqQche>UQS(3U(Q_4*PP?r3%R#)pXN&Ds^^;JI^{xg zNx5;kdAW7DeYtbFUvrQ1F67l4o_2tdwea$=0zmR_` z|7pHtzIwh{zEeIVpOhb$pO;^k-)4@1}P(z#g*li)s^*?&6RyEJ1)OaeyjXx zxm3AExq11Aa%efZJia`?yuQ4@e7=0U{BH$Q1xE#Mg;a$`g?Yt?3TOqnBEBNOqQ0WP zV!mR#;%_BWB}XN1rBtOxrFrFtN@yjyGQKjuvc9sva=voA@^2MW6-O0sl~k2Rm3h^N zDrgnCD!wYes=lhfYQAc_>TfkuHAgjXwN$l6wR!c2YG^gNI=(u;y1u%)YTB%x%TJzcuwa{8}ZG3Hh zZGCNj?R@Qa?cX}4I*vNtI;lF1I`g^@beSLj@{e1m){oe+r295^a2B`*(2J?mw4bTR1LwrMiLw!Sk!+gVb z!{0`xMvg|_MyW=PM)SrGjnGDNV|-(NV|`mtwN2W>)$q}HjXylHmNp^HuJXs0vB!Mw)nREw)(dIw)wX0w!iI6?HujA?NaR;?dI(t z+M(^__W1Vv_WJhz_WAbh_P-rW9UL9J9a0^d9TpwV9k7moj)abaj)snbj)jh$j+4%d zowqyrI;A@`J1sh$J7Jvxoe7-oeP~iohMxvyKZ;!bxC(=c3E^ecfq;>x)Qny zx*EC$x)!>2x=y+;cHi#i>z3};?6&B3?uK;-bSHEdbT@PlbT4%8bf5HG?77{;*CXAd z*<;b;+ymtGjPXEck#ev%cd;`)0ngbRC&I7Q4fPsX8f`NvCfq{jAoq?0Vi-WfZ z`39v2H3uyQod;oq0fPyH1%nNP1A_~LJA)@f7l&>S@eN52X%1NoIS;{x0)`TX3Wgen z28I@fc7{%dFAm=x<{OqC)*QANb{>We2Mi|+7YsKH4-791?+l-eTpYPQ!Z#v4qB&wQ z;yeNy2^dKjDHv%O85mg@*%>(*y*PS%ly6jeRCCl~)Oi#(8ZeqLS}@u$IxxC0x-)t* zc5&?X7~h!mnC6(pnDZEHEMP2QtYEBRY+!6*Y-j9b{NniSalUcsam{gyap!T^c))nV zc)@tX_`vwW_|Evr#Kno*6MPfW6Pgni6V4N`iGYcOiGqoSiGhiQiJgg)$%~V>C;29& zCp9N6CtW7tlYx_olZBIwlY^6sle?3rQ7Q-xEFQ-f2B zQ@c~A)0d_>r}?L4rnROmr(LGu(}B~8(}mNG(}UBC)4S8BGnZyKXZUAiX0&E3XIy6B zGl4USGlesaGlMgWGrKdVvzKN$XZdGkX0>K5XI*CDvw^dTvxT#bvxBpXv%9mWbC>2g z=lJJj=CtN4=UnFCbAfY-bA@w_bAxk>bGvh=^Oxp1=lSPl=C$T6=UwLE^MUh;^M&(` z^Mmt?^Skq>3zrr+7x))s7PJ;D7hD$L3xNxX3xx}f3xf-b3%d)ai`OS?;_ zpDumk{KWrB=9AVZ%TF$!;GY6NC4MUW)c9%e)8ePyPp6+Redhek|5@g<)@RGlE}!9_ z13xEzF8tj1dGPb%=iSezUoL&&{KEf5=8M)B%P%fp;9mm2Bz`IU()eZY%i@>aFQ?0w zmN}RCmt~f@^XH-L?xjiimDji!yEjirsfjkC?mn|C$^Hf1-pH?1~ZH-XKd z&7{qu&8E$v&85w~&9kk`TX(huwq&=ox2(2Yw}7pnt)#7@t){J^t);EKt+TI}zux&O z@KyG!_E)Q~u3v$#L0^-;7JY5{I`nnv>)zM1?aSMDwgt9jx3#yewq3V@?V#<@ep+g1=w>e*OE+ z?~lI`zk`1#|1SRB{CoKKr{CXx|NC?0&)q+Qe_sB1{pZb}kADzJFdPZ6iVr^%$5j!AI}hH z!Dq>5#b?cD!)Kq)zMcL1cje#Re}eyB{(JrJ&A*TT5dVVzCI2h_*Zgnz-=}}y{{5ru z|CIfovj0=|f6D$(+5ai~KV|=??EjSgpR)f`_J7L$Puc$|`#)v>r|kcf{hzY`Q}%z# z{!iKeDf>TV|EKK#l>MKw|5NsV%KlH;|0(-FW&fw#|5NV&Dfj=B`+v&)Kjr?Pa{o`c z|EJvlQ||vM_y3gpf6Dzo<^G>?|4+I9r`-SlFS`H#xBTy)nu5X`9lh7eYMM&tSe`mk ziU5=%0Hp{(DFRT60F)vCr3gSN0#J$olp+A72tX+UP>KNmbBh2j_Ff|Ybpg)NP&1!9 zcmDr=^#45mKc8Ryj}Z9pah2i-P#gh@BS3KkD2@Qd5ui8%6i0yK2v8gWiX%XA1SpOG z#Sx%50u)Dp;s{V20g59)aRexi0L2lYI06($fZ_;H907_WKyd^pjsV3Gpf~~)M}X=c zl@OI2l@8TgDmN-5RR~oIRS8uK)dB*OYKIDqz<7@p)R3r zp&p_BO#Pka9L-gldo)5cax^+LZ)x0UkTfAQDKsTCEi@xEpJ~3+o};}=dyiI#R*qJO z_ARX&Es{2bHifo?wuN?t_A~8wx^r|_>F&`9(aF*2(7mN|qeIe#(529o(6!Kw(0!)+ zPJfR6D*ZirA$mD_9s0NQZuCg{5c(AQ68aYU5&F;c-xdT{1So2NKSd3or~wo;fT9La)BuVaKv4rIY5+wIpr`>9HGrZ9{LfVbDE)ti zdzAh^rTR@Z|4-@vQ~Lju{y(MvPwD?t`u~*vKc)Xq>Hkyu|CIherTR@Z|4-@vQ~Lju{y(MvPwD?t`u~*vKc)ZwKfC|`?;r9U^4y^jVgUi!EJQQ{ zXNz=*3?O{e2@wj2cFrM?@TEEdgf~2+NDJWwk4)3~?>R8);(yOXq7K3b9ss+Az`<)= zVTcsq{96>F2GG~Lh8PB#6ciC#2$kmwNNU9LKVyU}miVI-aR<}6$%LTAFfP~wM`-hr zeqa|>*C`2XpzhZO0V`liVJWZ*N~hKXUy&=30C0#*BlIH9BeBpp1Sitll?ow;bb5OU z@doLjn}&cO{a;NYB9Q6NrV)k6jk8Q3fPC@CdBBy#uwez56EDs;00sok5pF;qFWnId z7~tO3>;Q(?)WU0k3FbJJ8?eLJMtlQ&(VqzNKoHsk$_`YbSzYkJGOFWE41yko)D=eX zpj2KtBIHolpSd8+!OhbR_;yIZfiiqDSb04Y-W|j+PX})cm>>3lx074iH{so+@)||> zAhEi@13pCU?mk;Kvj)rRne#`HJ zn+3EbZ^1v3GsD~A0i0J#L;A%r;x0sQzRt0LenE?>tGz+!9VV}LA7uSgEi zj-EZSgS91ye(!_T#5t`A!OCK?W}{)nQ9p(lU}cf|Z75h%ct_P5Y&c9IpA+^aWHk8| z>^RspoEv^C=o&5SVaK1|$!$wl*reu6nf ziejx`apBMX9ARHWpM1CkR|vjosrTR4I(>Z`-b9*~L&J9o*}~PpO`PX31Jtr;XwMaD zT_ChP1+~wOnSKRz$-dm*0EJ{kx5z_7(*!HZpoPgJIj7M6ME^ud==XSmP%+r;m}N{0 z%peNqI|a*(xbK9Ap9?Lqn1iba^J`bYu>p~C{P0@huCNz;8!!Fm8I-Y7bTP!L-nw2aT5(u41h``*3U5j1v zRe;$?b2^=ewMJ-K*ubxZzSY)-zYa2ZIrrc9OGx-RybXW$TOHC;AGK2jX|Hkr!VT%F z)R~Nd^p%PCaX_YvpEeaj4htTZ^FrBkA7$g9FSDM;PePs3Uj(m1Q&S|-Sm>w3r#|H{ z;ke6=0E`^9VEzp@8&1^Pf!_(a@R9_#3UCto4NoA{{j!9FwJvW@K%$#mKWjr0>&_+y zAeq%My~dD=3faaz$Y{y;G7rdMVP@7O^j4l}JRejwnzte(ZCp?X1FiWq&+b*RnQBL3%2YrJaz?(r;M;(2GSTae+|DypEt!sB1Pi$_|>BZtqqM(y-z1XR^m|mY^yj0^E?qa})qU4RL(sh7fyqmg*s)on7O4koeZB?mb9; zV@E?Uq^)+h^bX{66?r3-;kP_x1m@C`I1SIVmmI+#)F&<(qiLT&aDrW5~I zGaVKh#U|?y8w;5hx&vn=qYn8X|-$f%x*zn5eH%LssMOQc^y=$jl4pQ3| zQoIP6Y!c2ehaA3`o+jYIck~%@KDY2ZKNF(Ui8It1zMV$?m!3K$PhE_h22d`(`1F2#mvf_ z!J@-f1*>6G0dv3lF?fXf4jLl}jDL|r&jNOnKhb!AzOM-_39o1lLw$qWRj8o~;LmgV zQ0{QSL;;jETqINjbr~*-`2nuO<@_Fi9dIY79WV=CWN8lK;AgLmKs(^0+&-v?pb@bE zxez74n9*IBwCzE(2m0G*GMWpmIH``BMrHP0M@67mns!kJAhtXQ#Q|Q;5d^o8bqR4` zJ@R8n6Bve+z_@~*$a}uopgEG+$q!UQ(p&6-d`MdDZ{QW=ZMk6NH>8p98FC0&`qKo> zM*O?Yj_M*ZeVRu>30xCNC^5WZuM2pL^Jr29+psz1bYKYPAUhmBCo_vB2`H{ ze|Nlv-~1W)LOSp9)r&jtYI=C+2;V1-iUP=cH_?rE%)&=OwbS+W? z^3#^n{sBoTCec|yMB-GyWgsyAHIfzxj;Zzx1oEP;*_Q(Y;crY4z(Pp4#w_q9Fkfa0 zSS1w*rUUEvwC`dF@7i-~%m}f{li6M1U)kx<05DQQ-(C&m7T&2T1h9E83xfccY`ZiK zz&3*v^%t;Ats=Vsrb%;%kAQjnwkI{<6|-kA3PeUOnlb~qVTBqnpgh=6h78n^mjpim zEd=r7SL=Ic9{}?;;1DBFSSjE32f&qGu08~;i#7`80oDB0)MVggPHdDh zAd%@!8UsYrRDsWccrvvI63~h-vTFsLV&qNo03f1Bg9jjmY|AJD;ej-QFMtFh(;gNf z-N~`yfuL>mn0W(?HI5Ey0U32NZAt*Fs<~PTFe*1F5C)`*w^L34!2(ER1Mn!9i6jqj zWd;K#z=PCZ9<;!-By~GOKs^p=q6wHs1*uO0_F?a3r~vn%TY~2S2(f0j6!EN|b~y() z>pGcE1_s(r2cm)GX4ck7z^7ibDjv|TPRkrCXNJ>XX37PP-qIK8;{4~$PtqlGzGv^&Y zK%FvZtPL>4_p1E`E=2`PYXYpHyaFx&2f1v!0HHD$^f?|uH+69`5a=5v_u+xa!As4d z!26!4iabEJgD1BfxZ7Ni6c1dg=MMLW|Er27+=Krt{RXpv9~82=DZ%%1m~4mP-_s_I zPT}7YebuVq`_XIC%o19&t$f!~044}HVGh5L4};g;Yc zwJNx`u!AB?>=vv)T^0+4^~6wPufxUy(3nctGBN~X3j6K#7()f;aWF?0!X3HzOPBf_9M3o z8?aVLOj;)9H&QCv9TS5%CI7+5B1Vvx(VK{7&r)X@~*Y z*CDsh;Q)yg`%} zItv$1T0|lR~@l3O0VIdO(Dpk(}|<%ancIntko!9-}bEMjhl%~#z=H-!4;YocvJ?xm=sF9%IWilCYT6aRai^bT+Y zE~6yK&)p|cOvE!gaqttN-{d8jhfh);0qt@AGF0FVtcf5MvI8TrZHVR6$3gkR4RG<|}#$NgwiDk?%`ZR(GPm`Wx!f>TL z=~G76B=Izp&_)R_Dkf3KaRzw|sLU96G7HKmDnEh|bs=Jv&UT3&!=)#+(Tt09p{>$^b_Wj-lOQ_8p9pHmY7_^BM=o82J-`TBb?nVK!Gp`+a>UF z@QE=aaxJi2tqoa1wvfJu^dKAyXd?x2VO!J~%M#?05_+p}ZXAz>aWnpjcg zSv=+TD6{nPoOBdps!C!V*pf6K)(XNCeDP#ZA?^}%17wR1|2T#G5y@n0hnxuWHcCPk z2KTDHM?wQ>q^6MyBtC(gNP4{5rUZto%6Ab!7nVnjwWDQ9YI_(_lZ9)I#wefsJ7t9^ z&K&FPJ#Z*9JMkh&NZSp&4XUO{;J$-b6X8%}P9DH&u)(qc7DgZU)3&_qTtg){0QHKfmoTb{+gP%!m5d+uT7!MRkVOen&lTg%n$ZQ;j|uZ6LDtV;nX3qS82661iV0iIGH> z7t;GpA^mdlTv(9u84ut7L|#gU8=w%gaRn;Lh>XZ=Nn3c%&KxI>=VKn1$O#}`fl^5EWM zKIE_P2i<#QVc2q`D+vWFDLW$0!O%H2LejX015NW-1qi74ViJ(BR5a9nzW zph|d<6^XCJo5yeBAK@}Wxp7I@W88flJJteHijBf_yG~-+F=93`n0R!#(K*Zw^efeU zG#Rxlg+U#o0{Pcb4k($`3^H>V!<;C|JfwTLo0uCM)M-RK7i3-cm0%sHSc1X#2Z&_R z;HAg{@fNsJl0--{j+dy5O~>XDJRnk7etfj61EvbsZ1W5wkNswtj_$znsV1SNF}_k# zsA6;Unanrr2d6clYf8$_ImTpWHxd>G--(MUjqoz@xQS3)z3f8cdOOf&s(?ZNDE zlQ^lswU9g5>VPKfQ>+9z(Z2`NMuNEf#TXF{tOwEC1YtvEv=9EKss`!^$0+#&<&34` zcLJBtrz`5Dn+Yv5o5XkVj6+yLMXYJZUBcz)%Gw6JSJb^?E8J>CbjAkGESx<~9XlDC z5gdor50SKo~=N*2nG$S3It{=r-Doq|5tfG=Z2 ztsKK?e?ofJmBG9Cv-FX+R{Y!4_^AO8syyIKMGqxM>XGCDy9oroeo!5!_=TBBUG!|K zj$o2sGGK!*%@t{5z_VtxRF~pV85V_>*xzZiX-8P^^kTw8UrDrQ ztnY_pG;>tln;cYW_*Z=^lt$=z<$Ul{;7^G((3LdC=ZrkTr+f+{q}P^BzQq5kD(&0B z>z0?dMB^$-8md%r4+=-}PqFcNyQ#@o_8g|@2biSHM*#_#Cuu6+BDyx&%BK>oo$%(v zL)6#U*Kedz_^7A)=TY~=R+azzttQx8;vJ|;Ugvv>oFS+zjS+&IEhn(}^#+*fatq5&WWGy;_jKjKWh_j3dcn^Sy$-c zXzKFvZeabYFDLh54$3_uZ7`n2oumWwp8__dHX5Cy=dFaklHuaGfJ#nrw7QKFPEgWg z1lyyJl>$J6a6r5cxfV3f+lcffaWB*pz@f!4Z~Sy$Z;v2esH>rI8yD7AS6+lYZ5qz? z!@AcUC5vFbR&(#;6_}Q5*^1&w64tE;b7RAl zG(ho)HgPPnC%B(C9BD#oS>Pbx#@$9o@iW6(-9)@VKX0QVF0lJT`33B+w!NG=tW(oW zQaom*t~TNo#=43`{DNLB(?@8bKNQh;nV}AHNsde?XvV6g1By1~rtU{DB%Vi!3FM5T z64ycIht%*oA|=RXpB{zr!8s?XLLb8h`xZi;!iX)E!D29@s)!&xm`H(tpdXAaO(`G) zMjgFQ9*5Bdx{(=RcTo+bmoO#YB%(Xa&G{xF4VM4*8GaP@NB4QIzs(QW$fyBP1nZP8%c12e}H-RIsf&2$=oDxSC!lguMlU%UB$-Bf7tOck; zIKuS%Xc9y*icTwdFLcWrPFx9EP46#u6Sbjihvh)UNVs7%Q3iY(Xb6gR!8dp`q-@MF zXe}7uV;XoGWYeS)cqdT3LNY)tKtAso8BSJ6xkjoc=|v6_e-T~DoXSCek52oa%QwI=v{<+ zcY7c-oVl?kAU?FQyp-G<0?92T{R>t|4kgJ1-Hg;BA_9JqP6^%Q6{IWS8fnto7ypLX z;lzO}CX`s&V$bj?db(I;T!PXt27}EI-$yrKDtT8?2k72;zMxO>S4a5*_ha3OuP0YQE#0VAlu(4^chOh9qMU;~;w z8FyNSB{Rgn%ps8!qJxtBh}g*Wh*yMx2u0#4;bvGN0*-$ldfh7uS0C)@sET6^?66G1 zx&$!mrem5(R!ZU+dP1SNH(CvM!iz-_u^O}8K{v9LhKB-0GdMaY0?bl>)lZXg$;+jS zq=v+$>~-Rw_^*kxM48yL@H_%8hK=ZqpNx6|9N`~F=zHD3!NY9dPhh7)>@3BwLP2kI zl`-f5UBzwm2uVs@9DRduhu0KkiTytmob_8&d)vkJ*m2ZjcMvKFC;}paQc8)0QVI%6 zh=d3#H84!?-m_=+ba%ke-C`ZPkDbTvdemdTynn&^VPDt%z3;U?YyZ6QCw8&s{rMT_ zJ1cf<0XoI(*6oP+8*Vf%hl_Qj*%xZlY*l)IKUI$8I51rP0lW&xrGHbiIq$^c{X$NN zFey8QttQ8%oMHV(G{rn;ZNkIWE162P15fG#`FY@U^)b*N$WcaB-r@|%F7BVqnJEd{{g}N^ zXiC}1`kVhX24&47JlDrC3$PgPLyUene%T1d1YpxO0Y+{A^Q;M#wzwZ@LpmFiI*-DS z>e%)Fz;kN4jW3|>mT&UcpvmOJy99jH7Y}a8p_=x}%^a2TZ4t@-D4)E$h#f9jlKg|E z5-y4HWPaq2UO$Q%f*f(z3wN z2>CZWlJ$Y?+8Uw(IA5K~kpT8-`!dJKj?GEGQ$kWtEElm z43;$p5ca^~+HTHN;IC?T%4N<@leo}>U9Y>HwTkskJ$%~}R)~B;bO@6aeGCp{JmOnc zJ!h=K*EnaASg8yKgARnx1W6b0( zbPk~Jfj3==!-CI$I+cy?Iy2-D72%v1-zJ6|kIu4nLr)Gl>7GFjJ?o^Sz^smN?w>$y zO9=Z<&VLQQ<##w6Y%}-8uyOO*%skd*{nl;XteL9*s77X*?2n+M44ZKJDi-}aF~WH^ zJpz7kwM{&-qTzHbKv(7Y)qinh)XHU%?6RtL zS7(T}(U+Ww7CO?*he7f)ZDq${{-vsO^`m$_RTai~qPc3N9N{)lzwsn^J@p2FVQtjD z%5b!sT3cj5+NieOde}(qO9_K;>fIPVSW-1|eI_vIxO?B?1Xk5ATgY~xIsa2G>}TIN z-7X;6iwFKk=Cddr9{gzL{klq?KQqyAf$(A6l#l27Fmibd@U@I_z#J@$-coTF-AvCd z3PfV)p1VWgAo?H4pCDJ-otRK?ChgLC2VfZOmbZcZlJ?H|BkMAK@|C+nFIaTiUN8}A z7|7j2t|VR7r7yDl58p-0hJLeusG1fS%YQ)cPhpqrNF$x3b=}sk;Q{3?7HMA za6fx!bR&?>N)Og@QkYk~SFtxSqn#D3wT$zZ&4QJJf|K9L(d4v4QM|{zBW-Hp3_-Jf z;U4D3=;QDs_;RTnJA=*Q_Mz9&@$7TRD`YIigbaoMESv{Vfd0zzhNgp4wtWR>19PG? zfLR>Z;2oSP?8sHW*`rzeor{>?nX1d9$X@B*lRNnW@%{d8-ab)M+f-t!;I3^SHW)sfg%)_j-OR3~mRmX{bzPI98|9svY`NGx;Vze}?<_>q5_>e9bA0u*> z2(ak_1%4P^Oip3lN7nMl^5bvw7%L7^z9el67xHN6j&E6^^P?fy6@D?oCf@Hp5z~BDLMWJ zFTSy>XCL8LKeXu%*Uc7XjmF)qCiQX5$2?lJ4BcqRLH8qR+7pZhxI*nz+674z^u4=5 zoAg9_H_$2`mekAX5iHxZk9~~i71+nRjL-7wVLpH#I1!BZfc)HRetc*5u^l|$wubI= z#Ny`aCV%eihI&gYKBxA$dIjcK{Zc4JS6L<_&yaBA3Wf`uq1#mI4^h==`JX_pVn=!$ zAd*HV?czv94x8-QTK=s-DyxpW+Y4s)Ay=I88Rx+j=c4((eNT=a;w|n5yH^s^Iy@Q; z+;J^$Ec5U`8;`0K*z~#v;W*T>MuDUu-j)h_4IE?KT5=rPqg$HK0ZUYOcfA8Ba$3?@ z4n;CL@+zAy$PWx)VT8$RD^rQ~Iz=$*z{6+T_%25}k1poTJhZ4wLX7U=H;&^D>6~H) z@S&}Bs&UxZrk#Q+bVmIY_$IQnrj_mr2Us?j#6Ss#d-=1#bZyiw97tCh5?eTF(mRnD zJ45&(a2TtAchk#>S&6BgmNGCX_UuBw-HBC4xV(u+sa-?3zYm>hIDmidbufLzzI5VB z3i`Wk6!{4q-&6r7Aam*-(CT6L8vo*(P>31Ms{>>7ExTNSD0OdQ8YfI{j$F?U7v~53 z&DupkcaGO=kS%>%ZtTT+|_t*Um<3j)`frtUlo_-HRBj_0gU=574RUM_hQfUs5x zR(PIa26B^@K4e70`R6vuk5E1xy(`;Bv2_2YerOce)&#MP(-SBOTP{O%R+weGzyU{8;7VbsfN}^iF!t zV%q$(uCf}|*CVH;;jF~29Pw*rZ{rw|l<8q^6mDU3sdfscGd2o-@bA%IAceddx`=*} zsG^6J{NP5@N9Uiz|D@edk3`#Ohmwks1e$eIDD;`84>W;dT7y?L;6*#{L}Op3|8Yhx zbAzNu0ww3bW1UaM`++YFm}n8;ZC)d|%>k5eNFDp30ORMeBal%%UsfM|FENF+rbLZD zWggBi#VpLET|dw`#-}6~_%lPbX#^x=qy*-J>ln+tDmni$hB@tEv+4KFOq5#ixWiqN za4fVlQ}h-MYM3NcBN3*z-y++D~cmz+B^BN{TMB$sZx7#&fyi}0p694e?^&u zpk5%H%1t+IBroG3%F%okwwi3<6`-E57ZHs3&@9}AaA@%p>>HGlcY5&lEZVgd;Q&yg z5MIYIMpl9!*=Ga30($mPuZ5gw)>^08tgp<a562C}4-Vp9#=s5Qpj?)70RxG`kjqy<5ynSdoGI7^%#09#S7y`Wp8zQ~I zY5)r81JXD-p6@w-v168gW%aXsPWec}6weR+61|nLZa*wkNbx#`V6*t0aRWI`l%W{M zdn9-++6djRADP z9=PIpgnfgved$dW&U$iEAQ_?yJG5O?t%+@S5GJX1)jktUQqT?c{HwBNc^S_n`NCgL zP{aY?F>bsNsoI2lk#`R2u&I34Tov+^*uQfjT+Ll1 zT}nNH4->CE=!iZP`s6wzZKPM)Ex3&5l&}`^=1$od3w%c32K>QkgF8Ia*~Q?2rE=CP z&e9Vb#RA*q{%N9s>Y~=Wf_Ii_HWOKGJfz>xFVvUFLU`-7t9c`dxvK9#HU34>O%23u z%Bl{S(Jsl_oHhgzzDX;BBS~@meP|@lW8+ZZDBkIRm;<18o*wLtQ1Q}A)||E>9Q6UsGd}+VjR+-f&-YADX;5O1xKJQW=D)G_CtP(F)~@J-3i> z`S{ctc(`O?{0gvBxG*9YIKUt3AIkB@yF9M5ej#y71DVHxd&e`xzCA;FR|{WvoNXB| z(6?%xyLBH-Rsv}E+9K^RL799 zZ7i(|e|B@2WCHQBVG;f>_mJ&Bwi1_E4^_lq#U^IaH8fhkYR@=imipX|x6pY-bQ}Uw zC9UCi058GUbywJ5cvC!RtR8IK(veIS_~BTjc-GP1J#&TU1`ag67I1psS_jAtT}2u- ze?t2RF`KyCY{YkPn;L@H3vtkPo^lh*w8j@@qic;PvyI4b-TWOS)S-%x^8qttd&4Eb zY*FSqIr|3R!y}%h#O^J5&P<2Oj-C~dJ-MP=Bs_GC-?U3mHt;{IC+X9dsF}Xk4sKM~g>T{f5}#YQ zi`_%UcsR4Dc*Bx2%wTxPvFn|a)F_Pk|Jtw15{3I78zbO9P zLt-Xn0|yn2rEIR;AdpZf`_GVel-8UfJPGA<>O=02im3Q9d`m@FL?>#j^zz5yca^6+ z|AaiLWlPh5168YzZP#qBde)PsYOF$=cPXb=t*+iFXHh?DGo`<&osxVBo66?y6i*tY zvsVfg)VPZGg5}hW`>FgMYRH~;o+mXdRm~Msv*XubZ>Y)$1{z4c>7NR7tK2-Ef!C@O zOGAKpwAn}RsxPs^x}U06GP|4JD8x)p>;L4V7+snlQZ^$>GDb3y{(?J1q@?rN*MzS0 z&SfmWTG%=MrN;=W@rjU5q=+@sO`SHSKxzQ+-eO6ywc!Z}I% zb~IU?1a^1lDm#InO=XIOzdfpgjO1i?Tiz=s_G z_6wL1=Z)rbg2LtSRW@av5?akV?6DajSyPtWWxr(ZK734hL3FV5rNUh}w{e0@LY}a= zNXPN1>IgBx3lwFG?1+h2p`a6g%t|4*V23HIc^^@8;X{Hzh-@G30=RgGFQ$VMw!BAY zgFfNgVH8-rE(iP{XP(D0U<-TBl4I<8*1W?m%2IiJ=QjBr>EQ;JEKK5Qwo3X$jjA)^ z^+I3a8{swbBKkwHkDt$cO^)MDC_m4$aoY>&LM~cX=oh-a=EIy{+(>3;)Z#)q*6Xj6(}Aq-6_l# zO2u7hxxhm-i&@A&FDNhH#w+AMD4a@6;)U)m!!=yPb`)EPeccj|h|#dHYw$Q&u=W>N z4&GXMi}M&*u!LksvC|IyPw~U3?pPwrL{D|!?=u0AT%Mc_LPYL5fm(aMiN5S>*x|L0w z7I5AY3fqqT<}l{9A8YWJmxy9&Q~3>oC)GUY7CC4*(cbds7@n5=AbNCT_TJ?tsTXDz zVhe#(P zzf5dwzbDHUt!R<)tb${Wso))QN8Q4zpS;i2mrG_40yAyz7_O^fZbmKEq>(0%Ko=;! z#?FPA(sdyp!Pmk}zX~9gm+5|#eHjZ|{E`(0&F&qkSa$4ni;qlwm|BxBozXv0rx%O5 zRni9{yAD^LqoA#&2TUcS8iT7?yvMb@#YTcry&_-Don_)?Y{V40Guvv>QL4u==ioiE z`yq|sMNzL`C=kd$=&ocp;}aKOVJ(5Ay~!qTN@&Y~A&KH?Tdb?3EY))~)f9W#Q`JL? zBR^cZnBoSB<#`le+6ieBC8p%1_$Q@e?_E(Kr9G1-D588%zQX5K#Kqnrv=xU!Rk*3r zckNJAUU`1yY?w){TrwWatXkdk+1ON7-m+bPvC6IHi1ripxqh+6o?0hERB6;o-Uo%1 z8V!ZWK2cZFDx?8aw-SSxOLg6=5MHCYW(EnyQdcC)_)gRjJb5SYFs&f=AonsYJ2V)(P0R9YN3PS#-T#G7 z(X5M~1MRd=J#!5<;B7Omy8r}M|I&QrNOU=B7xq`_b!9#~lNTcIWZeLDvN5d0s{4|y z%zsP%64f&g?42U`&77QZlw8f|OLpX?F!sj!bN4Y^LZ@OB#xOqusi41fKMw7u-&}kR z$fTd@t~M+~6Pg$4qLHF%v8Dtjbz@Z3P`{L-cnN+XUdw$zA8?Zt0oYYM@g2^s;^QK3 zj$i&+0n9dLF!?9g)3&`JKCs}}G298P(IJnpg-og69%Kn~jk^`HXFOSa44B0bb$jaT ziL*^j+B4k7YB%*eT&^{#7GON-JjG5_ObBF+hz0mhIubrw6(CNAo)%MtZO~w!PB0z} z$q3@d0_EFk34l`&#>M70^l1g*c}0HGG8xl=3HUMclGFfg{zv@X%onI z)_!#vzh3L7wDL+N?edpI6yYIT%XI}55+Od7`dsu9`=i)N7>0b!_a!y(hx8x37tph9 zxx`ZNdW-UBo>Pl(1ec)^fzG;awiO2hyjFJ=!6X9BZ(8vFN?# zm@-Mol!V9?f<@fp(zpE6fQKZES56&VIf&&4&j^0vFY+FdiI_Rv#?zuH+w8dakg+ja z@X1hHNC>(b+~fNKjsYCpH-Rbahl{zKbe5r0rjx3!H!jn3D(70dRQKeWnqcJ=X{Y#v zJV`Q_8!4?7A)H>xDB%$5G*KE^d~lbbpZ773PR=6srpNNq@dsNkaMf5u%sA`unWkKO&f=odsUNC471vZl#6j|T3Mc%e zbiXWx<1aoZ;a4g|OGPIRyc2-JVR?JLw2Eq_w)0u3%H)VQ_+Q3Dp$P$MA%q{Z#-m1rmuJc z90ofUZ)M-&Y;J#{eN`J?U!|E={mDF8wcesv=@e8`yvSYlw_!ZiFP*17!CogWRkJG0 z!pq9F2Sy7#WM6Uv__(BImosry6r1!ncMSPD+5vOtQ8t7k3HV1}d#DVJUU3u}JkC?Wx+sjsMksQopZHHNh$e+ey`2#m?$A!djWl+>ZH6|1hp)9Tq3+4p+DfTQx!Z zrGlx-ft>67y>joJr-^2XJgEqOB>Wh40~h7L|wl6QgglO57pR)OG=R%IQ^;7}-)jaS7aU$e12!Tai|yEZfb(sUTDa?W z?IU$x`*CBr@^ed@a*|?IW0SxnW7Hi&{Uz6G?lMn{ms`G5HV6UJxT0?Ip?*frSiX;D z+Rh0?h2mS{3EUz*5w#vYA*x!Bz|YA!YY1p8SF~aW;Dmf$Jc%6whP58nQu}V#Zc{f5 z&YBO(ryUdptZ;0N71+!6HLXPYCCBRLGGoN^Y=4wr5bn3!DvBh}8SD4x`S!Y^w0a_4 zJu7iPj>+|#zM?kCD@VA9A7ak@r-aq#rCLh z{mj1Pa{Zc0d}HPH6&}cCD$DT%6kFxja=B(~m5&X!e4@TF1eqREYvkVy*Qp1| zF5Pu%1VYz5pxQB4t6oyamS0wUppGuuCHqDlzvr0b8+B^h4bfBT!bFweC^al<0iR3d ztj{7ssb|*g!k$(wS+M{)P$hNj1HaH_w@|7X%ndd#%U;F_{ae#^#%6hqA&&lxEYKy; zF~mo+i|)htpQ?cNv7A(t(K?G3%h)vf9#jI+wxtvwHZ6O_cw`1m>DU6QX&+nWS$_g?HFwRgITpRzc%L&&o~FOS&Lx*hAJxxT zX^eX1C+3UtSj7+~t?;RA5_8_3&61gnV`&AV@r;VZb>thys!gYP{q$e!=X0s_2WyHso<~Nh0N?VB}F% zxr}p;YTy?GY2IQT&AnZ-(DW8Rse5g>iS@}kb?4DTd`NQ%xdLxj-G@Ih>=f_e1!ZUC zL!sEhTz&Ull*-z={5-sXdB#xy zv@{%C_s02!QXdNgiZm#;S7h6#8Wa6^kol$Ov?E;bMsq zNy=7<+TgU*lY&ktE5X2T0W&xG6IviU*o2pI%6v|vo@~kT4(JwZz!3sUnQxj(Eo&u) z>P;qlv8Qg4VS@0a^pkFwAcuEWGm4xARjX$5uF^{sE`+3PnarP?y6=i40e8vXEXqc| zr7{J1$Us6Ce>Y6qG@6Km)&-a1Zs15CGxC;mZMhawvTGfwKoARPd}V&6+-^N-ydrPZ z8uf={sw@)`RMO8(+a+5JuA$knHM2%K=| zg!z0s)Dn4$n1I*?ug9K4DjynB2hLdzK@?6LjpgHd5JYcIDZkczR43<9QW`jbb(Ff~961vt;BO)@@ z8&oQAqcTZRN3)mjll7N8k#Z#G3v^e1GobGVbCS4 z0R7v?8S#Z>%cH^XV47nxrz@vA_kT zn`UX1O};@jq~xG9MSiuwTU;Wo&AK8)#8o>MlejQA-j!Fy|FQ8KcNbv^l45>%(CS~v zSmfw(5AYo1=@`vHIg0ul=Areq=5E86S{JoyFhQ&rGc@llYq&Y8AI8f-f^wq1xXN4Z zsGVIhLh7sP+dWwb^ycyz zpb`4v7|h88rqrjHuC~lH`x!1bMyu!RF4bY;p_*$o*YTICht}1cTZ+#nD|J9V&M>!F zAYGtk?adSqPPME_!U#p^_WfkI6pzDsUZSHL3%FCsr$LL+SH#EFX1Ejk*X=tYD*pCaq^n#R=f|5N;cZ-s|4&#D_!2$EU$vS7i}3qycfoB)k>fJXY~X8M zxT&>gl*!dl-|;~;MQ3ZhFZ`~lYr2ZPRCU&0XaB1>V|!A0UUuL5{$Rc2gXwKPCLW`I zo*5>bskxHUOHNhlw!9#|%i<#*;8#U&0~^q4vS@V_e2{zHZ2rsjq03`Ix%_sRs>8QX<}zLL9ES^h%NM^kFXL*Y>U zUnx8J-_`mp65_sMYJ?Q;l;i{+L=k~?^&&WqIO}Err=d3-C$lesH){hLo>AP5!F3wS zEM=H&8)b|zrg}Q%4=mk$jWUW&HOeV7Dlz>I%JPG3?L10Ueu?T9g^}s6ASg#ux?~;| z6Sh1OpQ^}-xG3CIaWxRZ{uJY*p{172C{^^-tYM50 zoV5H$*Pstgee^K)Xah?7R%xe8qIDizq?u3SO&O{HlZ5QsthXxmjE#<^?QTRW8Fp{S_tVG{z}LK)1o zYfOQxGS)@IpRA!3FSIw9!UM0=TBc{-BV`WbMnTS( z85qc`pr7!*hM%P$b@M}q(GNR-kPH2gZDQSN__bk?jR8+qcvbsC0fLR@uV4k5XY2z` zv4naAaHwe1CUW2dZR$nr|MD6Yuh|6|Y*{1gP0B<`2CI0>Cee7-(1>VK%M=GX^5!u8 zy$|C>jIVCXkvc|?0}H&(VAgch#c?O;FW5%lBjnGm9oR6zaB~ql5p^bvsem; zN^lGFR`rishM1$DRkKpGNWR+gSa6t3HfhLx2x!RSFK71ZRuSJRPc);r=k|Y5Ud0W0 zL*yomp8h~uh^8i&iB}-2HXjo_gGYp48#Z^Wx2Am7D95B|36cdI1+I%OcP#Vrf8oCaw(v?gB-PAr386~T$?kLZ^o7_#0q-fQWkq^7P({1p_BtKfrQMs>1=`DeF`9MB7abP3KvS zt6F8-EE^Q>_$<>fxhLGFKO+Sg@3kuNW6C0RktlS(uQFIrof|EmN&c0-PV$6TyzQ~b z#J#jRNU#le4ByQkf))kr=5paKZ*OcE_|x?cybJJkC;-~n#a5wBVtA_UuHK}3FMVhk zq4~w1U_7e+3l7%f%H<52cBeePyjkrftt`5%94$8Hyq4V+UflIiQcHf_MvC(I(__C1 z<`c`pop=NIssK-JEV{~jGI|nT>Usip1t&V}0U-7}>nvNEHBcK^J<~j0nq$6Xc*7GL zN!?lKynctKl`&E4t}>NxP>)bZigFa!Wk3!rtCy7R0wq*Y*0unVpCBZ*o_xul6ZV3K zavui_<<7xWt6rl*B-phH`UPHh$N+Y5GA$aLTP>+MVSQEOAbD@DvGnj38VgL@p{;sf z!vwlgJ3)K4?3(IdHC8lK(V`5?St^6%!*;n!k|gb0--xD)w#8%<^?3a}NwFEW6%a<_X6r@pzJ8&3BYm;v zgW+abmg=Z3w@|1MsXy#FBFj;h?z|xJmi=$5RQN%%J$4SM6t;xr@K*D``OENQ#IjX7 z)E^6W8<^ zNb6T?C})$@ir*D;VphsxD@sE#F<$X_-GIPSndWtjC#?M7qQQ3#!UGw|?5d5%+*XYG zLwTfm1hrALpn*=^i&N`{QhhluYGCTLs#NQo%8w-5rzS7Z{Jk29ziGQ%WSD}cu`htrXrBxpTNoTr8Q1uY{Y2Pa ze}El<&$s=L)yu)FD_Qg-FPHb$jkPcw>VDy}sA!1f6J>-M1su)3O;2*z== z7Qt;)y?HqFx@3ky2)PuL=oW$dcVAT7fPu78%H_bc#Q$UlPF}P|GKGCObh5C3y?9+FIca&AwIixY9BZu^|qo9DoMO3TMY`ME5$aTKIDJGNu0CmBKgtmZ(ilx zRJOMZ3yow^?J3Yw=1KjDrg{;gSlzHl=q0e$ipWRUyqd{;4O?!J^Kz++Om4*bVuk)N z*S=u5HU=NDJ4a)zSxWfAnT?0;CGLDCh<&*RA0F0Cs!r=1$=xyHL;(G?@C_?opW75EyD7g_KOl`3Y^wDUpF)pRcZy=!!Iogbv&z$hNe@~aq0i%c?`_lk zARg|Xr^2|x)Ln`hct~QpltkZ0&l8VAgds*j4!mmJ1YR9@%PR&y1u$F+kdqvjL6r{H zRsE>Ouj)nelKQ>MJ>+xSQ+X?zTD@B~j`hI&QBqKuYXn7i4!+jU7sl@0p=ltGWt~z5 z^Vgs&a&Jz0Bbtth*&i%m1w@5Gk^XRKo26vL={I)nOG%;{@fVgN{0rJU(BoStCu(nyEZ5&e%~XPZ91(fF;+- zol&Wx5MFG^2vW!OT1ybmuyLMW@xS16F2RUB$grn^&YTt6#>S8uv&^w>z#7F@*!)f3 zkXhCvhDN5=9IGp<@Gv~pL>=hYQB{lc*J=Kef6MGunxyA;%#(YF6$$>5T474mJkd;Y z!iKYC5z(?XgJ{P?J#S$TkmD|%@F&RAo(lZtNVUF=bLu;#^>wn^+5E+}xz$YAVAY#{ zGTqGXMlR)u;cvb7fi1f2ng@B;)NfVH%BdK)DAzyRlZZ(ZvBi4c#78xyt#a>2LVaTq*Uat8A#_)!2sAK7*aDK+Q@IZQjB(91>k^c#~Qu_L<{QDwjKP@qjf5dZ;-sgrcdppQZ zb`7eeflF0exD9gAvTg{Y7ptR{BWduFG!HqJud}1w!GN-7&`WD42H{G&` zl9(~dxRXMrAUcwAH|~_Wp~5Zlu%fR5T@OfGD*p3x5iu)^Jj~=m>L1QO2YC#$T_EzU zDnaGgRaZ4u^s@aXwTD~RI*wWb7Bq!YgXnJcLh9_YSGJFpUy3xl7i>xb-E{Kq}vFk!ULQriY&2R<(NBP_BDbjol_hN?WG< z-et#V5gl&d#0chcS}N!_z_7*+dKrC8{V2L)*(qBV?P?LtdW0s zNzn3XE^#tdJ8fyCRDPc3vVOPZJk8T@y3kAu@KEuiXuFmT$IsDpc1egY?W^*iP8}y! zq-Z%!xH!K^4@1?idrNa;C{YrhO13X@o*R~Og z;d(Zgf>PjXLnH8#)?51%2rZ-4Y~|Dx`B+*xvvQu8MzDG5gnl!7a>^VH$&$xqDi5=k zMdryKGyB%B7r$qw`2G@HWlr)C@Ic1ZWn=KE45M8>j5114`BL0=a zU8c`GC}*xAk_g!Kk5+^KOx~w@iFI!|t5}R4-1tJ8gg6D?5*0(YeVfQakmr#@tOLT9 z4M$&chS+f-4y!}q*FIADKjF9*S49+dqj8(e#xd6kq?48P+BnDT z#Dfj_W%6rnJ=$LaOY^@PH>|XAqH2(tt_xF0s%F)QWj9J*TE0jZ?rSq`73c0L(>Dp* zcdgZq6O7vSShbCxuq9S5B9t4eBuDW(!A#*jY=-YX{%s`0BY@iq?Q#B$76A-96||5o zmglrp8ovtGH5>F_u*nUVwEwXm)s0vGrgqe%s3w%4mL7$}zHO%YGQT~G^d*wGoi8;P zMTOgBszm~5%S8EhzIfvX2}G!Zw+OYk%r}y+LLm<)ZaJLk{2E;gdfFMl5A2_^-)*7R z=j4^Ht3Ce+0@$m`nA-!nibkvCCe;?dT7B9<9o%+>=XJ3+2x%QjX~18ElT-O zq}_Z}ks8wiFq?! zK2j7Gaa!U+-Uxal^yh6~^PBIBpI`YOcNXg6d=t3`mDx3dGET2-cI&6cME=`m$GRHi zXhX7XBuic^w-!_$tbS#BUA)Dz(NIy~YOK-ynLSKDTrEnwuSroZ*lJW(%ha1aWdBOW zMu3u$!kt0w!Wn$ant%9zaqq6YjlaQ$IG;ya;YoJwU?DI|DsR2fqUIMik7>M*gf;lr zJFx6(t84`ozpF1;2Ro-0H?z~;ej{!`cFT27w2#xOH0#v#qlt%uu>@!mGQYxx16YH)1wV47+( z>a#0=>K2>oV4h{N_43|mW085pZg<^D!-}*ynnl{=r1#2vRcS0wrjwV2e-{r(Qi6g6 zXNAsdD7+)Q7b|tR3YR#yBN>Rd-4Sp;a9JF6D2O5;7`+aZWT>HQ0%bMhLi=cn6XkBp z9E$6KJB`7VwfPt8_E6%o4%d`X4yLwRswhoKsPRC>kXT1uY(;E1O+B}wHn3KHx^jk3 zk0iDdcQ*<&)Y(q?#BplpB1iOURl0aZe^b>GBC`jlUV{Xk2dD(2tu2?jjdHx1MqP2> zctag^e11>ujmpnib=9vczohESU#O#!$_?+R3u32fFHzTq?^o%lIe~h4Jk{jWF1|;7 z?QRx$RE0T}5Id?m7I~xlX`bR){Yx2jL}brUMle*~@rZthA#c4!r%`H}-qKwT)HTef z-N`rBZlo!*q}97p(G2KtjgT(+0q*u2^QWo21o z*!xpcP3f%9Nq+il7Cq*kCXqEgJVd#gX$xdXe=(zcjDwAX5$@IeUyO53{K3YYW>GGZ z$~Y+M=(S*3+-F_oC;?4rPe6JYo-HBp8%k(n0=(ovOdTD{%#W(Efz?@ImcN0oslmou zKunUo?%^QOaZ-JaGb!9tafrP?@PL$H-Sbh2yjaQZR=$?`%t?wrWL7PzKqfM0i{M^2 z-Yjls=PzOw_^9nVcMfA@^NGRU;hcu+*vbQo>;6DP@|V;sN7AzFEfFvyb&)X*vLy}I z<$||j>ecDsxbXRk&45o}rqr2}?;{jmW!Je|_~ESYP8ytK#V$hN6U-V>Oi!=iGndv$ zkVn9pwtT*Tey({3FR%PjLje)F|3j^e>yh`V`Z&HY^ON}@Hf6_W!*}%0#JjrD$e0*R zJ#5fR947w={tk?loB_W3K*9>nPj>_F3um;G5szTcSR{iX)>6^fo-LC9xNAC>h`C@^ z+epzeI^6VCaIRd}FpjL;-&pI-_snar&LrMvcA1&nrX7b2D!eSwrme#QV|J;k(NSUd zhZv#)~(7Y+OwvOa#8uP z`W@0o`={9u$?Cj?)@~6ubD8Om;Q0<;{ZBG1(Np^u&lLTiY6ACH7)SOU-yHBpd=XXq ztQP?AzwUJ2bZDlN8RG!!7afM4u=51}b!QqR_=*l6ofSxLouTQa;Z0*z|CH6%&r|%b z=#VW!J~Q{6wOs0-amS<-@814F-zO9#e9&Ab`=a}l7kKx>;$+?2?*V^{aeRW$Qo#mv zihB`p9Ukdq!lHmzi_SxB?9&24x07WHcCKTn$pskJ@~>eGZAIgC?W?lbx>xF3MZ0Y? zm6vl%tzq)B8Hg!YdVITCkBW~Z)M~WCo@lyKMOwlZ$=EzJKqXG#X84R1jK^HvcM@9o zUnc`L2Lu+~h5(MEz^&_3%@=HU`vt3rqiAU}`&ON7tTVhQeN}f@XDu30^Hy^(ceZuD zYD30KQ-EU9_Bj16>Ai%VnqsjzdY!UJm>T*_mPn2Z*e7-*tgD}sH*s(GD53yqaMGYJ zq1lU`LCKs-vcKzWeLVW5z1nt;}@NcA>K7w0=xZo8_yCbU}SDyR(b7yZY3u=*Hj<9fOK5P@j1lN{}XEQ|gJ zE$3V&gS+aQ3en1TWH1G5Z7Hk`q&{ujQ(ap+v5sn4P`IMTY82)~S+42FrWYE&YARCH z`q|32@jV(hc~Vr3a*3omG)Fd4_#|MQ=sY=TwVI?8F7EckbZnKA2qj^6yCKkUz=?mT zOVFB!{Aw>~I>TPmvZcX^y00;!b}*o>+fjXK-#`synYZVmrQMjGKHhjkXGsaveO14S z&(!>>TpG1qIb0SRx>Wi=ywU%v$Sm+(T}meM7Pyb&zQjH{anWqJ({4O?1E3Kfj$WtS zf%gs|6bhD&)I*J25; zSh>k)cve9VNz)co++6p66rE*Q6z$uG?f%>O*g+{FDk6%2A}DFlpdtzih;(dB*Y0fh zF5TVT-C<#OcX#W%@0a~D4-1l{z=f~7*ptX5$(tNPWq*1j9+NSqP`VP9V zYNoIfj+Q*@afJ8rPIP>Oe&N?!OQ3qtx+xYyW-lqCP-R8lG8SrG24rKv+RPU#hga=pY3Y`!e z$^Gt5td{4~5sJCs!>!BFm*B?6ho~eTt?NfEBf6{mkk`S#D!P!8<6BE}5Y#Weh=m++ z&&c12*ty8FA0xZ$4`#$6c2>_*o+Ga2rxJ}3Uz0xN4uq{gRk|FhTV*YHj!u@`>fTA| z@SNM9kjZ#`i<+DdE^a(bDC0xwtcm3jWz{?I;^3DRX1MKf-O|O_E5G2v4_LChJg))s zbfL2Yu@&|X=|9ouR*zDm(RTBbnl`lBWJGZht<_&FX+WD-c?oE=N7C2TLZ|Y!v+3I!eB>H%*^F zdRkpc77>HyC)6(qCzI=n>G&=EEs`;K+$xfP2cIj!I=dxx^!v7B;zr!3d9tVtXsquM z4#v-@$r9X-@Tg4Te-74^srhpQZWe<)3%@l5esr+Ad+r8qii=0)6V4_3HEA-=bgPl% zwbVZI05yk{nB0+95|8zFi^~Y>Rmpq>o+|O|+^-0yyISAMt?~6uYS{vSuieG?5O=R; zi}-59`buL_cW`)_fv_&1x_Gvr-1l$(2Y$KxmYi;0wafNQgl@2(nP$vwwdzRf)O&-X7$tU`*;^V}xRTX?UVxHu`j(Zwgn$wD?9$@dAHYf?;VBIT)NnC#Quso%&eo;v2j7wAPn|6`vN8vep49v9?iQaOLWW4Zjw%PZp&ba!|V!Eb%htL;u7lxo6I+=^m!bU%d&9p zS^Xf9f-`N^D_#+~ORV4iB>Mz6r3KGKvHvw1YR@9#YG0=I#$KozN`4={y1XxG^{F!@ z-5QT$orRSuf$z$^WW|76SayPZyt6RFT)Nc`Nx3f$vhqxf6bj9kDIf6jOh#mebg6!* z$cK}^>Mt*WOcV{b=j46h?ryQmxq;R;e9Y=)ud1!esE-A!nog}M5hQgV zV-zwqXMJwv1*&ejnPl52o;rJFjFCOF^GL~)+_y4I+$_3aK1rD)C^zYre&n(BBSdSt z+g8uv?IqucLfb=%PI4Z!%r01rx;J#>o@aH`;@OheRaHTm$Iir;A4|79IaG2ub?Gs^ zLdT>ZKH70+&CNqEv*)PlotLIxP*8SjQetHeRTEr)+#yn#6iWI0&-yXKPTF?$ zR{9ks7INE{RLD5vnmfy8B8-NpVm|Xlty$spnEh3Y^2A}K<#TctpPW`QBQwK4y5MKJ zp7*odrzus3_Gfh^ZE=#PL+Xq@6)DRUKX%q>GGx29mMdq70h2`OVL_gLtWZvOt=>ns zQFn#Y+kRD_rnt=+6?c(w4g1UXGZSj(6gNdbsQg&CGR(H@bsiX$Tl_rxy8qmQE16ro zg}I$+ii3Z%Qj=deIi`Cj>h6h2{-oNslhVY>eYT1ecO^$nI8q~_hc+dMXS3RumU4{w zW`om#oy2f&Ir|TEtjm*qIpJOVBz8{}v$==e7y73`%03qe*1EHAANf=HhyB?LFH-{R z4}2|N07N_P$d3nFY(27@z}c2A>9@g1Gu`A1;4PzDY8vv@jg~KfUat5d;=vf-b^w5B z;%e`IumyCgvlaS~@VPAnYKUSr8$kTfU-jRi6M+de9gx$JZT+a-@ZFV*MXM1PzFq%k>^5<&M~H=M zQ;e{Q2_IXpq4`nqO-l4o=$HB+bbMe`jS zSRP1=KnKjWCdHtw#$!}F(01KQ={2-_rKivr9pG>4drwP=)^2SVhL$e9oNJgc(9*>r zqFfu<)RWN5b@r5Npm+5Qvg62|@)pv{E4U~A)0LO7A>CeS9G1E=M(`Jl;jiwC6nrQ0y8a4QKpWa+{Cx?PEt`1kDD#Gw zbX{m`Z4LKF;O;6G*Y3zb`Fc*0mq+nM>f3>f1wqtd$JIG^$U$4XOdHbD(kYc7>dh<@ z6A3Hh13sadyrzPP(l{#t?Pi(mr(|rDtZHxQR?K zUZtE%oY#FV=^>0)mI{vI65g9$NHLWN@0u#R0={ialIA5uHCsr~$glP9#37+zO@qiU za6%<4bUz|2+az%FT3K|P?{gq4KaLmZ@GJWZ9c8;ABY=xqTBnqAlxEhNi&U|(fufP@ z(fzBv`v)rr1WWP1ytdvc>T<%WQ>k(R+uQUN?-O=64a!p^`|A;zcj*2a2kG(?*DGg> zUmgi8?G-h7{w#_XN)MpEhy6U=sKqB!@AjTOhKdcB#F z^`vO!BmN!Y1dr^=Om-wDb{ZvifK+RTW_!ZarT|rIq@;ea(kXOF^#l2%6FC)yGUUkS z(qq!qo@a|@iw6$)=9P=09on)t3IE&uCq0edYq>i44KLhmi)JA`)7Vrolar&jO#G1A zvhoin7dA*g9WcuCm4rDYX5AG%+2 zqU8`Y=eFW&I_$Fxfzp;6nMv{68ky;eh~B!5sVO1uRX>x4AwZAiTLdI70rp-F8E*B>MJG`}IZ{k~z#|3}Y z5BJl#*~(Fe|7GdQYj;Pbaix+S?Mdgv$F}@a{SdA)Hj>xif?9s?KB;1UxR^lCeko zXRdSN(tVQ5v#QM9)6)zT^LNB1osu%QoL5OiqsGf*$%6m%d_`i~bCrx2PboOByM9*I zqfk4qT#r4cWp#-rwxFS>Xll4^Ejyoe>Oqxl&Z_{jd|K9Fzb(bR>F3=)6@aOGT}yM< zCe`h8&dk=#+;u7Sx6*ZoLz1}++ESxBD$X&UEejR2=p7UJ(}SzDJu%cBvghnC^#xrjm=weZkc?Znf^d^;trWuac{D6>kzZKizNz0##kHbpx?Fa-vgNeaqu!y;` zw-L6E?(Xt{$A-nVeTFWa+|;aua{WIw=tF#;`kG9L=fTKA_aIuDjH9AY=!|<0CGeqB!!{nFYulQ=19*|(!%(cA+m{D|Z zXBXNZ7T@NNMrc#UpsV~p)yt6wJ`FW9k!m-(f`RZ{TuKI!2>VwBUyx9nlR5v8NQ<6~ zM+kfKl$1n-HZ;`CLke`v4PZ~)c;zoJrHgBc6?V}6O+f^<$|fWSLO)v!Wdx&_H_uC1 zhYlNBsk6~xoddGZ=*S9B(FXJ!`F`jtEryx>6!#o+YR@9>{OJ6S0!~nvU#m6MdvbN- zEy~3IbzK;l<5OGpoLuKdm3t8-E>6W2gq8iP{3rN*n~?0^IN#!I`a9fa^U~xhe5|3X zdK)&P6Cx91X)9R5vseuIWGGg<;~UoZm;agZtDDPT6s_r4$g>M`Y$>9Vld~K5aQpmk z)?VSx_sOX`#0hgtDC?$1T&#*$P-gZI@_onxo4{-)xmKH!K;&&+oxF$GY8x=vi?#H7K7;%s41Iul5Ubm4MS8>M6*t!^ainF zMTbxy$HBG*}70`sdf27ku@Jt#sx8 zbaO84=1+3@uV^iAk$rET_SD(tK$e8N*P0d;uHrQ!h8tMln$^Jx*uQTtwubWA! zn^4DPY+=1%ihX77Z{8c5-C4TyHH+4?joj|d`bo1nMTW4li4y4aN-fCHmE#0i#9k5@ zP$nfpKYEuX&SW5+Icje7qBaxN|4#Qebty7}`1O8r8-Jhb*V1P`8?^hSQn%?PFU7l^ z9~K@Iez8x?)d-4gOf$RqCoF1G&+?XT)=#Y94jR(Rt1CM%2UBvzlM02wx@^s%qo1m`sxrMt%3Bnl4%L=e${n3G1qIS9 z`y)9Y#NTYDWG)stTcoG16r^ZVCi89?rYZXAiMkUcr#Nd?9_N20w-e?4SveWd)*js~ zUP4btNd_w_uyt!123d^}M6H2I5cLxhpgH z60#3}(Z8;!17voW7dR)(?%0w0AhNz?Ae$2!*XWmNA81$mDSh$LHC1HF2k$B6Gm-`m z{wo%1@|-^B|5G7$mvZbCPSzP2CuB1${8IwN9h+xr_6kpGb7%2>>TVOaa*>s7{G-%V z9PRs}1&hwCk&!Bow0B!JvQy_QQs!DmW|+#iSXicPkkoGashK6(V3;Sr&X?-iiD~YumACoEloP(7 zZ?HlK$m@11rQ@yJ?-hNBc-0bCa4e*<;cxD(6Z~3bw*QgnD!q(nUMI@R(jpHA6>muy z<8&%NH<4@SmHkrn%X&w~ScSL6{N%6F{7nzkH$<-u)8yF#9o_w6Et!1fFWz5D_+uRC z2gJW=00V$^56^(X?7P=igOTjw;oBg@=5(ZkGImJq9A0cPJ_;ekF0!c@gX?G9j9+(Eb1v7RjVLixx(;N6W^y9k+AVhz>nF%DK zVGsI&Jmkq$7^p|WhOGcCrK)2B@Df(ll!4P>yFz_%13V{{1G+%Zq*-7LG(zqJGoS$m z6TAW4K5YvvhW;EWg*@PO`_myQe0Y~JGy)fH;=z-V@!vApqa5bzqwL32@q?x8Z{(9J zSAqYL8-|hq0|IOh0-W$`HQKLA+_pdgBw_<8KY=dHN4gAnkNzPBKwY#k;U5@+Vxi~2 zO4RF!HTVVHzTXToLv?Kipcr(~CVi*^o%;1D+feZBRUUgc|Hb{2Y#-jkE4u6$`o-Wo zwwxZ**1>MzuBlF7Kj66M{{j9-sZ&CMo#Zo#6o@A6h&@0#QJa7RzX`KYAJ7GFKYRhy z;Lffi;2rFaO%SvKi#8pGj$+fk>akE+_RCkSG^yvkGFGc(_T^~SUD35c8|_>~Y1=~f zdVv-n%=Y3V`TlGU4@thnZl;BjaqO?$T)Y+7&goAW4@jvmArFB!)P}6o<35oxHdyyN~xe9tHI2?vPMG%1!nA1iazIx{d)YDF^F9kR+Fz zt^h~y_n%XkzG*%$LYP1b_wH6^dQ$tP@yx!&vHjPXAJrZ$b*#0@+DaD7N3k+*E=wv? zBq>>Er3*z@*|WvTST;LAv@}lZpcEtqe*kv!7xK{0rQ?gME9wBo-O=3hA zU3zwnkyH?VyMWP~H~S)s@j1J#&y%U22{-R#9!qzvn88d*)yb)3-bo&lxQDey^H`{6 zg{%5d2CH9@9Q&MYCOdiR3%gjN>ze~C5?*$`0WkQdtxAC_+>aaggC}ZOw{9?ki|<`1WvB}G^@C3xH#ZO@F#$>P^A`!h+nmZg(+ zFvgK3O`drYVNX<7`S`J86(&wy><1FY&LO~AkZh6-)N_m8l_iL~TOTVDnmP<_00~c9 zQqBc37B(*H_GCEMRy7=CaI5@E%^8E`6Ec4=XBFqFN|;9q?D%V$RXI0cW7f>f1JM^) z@u@W@=d&Is{__6KKB!vk6vb|l{@KX^^o5Nk;ediZ`u0`=Fv~(l-OlT0 zD;Trd#&s1k_B6TF7cs!Pni3VGrFvPWKXYt(oN5WvwRnW5VP+M~h02(}bIhX;v-~q` zgGyP0DMsGe>`m&|PV3ky@~EBrfJveelN~@bZ`RwD3HW)3NAnX(hi6}(mTeR=l}PW$`v>6Z1{}jHov(m#p?6 zXI533tG6qAQDTMTEjFaMxpNx(nfRK?RNx@5?F}PAa5?T_XhPM6#n+A`+!*d2ac4~I zXLous%)1@y0vX}$M#cV&qUQf)Y-PNuyQ%ocw5ZCW#Y}$LL9mf|zwk;F!rGa;G4Ks5 zHRFWWZ`Qx$P)7y(glfCxIrgCBuJJX%lz;m5&4jcYQ4jhOS}*^*T9a_+f_S))F>Yi{ zM>)f!zooXDakBepQ6D3_?OS>-<4F^wh+}T7{YU?giB<-IyO>u>5~CKdjPg?g#Vk52 z#;b_+Jav_$D|??>Z7E}yNFN#J0doY4-+w{7*yo;3MKI&wJoL4I+mS2INpL@M#VQFti(K9q3|~Snzr6$( z@dqA#gDKwm8_VDz`t~^+*oph4I~+FSE^o+z*Kk}*+u((iC@U9UKt51MX|Fc?@9;vR z44DZpA@pOaVH3RkF)oh?|cL6;c+H|@D{A+O(+zidi_udxhSKqbwFnFKO?W8 zbux9=YG|?4w*DA2OZ=;ZgrWl+Z zl)vg4L5(7=_z|d*eb2Z9YNUj+4a|}(r{jF^l7O%b0sql;#zM$|y8VI# zLTbL=?F0^08DIJb*p`P5S^<_NrEN4|RrtI5F0d!xqi`AEl2e|x5%A5LA~ywM(ob-{ z0IHM*z!_*u{1Leg_^LKO(F>X>_Iv&S!=(oun!!xb2Fo?zDDSzkH#C-`c-{=`Z2EEM zH=tYp?2;9*sOH814$P^%)H({xE4x{}0a#M}svsO#Tkt!T22As&%4C3Z_Ck(75Rvg8 zy8}?CPL5~dx+i@2x6wENz0dI5nJ$nb}bUeMY9hlR~ z8YO@UP4oLNu>aMywCVt3su@)nFu7uH{tz&`bYaSCU`63A>2JV1FO3=nytCceRsfX# zKEf6#PhlK?1w7Y0@!SEfP#QS=2OgAK>_`Ji;TogQU?cbIGhJYQU)^mMFs|#>#dGYh z?Hl^nus<|IEg<_-{m06C>>o8q{sv%N<*XDpZI(zH2Iv<})4Hto=Ebo(fY7YY@QZ*Z z?bC4%Fp@mU<1z40^={t<&{USaBLEB(85#9}YC7v_3@~S8)vY1+uYq|NSF_*r% z{#A@)-|aZ7?Sp^TB1=kTe{8T5j{uXa-xEE+nsS8c4cHVtI3os5Xeq&G0Dk6j_YXi# z@}GTUfV--q?KR+3sgaR6xRsyu`~_vqUVYb}>}TD-beTNPsu^@7$Fb7e28jYzQq3Xa z5KCRyMvP@C(>~#~+OfE4_!-s+_cp$sJrmrCy=A*a8DWj=yuc=m15EdNjU5DV$6{;= zxMJrF^d5M_#1hSc3Z8Ew8OYu{rQ`zm`Xzm$7LFXqCj8*}ZTiF%sI$5rZ-inB4e&^4 zM_LfR0a_==v6s*St{+wk%>a5a1~enG9dm%z26|x2AxAF>`UYY-dZCR_k7XN*!G^|P zP)E4n8LDNTN8TAngb{tC7%`hzGcXQs#_3i8eg^wly%JxC#S|#92k7tAYgiVVD*J^6 zqmG5|6O#M%Dxt?q zy?q29rt2?0!ehDD`h)Sc+;J^mu$!D6Ra*BP1s06Oyvehv`>>Uyi7W?ksMe%pse_E7cZ@@jOf==G5zZBVzEzK#iY{K< zh20fa_YGoFK~{?|=FCs4{D{rq<>p7AqjY0RGn&G^C4Ga2a;H;wP;1T}wm-U@!XskQ ziDdr?UF0n}*OQHm5vg4JH~&4VqcV}Zq3Bf72_`?V=l71K7DMQ z)U&x2?T{?0^h0scSA6#Y+-Aq_!Z@+H(Uq>D$gULZv@Ghz~w$_+c-fk-(g zJXatvb<|-pa-2N2LxNZmr;MH>v+;<>!&p?(hw`Sv^ql;-(nnX`*k%Qlv~> zoLh*1s`1HFk%J0<$pJ)1)=hdM6Qp{~_wWxf9KHhnDm)kP3jQQm=;05)<@xUO*ZQoJ zc8rI6sVheDa27f9kpn8uE57~&-J1iRL(uVAUOmf@o(!wT4uqGstvn3zNU_!KQ|l(V zCk4TOH9_JU_^Jvddf-NdjCl&Kk|mzWfGZ@502W*>BHVw%rGk)s)8K5L_4Za+!kuQc z3yvkvJ*3c`CE3^4A)ku2o$W%(@-KD=AQ8FojZ+Zw?Db_0$b^j7IS1hDX;q1>aCJ(& zcs?vnvLTG%1kHHHEjU_PcV-nFB|jQ4367F{byvbsBG%p-IE?>zI}<)i?=YGLJ5Zbl z-N@f6?zKRqs{Fy(iAZRvRaZW;p(wNA5d67dPT54bE0>p@1#7Y=BrbpxG5}F5?3wx% zSHm{RA&djCrDiZJ1GZExJEnjw<%iu(VM__JHvrxyM7D2*H}V1v?`kW$?GMHyqYZvn z^AW1{%!o5$RfTtcg#VRi)MvtjB}1j&aAM(~tRHY>zNsb#-k)<+coW{3Da0qi|D{zX z48il0JHm9~xtjK4dhi@&zS|{umh8;lIq-DxWQ%X`1U_O|4*lUgyT2Q0YFmEgEfUdm zbvOypuUB{4!!K(B>wdtsm3AdHFj=-O%NIUeyg_{v-d-?I=m0Ox`GJA(_{`A+C+K^c z>~szEDcQ=u3wp1<;KqhtDR$WRLr)|L7R}IofzS|xZgQpf2<_-j&y{V+(T>$aue20A zZbuq?t8sjtC!ANCTB7}zst#th!LH?#)O+9!#YKWkLa zHP1f)x|I09O%EDTez8ZOUg=c}6lxPn3_YL*+WhV{Bx`WuWf9`hyJlz)GPBdM{U?0B zm8`9UlN#?AGvL#89+|qZZPk#f3SL=mDA)-7E#{&L(4+hd@jlSS?609Wq2BaA{^OzM z?UVNnx>nQ__Xz6BQ-nrARayIv7C^bF*AH&#kIwvyA&TATfh zvzKL-2v9?;1EP=AK^95qkhfSvjMrocd%+o7;x{`iU^^jUUv|GvtOgG3n}C-BAGTk{ z&A}R@IIIKW-M64W!)LC}qy69+!>_r0P*rCycN=uFK9^Gstt%CB%)n>a-c&Q#uK7sq z0qaE(%oY6gq=0@jAzCWPRHV|NK%@Si)0H$&!on{j7|zg>>qfc_e7$47qD;}_nh>%Rog;ihx+6pQ zM&czRVN4+~Ot}Y!(9E{lLwp6E0ul z&gNzfwQw-brH)kUFK132PLb5%l5lDvS(#-;7L)VT56E2vDGVcS5aY3WA{OT-sEN7w z^01kBEmnI>2lvLj-0oxFuo-*xFe!Rr`)o`fO*8CAThNHR`?<438J8_MpinlnhWa4j zbxffm_`=$cJqeKUHp5PsEnB#`+#(z;M3G4B6^2O-_+>x~K zAHcp6Qa1sXihs7hjalM>7U$4w*lj~?BLwDsr;tO*A79c?pJhpd6m?4K*&a=5?U=Oz zWR&Q0u_rlMcr|k)ktOKTnzF6<4T1#xHm?c2fWvfWd^^5@+jn{o_JA|!ABrVX=iSUP z2lB4H6n#g0wGg3VVu7I_x&hyM`v+%S;*QaGRIIvT;4%41wY}{sc}CG!GeZ25A1~@7 zWU?t4`Gm2gL3J2ED-P%X!q13IP(OUW;A8wjte0OJdIuwT0shl4J9^%s`{*CepuHVh zNKqE9s4Ho2sE@oOCf~NF#A#PYcTrPP|2MFmq>>%mOvxFEiW);AP5riL17WW6$e4p) zRrDy&;t6tdel-4{viN@zG{Asllw5B<$QdWeHI(9L!~&=Bq-i&^Lb zDr&=bq?H)DC8hLoiY|)CoGeX0P3mNbT5+O0O;U{#ds1=>3H*N2V0t8uX+9}8;d<)j zy!+TW<$mM<#+HL|-qFaO#m*P`#&_5zL&~{58&Z%!%KYY1QeL5V zVHx?q(u}@kgs^yd%Q9kdK}OYTyduw}zz{!_-Iul+`-3B{B%kDNgF%EAwxL8pkT?Pef-g6rGd5sZGkArje1J-%^-46GeIe}cGQ z^{+RCI9{=>`3(NHlu?<0bBen1Nql|5qEr|g%{?u5$H1&c`af(##uO+Yy_@PBa}HG} z0l`w#Tb*+F7rIoDe{ddhTPoYri6o2Mw=F?@dEYnKA&WTt>-psV=74im#7KipkCw_^ ztJBnn->O)540l<@5d}I+r*71-re+3 zJJsV?ACp^w{jQmrT#sc{6iCxC$I^`)0R2>01+GOi^VUQMqi$J|r}m@M(lY!WAU#PH z2Ua4GD%ti6Vj=T1kAuGmU#%a6tLU(+6{Oiv#YiVn-gm9*BC)Z1V#7ncsokpVCw{P* zm%AAIRR27AEvBj2C7p{|S8%DX=(CbRAP8j80xoSUAp~I3 zh7SA+6nlN8C>6>-=P5h~9qbVZCqNULx&$_0U&TK@3Ci>L@J7MF)NtNfaGyMwW`g## zJ@+!`1U=zy1U+Jxb7=5H@H^@u$T<>7Z3Ej6!lVEiXQxlxg^q2DBaEQi8)P^e4!wp6 zzvIZ+PHo}zrTecS2#aj8;OC;>Dww%o+)C=fH}||CK=}1GEBqp|Xu}D79dh{UbD=Lc;H{%!DZ}zFydxwxZwK8(yiGYpFC~u2Lb)J*pXl}SBj`(XUT!0;^+qMX;A*(jr5`wdIr}+}I47xeP`j3pU!z}A+eo)l*T@W_ z>Tn1-nOJfllQ@kF_ng9q@!96*aeXXh!&HpG46hUmcFCfK|MKIdJG=JtDkQTSlId@v z*JUqgXW`Xc3vQ`kI5|SgEgzDC95!#16GuIzpMskxH}1b^n(X2(JOz+uoDGNP6NS{C z1Mb8^^3HP}6Tc>NvB8lQM&62DEIGVIU$uG-jH&ofsHHcX=t`I)k#-0QOC zIn`VX$@Qf7oD6ZYbOL9p=p<)66)&^|`p9R3dC}|0!@NhQRuI?d62Df$of~^#5#MNw`1#96`L3x`hgx{zWZ%w3^yS2g`fz%!W^QRe7gXUnYdH6n zW0C?n4sucgQ?;_0+L}X0%e3B4YQ&qO>d0Bb#*+<%P+;a4K+NEk?61KIdYx@D_LY-r zu8##$^Viep1wwGChW8??WN04GIHSHJf=);qs2k)CraURt;p!#N%O*I&!~=;V)DN{x z{DulvULk*xw-l?`=g32{Q&CpLuq5-OBjF&r;QJRJ6nxo#6u0Bgw{^psX@jlrF%!<# z^#N!WX>(~3FS)>OFo?dJx3ImN*3EuZw}Q*a94Y}h*VF5>9&mQ2RwNoxRmmk{4{BXv z0U1grsSDV9$$84`$PPjz7YAJ@{wIy_)!;($?)?+-X~IdiQ!$cPyR{zu%k@}qfhLe| zMi20gl#UEk(P_m&?en-#3dhz)bItRzi?47Ja}H!#awcX?)QBiXdW)!qe4QFW+#my! zby#i0Jq9yLp?{+S)ifU!+I&W-cI{Nvkx1wVh~4 zei1Q1Y);%B_!Tc#fj$>-BYBCdH&!HR-Oa)bgaun2&_Z6k!E4ljv*p4Y-ol3bes9{Z zwz##6n^#p=v!3&_e7H!-Ia2Z|b38Rzv_XA@+L3=;s3FU8#9AA=PG%FcnMg~!9^pnT zO1>8;#3h=`KGFCzWtHm`jFN@#-iA#SkJ&mOCHYYXedr|4=!HGJ39TRds%gij%+^(0 zRlQHOfb+U$P2o?DYvtsOFsi9+ylR-*P&`IBolMPtgIkkxbFMP22|BYp{679KEj&;M zk4s+Yvlx4$&T?tOPAO*W`i4H1IG8;^PY6yJWFn8ar_X26KfCVr&82PH54B)iLG$^l z_nZd}M+yTtwzZSBbDL$A1FG%RiZZSMA;rb!SPMBm{~n`7vJ5AzPjT9SQS z)?%jW)w`Hzk*w7WM0G@447`vmI`Lc-{eJj-&s}=+z@lbnF3@wdvYB(fBd5TWv$5q( zS{aqxXs%pJO{-<_!%0SEAzDv7FC9&|K=>9uJ|o75a?c+Bj+k5E)g#{!F(mvX zd#jSX0}9u#B_2U1&;O7If}i@hvLj$->td-Nc(}Syatzcj+AWR%$7ehdae&9_aA6KG zDjXE_0+)!X{I|e;*8lhmfX@+CyiMTjz@M}|xZCF?*Bzu?Va{>z=I$&i60+SIOaf4^ z!9Rivd!7%H^&8Fn)(f=?LJM-glo6%AXmb#4Q>+~5!-WD zWt%uYy$R9{RC>!oZNK!}s%o(r=~#G3bbzQ!|0-k}2ayUg6f z+l0l1|De6Fb%Ac&Nc5VI2Zun_E`KR)$II?5WCeO~>rCy6W@*rf-$vJ;+a=8qtnbk> zqxqK25^*ZeqiVCLh-MXB7Y=aC(_z6U&O6mi!5WT*AfA7S5@CZp4*8n#m~J8ug=cUd z6YYUxxf2OfpXr>{c(F?hwG-dG>lf*R4Vc|0VzHA3DqM_BIa?@QD4pBmBbh6H(>z5y zUv#drO0-7UTyR*pOHiLaRS?GSP^R%yc%y~3r#i`Qmjv<|@q1S%IgQw5R!OYK1qLy=19t1|Oo>W0qq|qER#Y{yL@DwkmFtD2 zvU&Np1ZO4J(uDl);yk53-#`RulMV}yqJLCyERX;4|=ykqF89_7C=*^$mLy;+3{ELrb(Wl9NJDSOsMCgqOE7RoF>85v`fpd(^4&r5Gmw_>MgYXL}6FyFzSpHk^CwY5btzc~;k{ZQ# z(~^1@@o3dXo`&vJ2+&E~f3kN8R@|M^17{X<62y7O)l{kQlUFDCKw#mlA?NcFc1HxnmX4Vc8r@8F1W?ik5(0$H`n#jPqXNrohLho0!epu;n$r zm-ANt6$X)oL$KJc80y?9aw#;e|0(p%dtO#2h|S5*jpwIkMWyWEUCP)ad&QfXwuENV zPRTFfuUuhbYrGZbyqXR(rfgfim(aAhqHLkSsn8_nCOSrpLgM z+~CwPah;sPB=U4U^;rEQz>8X-4Die)?PT>%xdc=E-o_fQ5su%&#Xiu#^b0T(>hYkl z=x6P>_D`b8)ywM|gi9(MN&$gcDU!2?f2?RYc{Wd7per4uM{^PG33_JM1?U}jf5y_d zeH>Bh;nVx5z9jCkk6P1Gj;9N`Mp5kKLwHDKHe+#-X#W;(Y=rklpVE4E*#msh!{%A- z?!u4tkL#8TC)aeAJQnCwR%Dm+50*72!8}g!9jPVVS@4f@oBKO&4iv%Nk-a?jB8QQ& zH1sA_mOA#Bn7o-d>hYDFpyD~rB(}&`TX*AU#DXmbSP}oKeh~VcJG1}2=yLnnwkF|| zme5*4@UwANiK}2qZC~~>{+=pL;yWIz9FGRDa*lD3ce=V9v8~2?R^$$e1%7s^Y9@h8?o_D$x z>j);a_GEtJn>20HT;K)Pm5Y<<)avDw3-@w46ZoR7aL&d=aO?}FhA64HoK^l|WMak~ z_pd~A@_olG_+z!mdN#gLK6`U2<}8ZPABU23>wv4~8e^p0Or69StXrl!#^^7dpfqIk zWq((UVT>kymEB_elHQg!F-^G&NjftW-Y6C@tKt-*1lE+(=|VSFXn>nw73+~_4etp% z+^K~w1g6@!b58@UTjZSeAV)u&dLTiH%ZMoRnlsZ1F{$lj3by z6*OP889Nj!5E`KWLQ4cQ(dJ`J-aQoX_(bQUPEHHCF{r8aFlQIKZi^l@3tgt~N!&&k z^f{|Q{)|=^r9IEM##ynD_9}Lez2geA9Hdt{qlqq(LC#vq4sj<1amI@p$%kOQu%2{@ z-72UcIzx}}lL)J08+kE!r$;4ii@P}9;V!_QS|?M_G2xc6WHYuye+MB#-}H7Xzl+be z3@JK94b>OqB4J_CCD|!KUgiy{E5AnbK(dQ>UVK?>L4TsMM4RcA;1S_wu6xWQ!A6ca zbT)rAHR7)&>Qd7^!nqI01CE)THX_|Rgwo*OH{T*7aWDPV#9pka*GS1y2wQe4?B)Ba zo#cyT(~3N$A0$^Yk4tWdOEr<=5s^e3tfkqoDNA98;0*9r(8&*p;qkk8!69wD20G9` zh0fs~@zCe;Io^&ZI1!YOwHbAQJhr)-G$O+EClOO|W{*rcFHzoHD1WS(SJf(OQY99i zlO`(lGww+Ud7t{Dm?4W0y%R-CSCWIm2=Qe=CWsM9V|4j(!u=ruyfDFJ|Gl&?uhIQB zcQ+m4xSC_YU1If*nn?|A<`ExBrrs<33SrRwS<#qwvw5amn7XJ+PZpHySGYsEH?c6| zpu|iwMI9_QRKbcXLe|(<9 zP3k%Q%&J#Q>fXNj7|~2odKd5<;(WK4A~I)TQ?%SRYgr{YC_9l0bM*_*~OQ^a)<7-mz2oAC+sPt$E+%?!f~3gBDq*wM>eW+)X*%!U_j5Rl={c zqRAw>X!B-*=1BBvaRwRD)uh-|xU=!Hd{X}Ginr4HIZq2FOa70cv*3zqZNspwpT|7L zv5`hWM39gMK@g-vKsu#UoMDFP+I!EQ?(SgdF1tI9$L{X#{N@+jYt5|nKJ&hFKi73g zb4Y2=98k3hA{adi37iD?$skfNeNci#Er<4tcGe#Sj|viZt_1e;*5%sL%$&(7SE&r< zsjZtS2pAo_jl2{3+iN;0i^g#l6XPi}4zA=)?o1xp#JO&H+MUWCFcXZmEQ!%h!C=C= zGyFaVq#@9cz_7}PI80~DAGF9IiIh>F2&%-MWnF+sa5QHx4a-YT!BY#^XQIDRVwi5h z^T>hpGSBNI4}j*}N_3{k4q&*C`!a_WoW0$eu03p7$2-GK7QwPj{)gFY8sWPznhf6b zV7OV^L-eI1)V3{l5Kh6Uy9JV@Z_2g+E#lyuJX)>*m$Hr;#O;dip*XPi23;mEf(@Qs zq`$yA=K|s+s@wj%-1Ea*ho^Ab2Ew|W*+6f&A%<1gl_sxe7Ppglaz>%~Bs6SgtIs10 z(o1xaNE1}4ZmO#Us}=mxTR^^4oil+JE|Mf)r>^3oqbn(sIl)14(g((6PZIGdl;9je zxI-P@AIIH0etW2%BOXz93fRcOa{VZ)pf6JPkeSvU#9P2f>4<|o;ncPr1Rr{uF&*g+ zW$L!peg`wvuBE$xSow!+Ep3fNmrSEh7Wzj!kRNlqgNjL)m_I!;iO1;9&Mt&;TKK*r z+|C2b2Oo2wJqJ2xu&YL^^scP5Av;+*b8FuVE{ze@eFSWWqdLU+L3&hM7GeO}YW%wv z0Hd{orLzHFRX}zmZGlXe>_d4jdL4C#e3HL3$dxq6UgqgeG}6C2&mu@@C-;SORY%+h zb2*p;M>|mL{Jpt48Efn47wK_k&=8;Vn-S3O2Rgw4-52ma^i3TZh(IX7{9o-7(AN-9 zq6Zu_*d0%)zZJ@)hm?yFMU6cd@eF#xuR*RR{N^n|<{*D_P9V*7C)mv`lgdI^^IOVt ziWriXdntbK5>!p}IK)BE46^cK(0e^Kw5C?7a~S0=CTE{s9*;gZ_+5GhE$)ny6r=0) z3ek6TrtE|D**lRJD~LdEg<#%0bR@yVRiU>c+pTl0u&$Lo9SxLzXX?;{IgX5V=x-^L z=xwbD(G1A3wL7REkYgM@skHf+Zf6e)0lQ_Nw`>HzW{@S_j62rxRD2OvsSgrW;FimH z!g<&$+}Hg5STW>j1-6$FlDIC|2xJ!f0oJi@151dVU)s;~#m>!s0w2JxOg=>q!TLn! zgX7q=ASU34rFm9SyRjFXZ79yTwR;cB9Ldx{J1bBAV+URwMk>|a5Z)p_ka-KJ#2PM( zA4Hr8Ugmxw$noDe-Gm6_40a9vP3M2!#IJj&Njhmc;93mbQwP&;siu=EEEmVaJoieD)qSZqF@4bCD(&DOhJP! z+$Qovyqe=dPD2c`-jUAKN|^1W_|i3uRN|HFa{3cuS#ma1OZXE#0r)~N28GjF3E7@r zls|-JPCv5r2jDV0|8<+tZ&zgJm?nP{{*L?acMPwH+X|{p0^&H2Cm|~1pM)_ zY%^^aqKs8V9j;Aex>6^VJch4P;h;B~L z$bJOvo+yc&>(f6bcHzuycN31V9%w~^H0ELHME*oZH>aFC0_%ZsPBUGNd&Axc=@Ijp z-@u;QiHuQTe~BEfrJdRFgua-1HR%!9Pk9(60^-SE17A_^kr!?FM5!V9I$gFprnAR~ zCBC9r{a&JD0;8o(n9YyUF6aN`zL9{uHjaknwcNfzPkJcC% zRdjku3~UE&-ywxgf-92LU^(q>R30#mY7E>*RZ!|ST%v3s`#T*Z9U@H~w-?`%ujZC1}mBRmtZ)muDizreum6t6TXE$?~@PldpvaP;!+Pb4vibX)PcfZ%v#Kw^FF9$&6uOqh%X$uN zWt>i$20Wqv+;W3PfouY|Q=I^>4IudlHNZ(sDk2As{uHMeSM;KUPxXJBzX}N2|EcTw z&gyBR7u*}lnXD3yOumqMnq4Th!7gSwi=CU386Sim)%ox!->Y~Py`Jlq1w#wjD--*H zZsw#d0$Kw7-#}Z+dyu}Nn9QenIuS{JdZ<@ECL9uH(Qcv zs~PnH&nbtYe>Vh?D}a?w)uefp?2$|2iQQLw(uI=FXwwzJCd(dGI`5u2KzNxeGMr#; z=Va-MDaYBX)xR;Tn2(h3t};fKd}$R4Zj?xhQs~a2nVFZtqx|Z`g+MlE&z8B=Z_M`r zgA_SDWy3$@&A<<*bmDW$!;uit(*be!VIi@1)U;J#-}O*=ig&tWr7)gLY^!Db%ZW5z zrbMy-(g(I`nddZ|rVk9E@_yA4I7{wS_?A9PlA75HnuPTUM}a^dBJvFN3cD}>M?o^2 zJ>HR5f#;k;h)1a9!@Z(^M<;dr3F`)58}|t2_uW^9^2WL!3yyLdJ0~;ZIo=i@@-fzT zQ)#O+bB~_fgk_L4(#m!?QmHAVLmy?#OnZKF)FKgvgPs7Zwt_o!Mck zY?{ev&_`E>z#B9R3SFQ(iv1ZUz-DPuf(tNJcr-GKD&?&TSWNL}C3)ybH|V8K^NAQ* z^-#O0`{3j*S7E~5qXrBA!x*4&<0*!>@%y>y0}J5IoFzRsNrzdtI%Q}Nros~2$Y&H9 z-&S0NZFJaz!_XKXj*rwGO5Daa`toASfIhc0GXVeC4#L!=wDL8)TaaAHN*g#aM zv9A=HWIwPb{u^l_wiY&s|HB55qeL;-h3Mmgy_la(zI-3dv&vmu1Ljs?5oZ$SX67W; zR?NGE3@mkX)I~UlL5mteI@wvJJVXjJtW_)|?vp=| z)e^VzWs+-zJ8+sfiGU@26P_ZtpfQ5Y`0I`Dczf}z%8lF&_~b$tHXHAlafW#V_bb7b z@el59WCT49cO_sdh{WCUz|rKmXO5RCdvL!7gH^AA)tyt7n`ou_lX8&SD+i>HssH0| zkt9>9;7OuWdkDektK|e~G zEQo?Gp#;3c;Iqc%+(>Y0MLqi<;8@Vi@}z}ltYrwO+3{lfTM8oL7!*ua_-lby(m9VH zY7c3;;{f?CF}(klk}j}{ljPU=$vRNx#+xR~lz^N=JO|MiE05=;uz{6LRPo<2y-prI$q&(wh=r!7HvsWaOi)^&NZOLZSy>$LVP~%X45SHso^5 z%#Y;<*=rf1{L4%k?6qCWm;fD%UqR0Tw@2iG9khr3DYVbjT94^eKk^qxDR~#EwePgz zo+{2_l6xxHT7ndmU6pPZzmz(1*NAe(Xz-ihmhd`Zh@USAXxYKN&y&`_=ah5)lxMSE zu=Deg%v|R1_GoxNV@BL1`cisgggsaVGW{KCqqGC-pHU}Lo;%XXTgY?!LKSNL-Ow-?@eIKXp3xGilpSM^=S6Hg5;xn&4)7H0;gG+x7{f zvM+8f2d*()H(B@a>80zblokMR+(9}{QTJ#R_Pw9Y1R1tFL|rJk+rbbAi(}eevR4TA zoAPNt1mXH8xKq4i+G+%uyH)kD&WC+YkzTf!6)zpn{lyp;&rbUQFA~IUvskU#$jz?6 z01MnyLYoSs)|XOJ!4$`>Bn@>{&r-$gAy3nHSw(-8>W1WWPmyRu?9&OeB*G5MIU3pu zn6k$u^7;(Th`HR2+Tq%x>=D)bvh6G%#mZa-LnHM_lheP8+_vRIQT+MgF9AOL=BAC* zhYafa2+9WNA4fk@D^=Xhl>ZvDF*V4thL@_W?8<>9qK#t5-c_u{LRps=^@m_(dph=C zp4!~FTg0_9U~ALadM&Z^HOp4jlpD-|=Z8?FfzG9sFz1=5zOu)Vbgi6_R8I> zx%157HILXF!<b9RqgE#@h; zfS8Unl&ukVv`ouI@b|Pdq&0K*wY=Tt!R|(3!@HO)^m4xka3=bU`*mnuE61USwg*$# zg)&}7+Zl=aH>ht)P&X5GSIE-1qAoKfs#Mffs!2gY-NEW*1E`m~Ns@c0-?b^?xoG>+ z&%%x9wA>heG@6+f&y7Xj+%|_D-0C0BW7@WA{m#>0V{F{dfo4p*Ll-R@8`0%pjKMh> z*Xs+h=al|hG!`$6P%E(E%oODb?0jme{3qrywoK-MIlgm=>O@7+CHd=KAJ^FXv1A5aQgC)%`}7rb2D zDeDQq4}v zH$jIApky)*DTc@=C^u!V$q|@`QfJbM=Ck4)QfiG^$R|E3&EOv)Vspm0kBD}u=h>eK zCu4QY&jeI>2>gT)EE_L9CQ>NuW+@0jwR6UzOx)R&UWRjx7 zZD5di#19Qs5fj}NKp^3fgN{0xfa&Pchp`^&&uNpGm*ub3WsEa|nMyKzmfIQNlD9pSnw{5*ZB<|S{roTi@2xy3gso!M>had{exM1CP2C_~7lVa@{cd5_A{-ZypT=R5YgldR2Ja^zK`NO-(>x@J&_kn#!CY^-&%#@7IynC zo^X(rUrphEVlF9(=dFj&XP@L`)3GVvSQyAT<{5(zoC-70Wwcs9dyq+8;9fy%A`d$7 zDaoYF_E??0LaA%e1j#1gJBtfxaD_?)wu=pryMf(*h_;2bi*&^uTgl zdtd9NeWU)V&XO%wEmK+%W{NQDcTy&6l0GBxC4Ca>#*yfwsHAC+&`Y4M8s#_h?i91Q zecXStSFrE0BUAF2f0&gqC5*peYFGe$8N~Cw1TF^H?we@SsaOXR`8~PJ5}~yit*#IC zF}*>`S3c2-xO)|I)ePu`EJDd7t&|YuLUg$3h*a5>AeM!H5=j&Cm&?$ zx!GH9!~Lu`p{JoSMwPFXwh5hcpF-^bRyx#@6_ot8aBX@wNmH#xbv%^_m0C**=aBq@ z=?wT;Hp7rdbeBZwrlNL;XlhGigW#xgb0v{KU3R&sh8rfy*|CsaDZG%}z$Eg$wu)dD zhY-qv_{>q?RDeytyzU8=0^D>cBsWtooA+vi`Z?<7>Y|<_lEq4>^EW3#KH44yBBk%l zCPKf&!?>#Dp{P#BZJa3RRDY>l!h5YuE&7Y=EYoI!+_XPy=6DcamBF@*UF1iPRXgx7KYqU0tM38mJL-l!#u4eMoNT>H)q>Z?!)q_(@i@ zt!t?gWgBZ77y_|QRngA7qCQ?|&W;sw@bn75jBduuM(q9HP|SKsD~9v zvMThP!c0jFIx=gqXbGB}MC4yXUyknO(pvpO`q-;lbv_6N7h}8bFX%0%%bo;;Vz(H{ z?G)^3<+ZkQEJEaNipQ>JQ4KzrPqb$`C(KEFnr1mhhwN3&#lZFN<-agsg|qY-1}fYj zK8>Mgz7r}itfXRo5vC_vY-K_|3Q@DVur@w5j5XNOb^n7P>>hiNb_Zu;47U6xb}RYj zJH++ESH?qxK~}WhLWrYvYNhzk_*H5Sz72_0((pU$56O{u_ww)3ow(11)5Qt6Q<G$QwSfz>DNt#*5D4>GigNJTf;|7BXvL-XHKO~6Pk^F zlzi3dry2*eoFF?2o~HDog=zu>IG)PjU+oB zk^n~+bNmR=AqeJe!XlqkxDfw#-30I~{-QmXwhljN(6zmV&MGIH&VnC=#fDDMhI!2D z7t5e+)R1X9oLSXM{e_&T*hS5$&z6-_4wXwKITZJTUQrBLnTZv+k{2XR;7ujbqPMWm zk*0(cFfl}iPbB<5qO;pK5Jfm)&!^rY)EMI0TA6PYy{7Gq7s4fmjqpn*PPdf)jrvwI zgT5G7rkV(aAkNFbgSGWG(zk%7yjc8*cC!E{{EzleW(t3dnw)r@E2U7Qo!B+x10hjN zNAh$ZFZvKE%k3?=o;YYPph}2~^e@}~ww}Ui#y8xlg1!3l>`TnG+96gaRjxKMMYw;I zY6cTQl*?eK?uwL42g_~6AOsg|7NS8;<_dlhz)rMqBWXa?ANEozA!H5XB8A|yie5>E z+#UljND^yGF43YVnA=62@|Q+f7%teZZ{Xi#9M)#@@Kk?w0@nvSq>N#IMYzZ#S$pen z(qJZe=XtRoBO(8*&<$RiagDzax{+AN`2-50&a#dJaUqi#B-%8eDReu^0k^Awg^aR( zVlE^f{e1Iw`7$}mxJioRKi01jzhIGlzg(`gR+lbtGgn%~&!QY5`|4JhuISIo zXogYkdEOVDT74O=&@hzu$xl@z`FBjMf*@Po{8EOO1lFcXFrv&d>r%I1SKd286A#+{ zf|t$VCnz|+EPm8V)>H-%+zcP0S9z2Sx$X!~M$=J3~HrK!~{>#JZ z5PBi~lcr2tMs8N+s<&b$D6*CQ&3M@kxogd9$#&_&vNTbe=zE@6uuX7vyM*V(GbC*1 z%;J=7xy!uDS{57$^WhP1G!zVlxzTCYX@mAON;Sn!Yj47JMo77a3dRoLq{?o!KaH79D8NLHxgO6j5{@>98Y1hb`q>F>DjM1uJJ>|^}vTXL98?#!U; z@K%$U_Uh9@a1Hq;CD9Q@TZ;gW~c3_W0VDRmI#hs;d=yrgpn#CQZq`9g&ZDVMS zVwTBv*E-q%^j2A{WU}URsiW|>$}Shje=pBXmvH}+VB_oAZ9>wPCCoZrV~`qlVJCP` z0naiPxJA%Pz(e+pZM~-vcH)HY281{JW9KqN3@El7LzEE1%_syN_15T%IMl?^y+%%} zw%3S|i6vbsf21XMk^Dl-f^<*m_7-ydM9~G*j7Xut7bOjv&TU0++8Du-pr5*KhefSM zyA+U$sZrJR2BNNs7j)f64YEf%K$IFRu=t=v#CfLYC@JcQ!GJO~)#`FlW7U&2f1&;> zVJmyllXJex#pVWsrW;7ATSNzsU&tUhU`S{9fMXY_zg=2 zKnGy+B-%xS)o)I7LCw`QP=Utd8h6TvstV-;N@2-lIiGwvr&$_GPDs;zq6LE|4jCL=O#M(ja*f;?; zQd(TMgICDWc2ep$(qhHx&Rx=dg8$kVOMIAg^O&fY7HupO`rzm2SMiS{Z)tAxiW@X4 zC3i|?s$vIQQ#?Vom=%!?iBB>fq+Sx%!KiK5__OJ=B04xyP!yQQngiH)J%#hAO4n_m zh_cL1PCZQG%6mH3DqjoAET~-qn) zY%=eMPnsSWGbwBXS3dL-l;;)L^2>5qmQW~>#z2O;=Wrbmv$piC7w_bm2_ZjtD?XEhVvPbPuUMsIr94_>iu9f9yJrUg&zfH*$;Dr^j zblzg#xy?Mbgku{(VE)A{@Jgf?(dn)e0TZCJ(^BqJ`lMMMDSe5&rER~u<@B2-bH^L9 z&alH0hiTC*H0|A;rT)(lQs<)-X!n*+lP9a!7hI9fR!Fj7(E;g~l<9&(v43nh_p_jA zb1ECjZ4O9aJY^xh+^h|nvbApj92jAzql{3ON*;7<8su};Z4dgc(hE(@o)u&_Ls(}K zX0rB&rKkC^ddxhv_NWqNNZBcs`)c_Gjgt4O^I022I{EkHK0%~(TFe*j72*5M3)mUF zV*$$;N7%5}box?8=-OM>F4lcJJ%vTZiHSou5I!8nfG;8v684G_+ezZ?$%vX(eg_u8 zZiXx~5GQJzO)zrS&UF1BWJZCHwiG##`BAmE#WuM{F}Xz$qn2($Ee}_UGEklVXnrbs zo2NU+7X59_DC2bNfUOr)g4r!j9g0WYZ%NLUjwrI_H z_%n8oZ8j*x*@-6%`Vg0KX7(`&-Oz&WSAbMx&*@jJUNb8el#nLc+26@1 zW$*6!1rtD0S2A5qWVH`KC(yDsH}L1KHsb*3Thp!&0T5*h%@}P@KCJSh{Vy|1uBL8J zdM2GrF~ssBuV4x$s8s9wdM_-LPXojfDpo4kzIcfr;zQ{!(hjP z;hm3Jn}~_+F-#A1p7{{NYgdgi4i2qp&|RS?mz8VMptAfO%5xw-(_J14$dV38w6wvP zC87z`Q{ln<7|MPBC{7(^g6BHsPO`(A_w*oAoUIXH^j#3(S@(OK_};+#P8|0Q zaiZl3rx!inT)-yovNOD7<<+d!H8VrY9Mqo~w)rcRjqtRLkFv+m%OsX07Cf`{wa^4u z!dLR%(;-l;AaQt$R7kY`bYkh@XYcea#Xsi`sosyc`gAIton$gM}^# zL=Bs#^KLesHgLG)>MOcc9M4j#my>lT@1oL%$;jxD@fm?hsp22>M_b2)=@1b1iKhhD z_%CFipdIv_%Q!<#Tk{ecAm6tgrZGrW!nu8G6>OGmcdP6j;MH+OnnH-M1c^sc8Kz;; z+NN4Vq(EKWs=dfxR*F-naXEPe#bwT{j51j~iSr9%5Ry=UXV zB^!P)09xUiM-Y;tw%t$LLAohW_deI)nS@4x!=!{%+fE`LdQTrWR>t(>%j8 zv8vim%M(5=U87ziaLe1EknkEaR>~G|`V-HKk*t?nV+7}!E5anaMR540^XxDvYr`W( z9FVu>HWW@xw>?fz?Cm#HFk`xB>auA?9i^INJgV)SDyfBUidEz`_UNz35LHLD z#S%`*Mb%T0G53z5LNLDlqVxgpWFjn%=A7C(PoQ8O46ESYV^}t|vuDw*>$;3(;MO&l zq50I`wr8n7$rk>BUP{|$#*MDq#`n}u9YJ~%eqNhVTixPja!_w=jMaB3eXEMKfpVvk zU8;T3*|{`Dxai$>f^<}HCUJ{+6;B*}LD0ZS3-jUjvnFpMv2Mf44Fm8?$a&2v@BvL> z`ycfjWfniax1i%Jqr7X(l0+qSEH~Z6soE%pUC4c=NjisydwN*?r1F<`sj{tPp-Lt% z%3Y^eC0(+8y;LANl<-6JU62{g6l~+(4E@cev*R{pum%{N8$|E{=*OD<*2b@|?G37y zQpvl~ySZmB{HaUciJ{JG|7CfHTh~@*u13Zi9~iziH0o<~%u1f-o5rEITZLA3=A4)R zmiwe%lkSpqCv=Feisnbh3fAzmLJxDxtP;buECzF6LkldXpIFleGJ!L;_ozt9DPC#s zn*Mb#u8YvarAXTEb$-JRx5c#QA#NIvnvXY3(I*l8miCkT#MRY0b6jWvqht(y{L!T&-8E6i_^Yq=c6;@ K9 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
mpr?uV zUi3$oiW-hX=%YgMz5^I10ljw|2G6H9M`CW;jJah%Fjpqa1x%bqP7rn$`vCC@mdg4E zJq|aS`FhV0oRRS-&l!wh{GGBIyhC@0J_^;*yn=qg6RAO~r@%5w^gnA*bxQC86*Z=C zICdEwB0IKkDOxW@_UuCck@#4fF?pg7`YV_t!hO<9fRBL5?gfneEMgjV8P5~N#ny8^ z?Ad_}XZPkF!98JBCXazE=89-6w2FQq=pb~6hW5G+XH%Em0FnRdYtLUoC2G$eTZtOb zcc7{IBhd}n=N*wZ&A6jTTdqPgas*}7 zAL^cio}+!!l#hlq&N>PDr7BCJ!4xWlEH37r>=YpkD3<&}GJ((HmAlKaDq&d8TU<0h zKH1U6A&uO857e`rf;L0(jOShu^p)0peF1WD%er&bNYJ|E$O+^<%a4)8sF?Z(UAs{| zrqhih=oN+m?K`wWXA=Ly%++w1pD+?zUZe}KQ{*5g0$nn{vJcpF@z)$WZlbVr`%zpU zKX$VOEaDspnhGsuE%8c%&e2Q8N)cB&f1EvvT-Po?;)v9?t{Fz6{<2=`Jb^+qA{&>Y z9ycu0rlNP64v9LVoa89BHj)2Sgk|Q0Z{2wM#MVF6gf`Vi{k&bSK!FPfX#7W z053N10{DU*;~5Pr+1~9aGb)65e)EHVC3mnuQ%$|;|Rks5a0GB=L55wnJ-K z9#{T{0FF7GPD1>1$a-)X8M@zpa4FI}a-tnT%@{1Nzk&k$?9@w8ue(kO!_j5!Mf7a+ zljbkbCQMZ$y7q{T9($qWU!d7Aac3_!Q?oE>HExPxeAEV9zj*)p|G)x%iYE=4!@71Q z1A#i#ed;jc(s9>=?ns}*+5t9Fd|-ClGvv3u)cR;t+2B`|5Ou4sNHB^{?K(iahrZlC z7d(T>Xilizfq7}kE};SvQ^d}l*ht;DZTGQnmD!0+s*$GA>8|*GGaS4!>g5e8}`38hF^h6Ih%uD zi@_hhkB`B$4rk-DFtF0QfDmNnDDb3nefi{CcPszYU)USrEf`0d%Kq7=H#{ znUh7B1*5k&5E9|5oACrLJ|gf3;SBzuR|0V=VartwL?)rm_(EEe>!Cx?Ao0#nEOd>i z>bL-XCT3ewU^}9dW*FW~crSVfR}(HXXTTc50Q>|#Luf!`;wKQyWd-#uoC00`~VNrn@-EY`*epxtHD1s)!;s81#M}^7AS^l zYPbwlQ@u4|kcM(Z1VN`Lg$x7qgEAc^!T#jEb>CqGxwdRNe30ywJ&K=CdbNEKzL+$$ zc|5+C#0wM>#*s=r-x3l@kykRonVfg0zJlHykAre>8@qJS6GX6D+xLSi=9h*f@Gvu8 zeH47dXcSF_oETFX{!j)T0oy|g+QT{>bf31pv=0uY9?Y(Ujnp+s7vP_imZ-z{G>Thb z7QUI>>UjkJlk9uB7gr*RJC%uJ3%4Bjj_VS{4#;uW`N{3RxIetY`ct3>7g9%o8634x z1oCYCPlv%%tfx>fG>bK_E(FSD2A5_+#~E4KOW^f%ToM&F(zH=T_z(3^;7WWB<%y>f zKSY^zc`Ejj{NBkv><^jhz)qZ-G`;@^E=^+Brp8f3FY5zwUBY9kVcbK3SvUh+#wXJ^ zgW0??C=9f4x7J<&zjNG5K0~FfZ#xe{x0xqwOv40*I4TUb<+;|s!Y`#Q^Q^)XD0eRH z#KveFPfo+4HOux7U>jA^{w(Zu<^0wkxT$i3S&s{qtyQhWAtYx75UxjDMB9h^A{qyd zg3$tX?GDhycPn8-v$?H1^C2e3dD}zi2TNjO8dfkquOEPK)3