mirror of
https://github.com/Intervention/image.git
synced 2025-08-01 03:20:17 +02:00
Add drawing methods
- Image::drawCircle() - Image::drawEllipse()
This commit is contained in:
@@ -4,6 +4,8 @@ namespace Intervention\Image\Drivers\Abstract;
|
|||||||
|
|
||||||
use Intervention\Image\Collection;
|
use Intervention\Image\Collection;
|
||||||
use Intervention\Image\EncodedImage;
|
use Intervention\Image\EncodedImage;
|
||||||
|
use Intervention\Image\Geometry\Circle;
|
||||||
|
use Intervention\Image\Geometry\Ellipse;
|
||||||
use Intervention\Image\Geometry\Point;
|
use Intervention\Image\Geometry\Point;
|
||||||
use Intervention\Image\Geometry\Rectangle;
|
use Intervention\Image\Geometry\Rectangle;
|
||||||
use Intervention\Image\Interfaces\CollectionInterface;
|
use Intervention\Image\Interfaces\CollectionInterface;
|
||||||
@@ -227,6 +229,22 @@ abstract class AbstractImage implements ImageInterface
|
|||||||
return $this->modify($modifier);
|
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
|
public function resize(?int $width = null, ?int $height = null): ImageInterface
|
||||||
{
|
{
|
||||||
return $this->modify(
|
return $this->modify(
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace Intervention\Image\Drivers\Abstract\Modifiers;
|
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\DrawableInterface;
|
||||||
use Intervention\Image\Interfaces\PointInterface;
|
use Intervention\Image\Interfaces\PointInterface;
|
||||||
use Intervention\Image\Traits\CanHandleInput;
|
use Intervention\Image\Traits\CanHandleInput;
|
||||||
@@ -16,4 +18,31 @@ class AbstractDrawModifier
|
|||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
53
src/Drivers/Gd/Modifiers/DrawEllipseModifier.php
Normal file
53
src/Drivers/Gd/Modifiers/DrawEllipseModifier.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||||
|
|
||||||
|
use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier;
|
||||||
|
use Intervention\Image\Interfaces\ImageInterface;
|
||||||
|
use Intervention\Image\Interfaces\ModifierInterface;
|
||||||
|
|
||||||
|
class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterface
|
||||||
|
{
|
||||||
|
public function apply(ImageInterface $image): ImageInterface
|
||||||
|
{
|
||||||
|
return $image->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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -3,8 +3,6 @@
|
|||||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||||
|
|
||||||
use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier;
|
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\ImageInterface;
|
||||||
use Intervention\Image\Interfaces\ModifierInterface;
|
use Intervention\Image\Interfaces\ModifierInterface;
|
||||||
|
|
||||||
@@ -14,58 +12,31 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte
|
|||||||
{
|
{
|
||||||
$image->eachFrame(function ($frame) {
|
$image->eachFrame(function ($frame) {
|
||||||
// draw background
|
// draw background
|
||||||
if ($this->rectangle()->hasBackgroundColor()) {
|
if ($this->drawable()->hasBackgroundColor()) {
|
||||||
imagefilledrectangle(
|
imagefilledrectangle(
|
||||||
$frame->getCore(),
|
$frame->getCore(),
|
||||||
$this->position->getX(),
|
$this->position->getX(),
|
||||||
$this->position->getY(),
|
$this->position->getY(),
|
||||||
$this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(),
|
$this->position->getX() + $this->drawable()->bottomRightPoint()->getX(),
|
||||||
$this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(),
|
$this->position->getY() + $this->drawable()->bottomRightPoint()->getY(),
|
||||||
$this->getBackgroundColor()
|
$this->getBackgroundColor()->toInt()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->rectangle()->hasBorder()) {
|
if ($this->drawable()->hasBorder()) {
|
||||||
// draw border
|
// draw border
|
||||||
imagesetthickness($frame->getCore(), $this->rectangle()->getBorderSize());
|
imagesetthickness($frame->getCore(), $this->drawable()->getBorderSize());
|
||||||
imagerectangle(
|
imagerectangle(
|
||||||
$frame->getCore(),
|
$frame->getCore(),
|
||||||
$this->position->getX(),
|
$this->position->getX(),
|
||||||
$this->position->getY(),
|
$this->position->getY(),
|
||||||
$this->position->getX() + $this->rectangle()->bottomRightPoint()->getY(),
|
$this->position->getX() + $this->drawable()->bottomRightPoint()->getX(),
|
||||||
$this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(),
|
$this->position->getY() + $this->drawable()->bottomRightPoint()->getY(),
|
||||||
$this->getBorderColor()
|
$this->getBorderColor()->toInt()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return $image;
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
58
src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php
Normal file
58
src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
class DrawEllipseModifier extends AbstractDrawModifier implements ModifierInterface
|
||||||
|
{
|
||||||
|
public function apply(ImageInterface $image): ImageInterface
|
||||||
|
{
|
||||||
|
return $image->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;
|
||||||
|
}
|
||||||
|
}
|
@@ -3,13 +3,12 @@
|
|||||||
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||||
|
|
||||||
use ImagickDraw;
|
use ImagickDraw;
|
||||||
use ImagickPixel;
|
|
||||||
use Intervention\Image\Drivers\Abstract\Modifiers\AbstractDrawModifier;
|
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\Drivers\Imagick\Color;
|
||||||
use Intervention\Image\Exceptions\DecoderException;
|
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
|
class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInterface
|
||||||
{
|
{
|
||||||
@@ -17,18 +16,18 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte
|
|||||||
{
|
{
|
||||||
// setup rectangle
|
// setup rectangle
|
||||||
$drawing = new ImagickDraw();
|
$drawing = new ImagickDraw();
|
||||||
$drawing->setFillColor($this->getBackgroundColor());
|
$drawing->setFillColor($this->getBackgroundColor()->getPixel());
|
||||||
if ($this->rectangle()->hasBorder()) {
|
if ($this->drawable()->hasBorder()) {
|
||||||
$drawing->setStrokeColor($this->getBorderColor());
|
$drawing->setStrokeColor($this->getBorderColor()->getPixel());
|
||||||
$drawing->setStrokeWidth($this->rectangle()->getBorderSize());
|
$drawing->setStrokeWidth($this->drawable()->getBorderSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
// build rectangle
|
// build rectangle
|
||||||
$drawing->rectangle(
|
$drawing->rectangle(
|
||||||
$this->position->getX(),
|
$this->position->getX(),
|
||||||
$this->position->getY(),
|
$this->position->getY(),
|
||||||
$this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(),
|
$this->position->getX() + $this->drawable()->bottomRightPoint()->getX(),
|
||||||
$this->position->getY() + $this->rectangle()->bottomRightPoint()->getY()
|
$this->position->getY() + $this->drawable()->bottomRightPoint()->getY()
|
||||||
);
|
);
|
||||||
|
|
||||||
$image->eachFrame(function ($frame) use ($drawing) {
|
$image->eachFrame(function ($frame) use ($drawing) {
|
||||||
@@ -38,39 +37,23 @@ class DrawRectangleModifier extends AbstractDrawModifier implements ModifierInte
|
|||||||
return $image;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
|
$color = parent::getBackgroundColor();
|
||||||
if (!is_a($color, Color::class)) {
|
if (!is_a($color, Color::class)) {
|
||||||
return new ImagickPixel('transparent');
|
throw new DecoderException('Unable to decode background color.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $color->getPixel();
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
48
src/Geometry/Circle.php
Normal file
48
src/Geometry/Circle.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Intervention\Image\Geometry;
|
||||||
|
|
||||||
|
class Circle extends Ellipse
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
int $diameter,
|
||||||
|
protected ?Point $pivot = null
|
||||||
|
) {
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
55
src/Geometry/Ellipse.php
Normal file
55
src/Geometry/Ellipse.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Intervention\Image\Geometry;
|
||||||
|
|
||||||
|
use Intervention\Image\Geometry\Traits\HasBackgroundColor;
|
||||||
|
use Intervention\Image\Geometry\Traits\HasBorder;
|
||||||
|
use Intervention\Image\Interfaces\DrawableInterface;
|
||||||
|
|
||||||
|
class Ellipse implements DrawableInterface
|
||||||
|
{
|
||||||
|
use HasBorder;
|
||||||
|
use HasBackgroundColor;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected int $width,
|
||||||
|
protected int $height,
|
||||||
|
protected ?Point $pivot = null
|
||||||
|
) {
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user