From 6d744bf30223398a4627aab1f2a671a02dfcda23 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 23 Oct 2023 15:47:32 +0200 Subject: [PATCH] Add CropModifier --- src/Drivers/Abstract/AbstractImage.php | 12 +++ src/Drivers/Gd/Modifiers/CropModifier.php | 94 +++++++++++++++++++ .../Imagick/Modifiers/CropModifier.php | 38 ++++++++ .../Drivers/Gd/Modifiers/CropModifierTest.php | 28 ++++++ .../Imagick/Modifiers/CropModifierTest.php | 28 ++++++ 5 files changed, 200 insertions(+) create mode 100644 src/Drivers/Gd/Modifiers/CropModifier.php create mode 100644 src/Drivers/Imagick/Modifiers/CropModifier.php create mode 100644 tests/Drivers/Gd/Modifiers/CropModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/CropModifierTest.php diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 3a67cc21..bb9d647b 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -345,6 +345,18 @@ abstract class AbstractImage implements ImageInterface ); } + public function crop( + int $width, + int $height, + string $position = 'center', + int $offset_x = 0, + int $offset_y = 0 + ): ImageInterface { + return $this->modify( + $this->resolveDriverClass('Modifiers\CropModifier', $width, $height, $position, $offset_x, $offset_y) + ); + } + public function removeAnimation(int $position = 0): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Modifiers/CropModifier.php b/src/Drivers/Gd/Modifiers/CropModifier.php new file mode 100644 index 00000000..181df9f4 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/CropModifier.php @@ -0,0 +1,94 @@ +width, $this->height); + $crop->align($this->position); + $crop->alignPivotTo($image->getSize(), $this->position); + + foreach ($image as $frame) { + $this->cropFrame($frame, $crop); + } + + return $image; + } + + protected function cropFrame(FrameInterface $frame, SizeInterface $resizeTo): void + { + // create new image + $modified = imagecreatetruecolor( + $resizeTo->getWidth(), + $resizeTo->getHeight() + ); + + // get current image + $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); + } else { + imagesavealpha($modified, true); + } + + // copy content from resource + imagecopyresampled( + $modified, + $current, + 0, + 0, + $resizeTo->getPivot()->getX(), + $resizeTo->getPivot()->getY(), + $resizeTo->getWidth(), + $resizeTo->getHeight(), + $resizeTo->getWidth(), + $resizeTo->getHeight(), + ); + + 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); + } +} diff --git a/src/Drivers/Imagick/Modifiers/CropModifier.php b/src/Drivers/Imagick/Modifiers/CropModifier.php new file mode 100644 index 00000000..eae927ed --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/CropModifier.php @@ -0,0 +1,38 @@ +width, $this->height); + $crop->align($this->position); + $crop->alignPivotTo($image->getSize(), $this->position); + + foreach ($image as $frame) { + $frame->getCore()->extentImage( + $crop->getWidth(), + $crop->getHeight(), + $crop->getPivot()->getX() + $this->offset_x, + $crop->getPivot()->getY() + $this->offset_y + ); + } + + return $image; + } +} diff --git a/tests/Drivers/Gd/Modifiers/CropModifierTest.php b/tests/Drivers/Gd/Modifiers/CropModifierTest.php new file mode 100644 index 00000000..8f44df6d --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/CropModifierTest.php @@ -0,0 +1,28 @@ +createTestImage('blocks.png'); + $image = $image->modify(new CropModifier(200, 200, 'bottom-right')); + $image->toPng()->save('./test.png'); + $this->assertEquals(200, $image->getWidth()); + $this->assertEquals(200, $image->getHeight()); + $this->assertColor(255, 0, 0, 255, $image->pickColor(5, 5)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(100, 100)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(190, 190)); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/CropModifierTest.php b/tests/Drivers/Imagick/Modifiers/CropModifierTest.php new file mode 100644 index 00000000..54f39212 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/CropModifierTest.php @@ -0,0 +1,28 @@ +createTestImage('blocks.png'); + $image = $image->modify(new CropModifier(200, 200, 'bottom-right')); + $image->toPng()->save('./test.png'); + $this->assertEquals(200, $image->getWidth()); + $this->assertEquals(200, $image->getHeight()); + $this->assertColor(255, 0, 0, 255, $image->pickColor(5, 5)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(100, 100)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(190, 190)); + } +}