diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 1c32b466..6626cad3 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 @@ abstract class AbstractImage implements 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 00000000..1dfa0f13 --- /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 00000000..bb6f9be0 --- /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 1a9cc547..c7e5b1bb 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 @@ class Rectangle extends Polygon implements SizeInterface $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 @@ class Rectangle extends Polygon implements SizeInterface 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 00000000..5987a516 --- /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 00000000..f9df9900 --- /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 00000000..065d28de --- /dev/null +++ b/src/Interfaces/DrawableInterface.php @@ -0,0 +1,17 @@ +