diff --git a/README.md b/README.md index c4a44f6..1d5feda 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,11 @@ $manager = new ImageManager(['driver' => 'vips']); - [x] BackupCommand - [x] BlurCommand - [x] BrightnessCommand +- [x] ColorizeCommand +- [x] ContrastCommand - [x] CropCommand - [x] DestroyCommand +- [x] FillCommand - [x] FitCommand - [x] FlipCommand - [x] GammaCommand @@ -41,32 +44,29 @@ $manager = new ImageManager(['driver' => 'vips']); - [x] GreyscaleCommand - [x] HeightenCommand - [x] InsertCommand +- [x] InterlaceCommand - [x] InvertCommand +- [x] LimitColorsCommand +- [x] MaskCommand - [x] OpacityCommand - [x] PickColorCommand - [x] PixelCommand +- [x] PixelateCommand - [x] ResetCommand - [x] ResizeCommand +- [x] ResizeCanvasCommand - [x] RotateCommand - [x] SharpenCommand +- [x] TrimCommand - [x] WidenCommand -- [ ] ColorizeCommand -- [ ] ContrastCommand -- [ ] FillCommand -- [ ] InterlaceCommand -- [ ] LimitColorsCommand -- [ ] MaskCommand -- [ ] PixelateCommand -- [ ] ResizeCanvasCommand -- [ ] TrimCommand ### Shapes -- [ ] CircleShape -- [ ] EllipseShape -- [ ] LineShape -- [ ] PolygonShape -- [ ] RectangleShape +- [x] CircleShape +- [x] EllipseShape +- [x] LineShape +- [x] PolygonShape +- [x] RectangleShape ## Credits diff --git a/composer.json b/composer.json index 5a48fad..d692f39 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "require": { "php": "^7.1", "intervention/image": "^2.4", - "jcupitt/vips": "^1.0" + "jcupitt/vips": "^1.0", + "phenx/php-font-lib": "^0.5.2" }, "autoload": { "psr-4": { diff --git a/src/Color.php b/src/Color.php index 4503b16..db5228b 100644 --- a/src/Color.php +++ b/src/Color.php @@ -4,8 +4,9 @@ namespace Intervention\Image\Vips; +use ImagickPixel; +use ImagickPixelException; use Intervention\Image\AbstractColor; -use Intervention\Image\Exception\NotSupportedException; class Color extends AbstractColor { @@ -88,13 +89,13 @@ public function initFromString($value) /** * Initiate the color from the ImagickPixel object. * - * @param \ImagickPixel $value + * @param ImagickPixel $value * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @throws ImagickPixelException */ public function initFromObject($value) { - throw new NotSupportedException('VIPS color cannot be initiated from the ImagickPixel object.'); + $this->initFromArray($value->getColor()); } /** diff --git a/src/Commands/AbstractCommand.php b/src/Commands/AbstractCommand.php index c2ab216..8b3f0f5 100644 --- a/src/Commands/AbstractCommand.php +++ b/src/Commands/AbstractCommand.php @@ -5,9 +5,9 @@ namespace Intervention\Image\Vips\Commands; use Closure; -use Jcupitt\Vips\Image; -use Jcupitt\Vips\Exception; use Intervention\Image\Commands\AbstractCommand as BaseAbstractCommand; +use Jcupitt\Vips\Exception; +use Jcupitt\Vips\Image; abstract class AbstractCommand extends BaseAbstractCommand { @@ -61,7 +61,7 @@ protected function extractAlphaChannel(Image $image): Image /** * Handle the command. * - * @param \Closure $command + * @param Closure $command * @return bool */ protected function handleCommand(Closure $command): bool diff --git a/src/Commands/BlurCommand.php b/src/Commands/BlurCommand.php index cafa6db..44eb96a 100644 --- a/src/Commands/BlurCommand.php +++ b/src/Commands/BlurCommand.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Vips\Commands; +use Jcupitt\Vips\Image; + class BlurCommand extends AbstractCommand { /** @@ -19,6 +21,7 @@ public function execute($image): bool ->value(1); return $this->handleCommand(function () use ($image, $amount) { + /** @var Image $core */ $core = $image->getCore(); $core = $core->gaussblur($amount * 0.53); diff --git a/src/Commands/BrightnessCommand.php b/src/Commands/BrightnessCommand.php index f309e44..d8590fc 100644 --- a/src/Commands/BrightnessCommand.php +++ b/src/Commands/BrightnessCommand.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Vips\Commands; +use Jcupitt\Vips\Image; + class BrightnessCommand extends AbstractCommand { /** @@ -20,6 +22,7 @@ public function execute($image): bool ->value() * 2.55; return $this->handleCommand(function () use ($image, $level) { + /** @var Image $core */ $core = $image->getCore(); if ($core->hasAlpha()) { diff --git a/src/Commands/ColorizeCommand.php b/src/Commands/ColorizeCommand.php index fd0483d..59f1562 100644 --- a/src/Commands/ColorizeCommand.php +++ b/src/Commands/ColorizeCommand.php @@ -4,7 +4,7 @@ namespace Intervention\Image\Vips\Commands; -use Intervention\Image\Exception\NotSupportedException; +use Jcupitt\Vips\Image; class ColorizeCommand extends AbstractCommand { @@ -12,11 +12,57 @@ class ColorizeCommand extends AbstractCommand * Execute the command. * * @param \Intervention\Image\Image $image - * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @return bool */ public function execute($image) { - throw new NotSupportedException('Colorize command is not supported by VIPS driver.'); + $red = $this->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(); + + return $this->handleCommand( + function () use ($image, $red, $green, $blue) { + /** @var Image $core */ + $core = $image->getCore(); + + // calculate a and b for colors linear transformation + $a = [1, 1, 1]; + $b = [0, 0, 0]; + [$a[0], $b[0]] = $this->normalizeLevel($red); + [$a[1], $b[1]] = $this->normalizeLevel($green); + [$a[2], $b[2]] = $this->normalizeLevel($blue); + + if ($core->hasAlpha()) { + $flatten = $this->flattenImage($core); + + $mask = $this->extractAlphaChannel($core); + + $core = $flatten->linear($a, $b) + ->bandjoin($mask); + } else { + $core = $core->linear($a, $b); + } + + $image->setCore($core); + } + ); + } + + private function normalizeLevel($level) + { + if ($level > 0) { + return [1 - $level / 100, $level * 2.55]; + } + + return [1 + $level / 100, 0]; } } diff --git a/src/Commands/ContrastCommand.php b/src/Commands/ContrastCommand.php index fdaa12c..76640e3 100644 --- a/src/Commands/ContrastCommand.php +++ b/src/Commands/ContrastCommand.php @@ -4,7 +4,7 @@ namespace Intervention\Image\Vips\Commands; -use Intervention\Image\Exception\NotSupportedException; +use Jcupitt\Vips\Image; class ContrastCommand extends AbstractCommand { @@ -12,11 +12,34 @@ class ContrastCommand extends AbstractCommand * Execute the command. * * @param \Intervention\Image\Image $image - * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @return bool */ public function execute($image) { - throw new NotSupportedException('Contrast command is not supported by VIPS driver.'); + $level = $this->argument(0)->between(-100, 100)->required()->value(); + + return $this->handleCommand( + function () use ($image, $level) { + /** @var Image $core */ + $core = $image->getCore(); + + // calculate a and b for linear + $a = 1 + $level / 100; + $b = 255 * (1 - $a); + + if ($core->hasAlpha()) { + $flatten = $this->flattenImage($core); + + $mask = $this->extractAlphaChannel($core); + + $core = $flatten->linear([$a, $a, $a], [$b, $b, $b]) + ->bandjoin($mask); + } else { + $core = $core->linear([$a, $a, $a], [$b, $b, $b]); + } + + $image->setCore($core); + } + ); } } diff --git a/src/Commands/CropCommand.php b/src/Commands/CropCommand.php index 628ac49..5f6a29c 100644 --- a/src/Commands/CropCommand.php +++ b/src/Commands/CropCommand.php @@ -4,10 +4,9 @@ namespace Intervention\Image\Vips\Commands; -use Jcupitt\Vips\Image; -use Intervention\Image\Size; -use Intervention\Image\Point; use Intervention\Image\Exception\InvalidArgumentException; +use Jcupitt\Vips\Image; +use Jcupitt\Vips\Interesting; class CropCommand extends AbstractCommand { @@ -42,21 +41,15 @@ public function execute($image): bool ->type('digit') ->value(); - $size = new Size($width, $height); - - $position = new Point($x, $y); - - return $this->handleCommand(function () use ($image, $width, $height, $x, $y, $size, $position) { - if (is_null($x) && is_null($y)) { - $position = $image - ->getSize() - ->align('center') - ->relativePosition($size->align('center')); - } - + return $this->handleCommand(function () use ($image, $width, $height, $x, $y) { + /** @var Image $core */ $core = $image->getCore(); - $core = $core->crop($position->x, $position->y, $size->width, $size->height); + if (is_null($x) || is_null($y)) { + $core = $core->smartcrop($width, $height, ['interesting' => Interesting::CENTRE]); + } else { + $core = $core->crop($x, $y, $width, $height); + } $image->setCore($core); }); diff --git a/src/Commands/FillCommand.php b/src/Commands/FillCommand.php index 332886e..8446edb 100644 --- a/src/Commands/FillCommand.php +++ b/src/Commands/FillCommand.php @@ -4,11 +4,11 @@ namespace Intervention\Image\Vips\Commands; -use Jcupitt\Vips\Extend; -use Jcupitt\Vips\BlendMode; +use Intervention\Image\Exception\NotReadableException; use Intervention\Image\Image; use Intervention\Image\Vips\Color; -use Intervention\Image\Exception\NotReadableException; +use Jcupitt\Vips\BlendMode; +use Jcupitt\Vips\Extend; class FillCommand extends AbstractCommand { @@ -31,6 +31,7 @@ public function execute($image): bool ->value(); return $this->handleCommand(function () use ($image, $filling, $x, $y) { + /** @var \Jcupitt\Vips\Image $core */ $core = $image->getCore(); try { @@ -39,30 +40,51 @@ public function execute($image): bool $filling = new Color($filling); } - if (is_int($x) && is_int($y)) { - if ($filling instanceof Image) { - // - } elseif ($filling instanceof Color) { - // - } - } else { - if ($filling instanceof Image) { - $overlayCore = $filling->getCore(); + if ($filling instanceof Image) { + $overlayCore = $filling->getCore(); - $overlayCore = $overlayCore->embed(0, 0, $core->width, $core->height, [ + $overlayCore = $overlayCore->embed( + 0, + 0, + $core->width, + $core->height, + [ 'extend' => Extend::REPEAT, - 'background' => [0, 0, 0, 0], - ]); - - $core = $core->composite([$core, $overlayCore], BlendMode::OVER); - } elseif ($filling instanceof Color) { - $overlay = $image->getDriver()->newImage($core->width, $core->height, $filling->getArray()); + ] + ); + } elseif ($filling instanceof Color) { + $overlay = $image->getDriver()->newImage( + $core->width, + $core->height, + $filling->getRgba() + ); + $overlayCore = $overlay->getCore(); + } else { + return; + } - $overlayCore = $overlay->getCore(); + if (is_int($x) && is_int($y)) { + $mask = \Jcupitt\Vips\Image::black($core->width, $core->height); + $mask = $mask->draw_flood( + [255], + $x, + $y, + [ + 'equal' => true, + 'test' => $core, + ] + ); - $core = $core->composite([$core, $overlayCore], BlendMode::OVER); + if ($overlayCore->hasAlpha()) { + $mask = $mask->composite2( + $this->extractAlphaChannel($overlayCore), + BlendMode::DARKEN + ); + $overlayCore = $this->flattenImage($overlayCore); } + $overlayCore = $overlayCore->bandjoin($mask[0]); } + $core = $core->composite2($overlayCore, BlendMode::OVER); $image->setCore($core); }); diff --git a/src/Commands/FitCommand.php b/src/Commands/FitCommand.php index df0ca0c..bfe73d5 100644 --- a/src/Commands/FitCommand.php +++ b/src/Commands/FitCommand.php @@ -4,7 +4,6 @@ namespace Intervention\Image\Vips\Commands; -use Jcupitt\Vips\Image; use Intervention\Image\Size; class FitCommand extends AbstractCommand diff --git a/src/Commands/FlipCommand.php b/src/Commands/FlipCommand.php index e1ea759..e4a9f5d 100644 --- a/src/Commands/FlipCommand.php +++ b/src/Commands/FlipCommand.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Vips\Commands; use Jcupitt\Vips\Direction; +use Jcupitt\Vips\Image; class FlipCommand extends AbstractCommand { @@ -19,6 +20,7 @@ public function execute($image): bool $mode = strtolower((string) $this->argument(0)->value('h')); return $this->handleCommand(function () use ($image, $mode) { + /** @var Image $core */ $core = $image->getCore(); if (in_array($mode, ['2', 'v', 'vert', 'vertical'])) { diff --git a/src/Commands/GammaCommand.php b/src/Commands/GammaCommand.php index 1e28eea..2f82df6 100644 --- a/src/Commands/GammaCommand.php +++ b/src/Commands/GammaCommand.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Vips\Commands; +use Jcupitt\Vips\Image; + class GammaCommand extends AbstractCommand { /** @@ -20,6 +22,7 @@ public function execute($image): bool ->value(); return $this->handleCommand(function () use ($image, $gamma) { + /** @var Image $core */ $core = $image->getCore(); $core = $core->gamma(['exponent' => $gamma]); diff --git a/src/Commands/GetSizeCommand.php b/src/Commands/GetSizeCommand.php index f41ca43..d873c27 100644 --- a/src/Commands/GetSizeCommand.php +++ b/src/Commands/GetSizeCommand.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Vips\Commands; use Intervention\Image\Size; +use Jcupitt\Vips\Image; class GetSizeCommand extends AbstractCommand { @@ -17,6 +18,7 @@ class GetSizeCommand extends AbstractCommand public function execute($image): bool { return $this->handleCommand(function () use ($image) { + /** @var Image $core */ $core = $image->getCore(); $core = $core->autorot(); diff --git a/src/Commands/GreyscaleCommand.php b/src/Commands/GreyscaleCommand.php index 0c1e568..8f55199 100644 --- a/src/Commands/GreyscaleCommand.php +++ b/src/Commands/GreyscaleCommand.php @@ -4,6 +4,7 @@ namespace Intervention\Image\Vips\Commands; +use Jcupitt\Vips\Image; use Jcupitt\Vips\Interpretation; class GreyscaleCommand extends AbstractCommand @@ -17,6 +18,7 @@ class GreyscaleCommand extends AbstractCommand public function execute($image): bool { return $this->handleCommand(function () use ($image) { + /** @var Image $core */ $core = $image->getCore(); $core = $core->colourspace(Interpretation::B_W); diff --git a/src/Commands/HeightenCommand.php b/src/Commands/HeightenCommand.php index a7fcec0..8aa355b 100644 --- a/src/Commands/HeightenCommand.php +++ b/src/Commands/HeightenCommand.php @@ -27,7 +27,7 @@ public function execute($image): bool $this->arguments[1] = $height; - $this->arguments[2] = function ($constraint) use ($constraints) { + $this->arguments[2] = static function ($constraint) use ($constraints) { $constraint->aspectRatio(); if (is_callable($constraints)) { diff --git a/src/Commands/InsertCommand.php b/src/Commands/InsertCommand.php index 985d494..6696a1d 100644 --- a/src/Commands/InsertCommand.php +++ b/src/Commands/InsertCommand.php @@ -4,9 +4,9 @@ namespace Intervention\Image\Vips\Commands; -use Jcupitt\Vips\Image; -use Jcupitt\Vips\Extend; use Jcupitt\Vips\BlendMode; +use Jcupitt\Vips\Extend; +use Jcupitt\Vips\Image; class InsertCommand extends AbstractCommand { @@ -37,7 +37,9 @@ public function execute($image): bool return $this->handleCommand(function () use ($image, $source, $position, $x, $y) { $watermark = $image->getDriver()->init($source); + /** @var Image $imageCore */ $imageCore = $image->getCore(); + /** @var Image $watermarkCore */ $watermarkCore = $watermark->getCore(); $imageSize = $image->getSize()->align($position, $x, $y); @@ -51,9 +53,9 @@ public function execute($image): bool 'background' => [0, 0, 0, 0], ]); - $imageCore = $imageCore->composite([$imageCore, $watermarkCore], BlendMode::OVER); + $imageCore = $imageCore->composite2($watermarkCore, BlendMode::OVER); } else { - $imageCore = $imageCore->insert($watermarkCore->bandjoin(255), $target->x, $target->y); + $imageCore = $imageCore->insert($watermarkCore->bandjoin_const(255), $target->x, $target->y); } $image->setCore($imageCore); diff --git a/src/Commands/InterlaceCommand.php b/src/Commands/InterlaceCommand.php index f9e8915..b2b54ba 100644 --- a/src/Commands/InterlaceCommand.php +++ b/src/Commands/InterlaceCommand.php @@ -4,7 +4,7 @@ namespace Intervention\Image\Vips\Commands; -use Intervention\Image\Exception\NotSupportedException; +use Intervention\Image\Vips\Encoder; class InterlaceCommand extends AbstractCommand { @@ -13,10 +13,15 @@ class InterlaceCommand extends AbstractCommand * * @param \Intervention\Image\Image $image * @return void - * @throws \Intervention\Image\Exception\NotSupportedException */ public function execute($image) { - throw new NotSupportedException('Interlace command is not supported by VIPS driver.'); + $mode = $this->argument(0) + ->type('bool') + ->value(true); + + /** @var Encoder $encoder */ + $encoder = $image->getDriver()->encoder; + $encoder->interlace = $mode; } } diff --git a/src/Commands/InvertCommand.php b/src/Commands/InvertCommand.php index f32067c..a08f6e9 100644 --- a/src/Commands/InvertCommand.php +++ b/src/Commands/InvertCommand.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Vips\Commands; +use Jcupitt\Vips\Image; + class InvertCommand extends AbstractCommand { /** @@ -15,6 +17,7 @@ class InvertCommand extends AbstractCommand public function execute($image): bool { return $this->handleCommand(function () use ($image) { + /** @var Image $core */ $core = $image->getCore(); if ($core->hasAlpha()) { diff --git a/src/Commands/LimitColorsCommand.php b/src/Commands/LimitColorsCommand.php index 61c7318..5435895 100644 --- a/src/Commands/LimitColorsCommand.php +++ b/src/Commands/LimitColorsCommand.php @@ -4,7 +4,9 @@ namespace Intervention\Image\Vips\Commands; -use Intervention\Image\Exception\NotSupportedException; +use Intervention\Image\Vips\Color; +use Jcupitt\Vips\BlendMode; +use Jcupitt\Vips\Image; class LimitColorsCommand extends AbstractCommand { @@ -12,11 +14,75 @@ class LimitColorsCommand extends AbstractCommand * Execute the command. * * @param \Intervention\Image\Image $image - * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @return bool */ public function execute($image) { - throw new NotSupportedException('LimitColors command is not supported by VIPS driver.'); + $count = $this->argument(0)->type('int')->value(); + $matte = $this->argument(1)->value(); + + $bits = 8; + if ($count < 3) { + $bits = 1; + } elseif ($count < 5) { + $bits = 2; + } elseif ($count < 17) { + $bits = 4; + } + + return $this->handleCommand( + function () use ($image, $bits, $matte) { + /** @var Image $core */ + $core = $image->getCore(); + + $alpha = null; + if ($core->hasAlpha()) { + $alpha = $this->extractAlphaChannel($core); + $core = $this->flattenImage($core); + } + + if ($matte) { + $matteColor = new Color($matte); + + $canvas = $image->getDriver()->newImage( + $core->width, + $core->height, + $matteColor->getRgba() + ); + + $buffer = $core->pngsave_buffer( + [ + 'palette' => true, + 'bitdepth' => $bits, + 'dither' => 0.5, + 'Q' => 90, + ] + ); + $core = Image::pngload_buffer($buffer); + if ($alpha) { + $core = $core->bandjoin($alpha); + } + + $canvas = $canvas->getCore()->composite2($core, BlendMode::OVER); + + $image->setCore($canvas); + } else { + $buffer = $core->pngsave_buffer( + [ + 'palette' => true, + 'bitdepth' => $bits, + 'dither' => 0.5, + 'Q' => 90, + ] + ); + $core = Image::pngload_buffer($buffer); + if ($alpha) { + $core = $core->bandjoin($alpha); + } + + $image->setCore($core); + } + } + ); } } diff --git a/src/Commands/MaskCommand.php b/src/Commands/MaskCommand.php index 8f0b01f..587ef03 100644 --- a/src/Commands/MaskCommand.php +++ b/src/Commands/MaskCommand.php @@ -4,7 +4,8 @@ namespace Intervention\Image\Vips\Commands; -use Intervention\Image\Exception\NotSupportedException; +use Jcupitt\Vips\BlendMode; +use Jcupitt\Vips\Image; class MaskCommand extends AbstractCommand { @@ -12,11 +13,44 @@ class MaskCommand extends AbstractCommand * Execute the command. * * @param \Intervention\Image\Image $image - * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @return bool */ public function execute($image) { - throw new NotSupportedException('Mask command is not supported by VIPS driver.'); + $mask_source = $this->argument(0)->value(); + $mask_w_alpha = $this->argument(1) + ->type('bool') + ->value(false); + + return $this->handleCommand( + function () use ($image, $mask_source, $mask_w_alpha) { + $mask = $image->getDriver()->init($mask_source); + if ( + $mask->width() !== $image->width() || + $mask->height() !== $image->height() + ) { + $mask->resize($image->width(), $image->height()); + } + $mask = $mask->getCore(); + /** @var Image $mask */ + if ($mask_w_alpha) { + $mask = $this->extractAlphaChannel($mask); + } else { + $mask = $mask->bandmean(); + } + + /** @var Image $core */ + $core = $image->getCore(); + if ($core->hasAlpha()) { + $mask = $this->extractAlphaChannel($core) + ->composite2($mask, BlendMode::DARKEN); + $core = $this->flattenImage($core); + } + + $core = $core->bandjoin($mask); + + $image->setCore($core); + } + ); } } diff --git a/src/Commands/OpacityCommand.php b/src/Commands/OpacityCommand.php index 1e3c55f..200de4b 100644 --- a/src/Commands/OpacityCommand.php +++ b/src/Commands/OpacityCommand.php @@ -4,7 +4,7 @@ namespace Intervention\Image\Vips\Commands; -use Jcupitt\Vips\BlendMode; +use Jcupitt\Vips\Image; class OpacityCommand extends AbstractCommand { @@ -22,12 +22,11 @@ public function execute($image): bool ->value() / 100; return $this->handleCommand(function () use ($image, $transparency) { + /** @var Image $core */ $core = $image->getCore(); if (! $core->hasAlpha()) { - $background = $image->getDriver()->newImage($core->width, $core->height, [0, 0, 0, 0])->getCore(); - - $core = $background->composite([$background, $core], BlendMode::OVER); + $core = $core->bandjoin_const(255); } $core = $core->multiply([1.0, 1.0, 1.0, $transparency]); diff --git a/src/Commands/PickColorCommand.php b/src/Commands/PickColorCommand.php index 100b4c0..fc97148 100644 --- a/src/Commands/PickColorCommand.php +++ b/src/Commands/PickColorCommand.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Vips\Commands; use Intervention\Image\Vips\Color; +use Jcupitt\Vips\Image; class PickColorCommand extends AbstractCommand { @@ -31,6 +32,7 @@ public function execute($image): bool ->value('array'); return $this->handleCommand(function () use ($image, $x, $y, $format) { + /** @var Image $core */ $core = $image->getCore(); $pixel = $core->getpoint($x, $y); diff --git a/src/Commands/PixelCommand.php b/src/Commands/PixelCommand.php index fc24be6..9877bdc 100644 --- a/src/Commands/PixelCommand.php +++ b/src/Commands/PixelCommand.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Vips\Commands; use Intervention\Image\Vips\Color; +use Jcupitt\Vips\Image; class PixelCommand extends AbstractCommand { @@ -33,8 +34,13 @@ public function execute($image): bool ->value(); return $this->handleCommand(function () use ($image, $color, $x, $y) { + /** @var Image $core */ $core = $image->getCore(); + if (! $core->hasAlpha()) { + $core = $core->bandjoin_const(255); + } + $core = $core->draw_rect([$color->red, $color->green, $color->blue, $color->alpha], $x, $y, 1, 1); $image->setCore($core); diff --git a/src/Commands/PixelateCommand.php b/src/Commands/PixelateCommand.php index bf2d0e7..6dd859f 100644 --- a/src/Commands/PixelateCommand.php +++ b/src/Commands/PixelateCommand.php @@ -4,7 +4,8 @@ namespace Intervention\Image\Vips\Commands; -use Intervention\Image\Exception\NotSupportedException; +use Jcupitt\Vips\Image; +use Jcupitt\Vips\Kernel; class PixelateCommand extends AbstractCommand { @@ -12,11 +13,23 @@ class PixelateCommand extends AbstractCommand * Execute the command. * * @param \Intervention\Image\Image $image - * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @return bool */ public function execute($image) { - throw new NotSupportedException('Pixelate command is not supported by VIPS driver.'); + $size = $this->argument(0)->type('digit')->value(10); + + return $this->handleCommand( + function () use ($image, $size) { + /** @var Image $core */ + $core = $image->getCore(); + + $core = $core->resize(1 / $size) + ->resize($size, ['kernel' => Kernel::NEAREST]) + ->crop(0, 0, $image->width(), $image->height()); + + $image->setCore($core); + } + ); } } diff --git a/src/Commands/ResizeCanvasCommand.php b/src/Commands/ResizeCanvasCommand.php index fc61428..5973e70 100644 --- a/src/Commands/ResizeCanvasCommand.php +++ b/src/Commands/ResizeCanvasCommand.php @@ -4,7 +4,9 @@ namespace Intervention\Image\Vips\Commands; -use Intervention\Image\Exception\NotSupportedException; +use Intervention\Image\Vips\Color; +use Jcupitt\Vips\Extend; +use Jcupitt\Vips\Image; class ResizeCanvasCommand extends AbstractCommand { @@ -12,11 +14,68 @@ class ResizeCanvasCommand extends AbstractCommand * Execute the command. * * @param \Intervention\Image\Image $image - * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @return bool */ public function execute($image) { - throw new NotSupportedException('ResizeCanvas command is not supported by VIPS driver.'); + $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(false); + $bgcolor = $this->argument(4)->value('#ffffff'); + + $original_width = $image->getWidth(); + $original_height = $image->getHeight(); + + // check of only width or height is set + $width = is_null($width) ? $original_width : (int) $width; + $height = is_null($height) ? $original_height : (int) $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; + + return $this->handleCommand( + function () use ($image, $width, $height, $anchor, $bgcolor) { + + /** @var Image $core */ + $core = $image->getCore(); + + $anchor_to_gravity = [ + 'top-left' => 'north-west', + 'top' => 'north', + 'top-right' => 'north-east', + 'left' => 'west', + 'center' => 'centre', + 'right' => 'east', + 'bottom-left' => 'south-west', + 'bottom' => 'south', + 'bottom-right' => 'south-east', + ]; + + $color = new Color($bgcolor); + + if (! $core->hasAlpha()) { + $core = $core->bandjoin_const(255); + } + $core = $core->gravity( + $anchor_to_gravity[$anchor], + $width, + $height, + [ + 'extend' => Extend::BACKGROUND, + 'background' => [$color->red, $color->green, $color->blue, $color->alpha], + ] + ); + + $image->setCore($core); + } + ); } } diff --git a/src/Commands/ResizeCommand.php b/src/Commands/ResizeCommand.php index 8261406..91aeaf4 100644 --- a/src/Commands/ResizeCommand.php +++ b/src/Commands/ResizeCommand.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Vips\Commands; +use Jcupitt\Vips\Image; + class ResizeCommand extends AbstractCommand { /** @@ -23,6 +25,7 @@ public function execute($image): bool ->value(); return $this->handleCommand(function () use ($image, $width, $height, $constraints) { + /** @var Image $core */ $core = $image->getCore(); $size = $image->getSize()->resize($width, $height, $constraints); diff --git a/src/Commands/RotateCommand.php b/src/Commands/RotateCommand.php index b2381d2..27f280f 100644 --- a/src/Commands/RotateCommand.php +++ b/src/Commands/RotateCommand.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Vips\Commands; use Intervention\Image\Vips\Color; +use Jcupitt\Vips\Image; class RotateCommand extends AbstractCommand { @@ -24,6 +25,7 @@ public function execute($image): bool $color = new Color($this->argument(1)->value()); return $this->handleCommand(function () use ($image, $angle, $color) { + /** @var Image $core */ $core = $image->getCore(); switch ($angle) { @@ -50,7 +52,7 @@ public function execute($image): bool $background = [$color->red, $color->green, $color->blue, $color->alpha]; if (! $core->hasAlpha()) { - $core = $core->bandjoin(255); + $core = $core->bandjoin_const(255); } } else { $background = [$color->red, $color->green, $color->blue]; diff --git a/src/Commands/SharpenCommand.php b/src/Commands/SharpenCommand.php index 874304f..9b8c8ff 100644 --- a/src/Commands/SharpenCommand.php +++ b/src/Commands/SharpenCommand.php @@ -24,13 +24,14 @@ public function execute($image): bool $max = $amount * -0.025; $abs = ((4 * $min + 4 * $max) * -1) + 1; - return $this->handleCommand(function () use ($image, $amount, $min, $max, $abs) { + return $this->handleCommand(function () use ($image, $min, $max, $abs) { $mask = Image::newFromArray([ [$min, $max, $min], [$max, $abs, $max], [$min, $max, $min], ], 1, 0); + /** @var Image $core */ $core = $image->getCore(); $core = $core->conv($mask); diff --git a/src/Commands/TrimCommand.php b/src/Commands/TrimCommand.php index 2957003..37b1792 100644 --- a/src/Commands/TrimCommand.php +++ b/src/Commands/TrimCommand.php @@ -4,7 +4,7 @@ namespace Intervention\Image\Vips\Commands; -use Intervention\Image\Exception\NotSupportedException; +use Jcupitt\Vips\Image; class TrimCommand extends AbstractCommand { @@ -12,11 +12,116 @@ class TrimCommand extends AbstractCommand * Execute the command. * * @param \Intervention\Image\Image $image - * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @return bool */ public function execute($image) { - throw new NotSupportedException('Trim command is not supported by VIPS driver.'); + $base = $this->argument(0) + ->type('string') + ->value('top-left'); + $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; + } + + return $this->handleCommand( + function () use ($image, $base_x, $base_y, $checkTransparency, $away, $tolerance, $feather) { + /** @var Image $core */ + $core = $image->getCore(); + + $trim_core = $core; + + if ($checkTransparency) { + $point = [0, 0, 0]; + $trim_core = $this->extractAlphaChannel($core); + } else { + $point = $core->getpoint($base_x, $base_y); + unset($point[3]); + } + $trim = $trim_core->find_trim( + [ + 'background' => $point, + 'threshold' => $tolerance, + ] + ); + + $crop_x = $trim['left']; + $crop_y = $trim['top']; + $crop_width = $trim['width']; + $crop_height = $trim['height']; + + if (! in_array('right', $away, true)) { + $crop_width = $crop_width + ($image->width() - $crop_width - $crop_x); + } + + if (! in_array('bottom', $away, true)) { + $crop_height = $crop_height + ($image->height() - $crop_height - $crop_y); + } + + if (! in_array('left', $away, true)) { + $crop_width += $crop_x; + $crop_x = 0; + } + + if (! in_array('top', $away, true)) { + $crop_height += $crop_y; + $crop_y = 0; + } + + // add feather + $crop_width = min($image->width(), ($crop_width + $feather * 2)); + $crop_height = min($image->height(), ($crop_height + $feather * 2)); + $crop_x = max(0, ($crop_x - $feather)); + $crop_y = max(0, ($crop_y - $feather)); + + // crop image + $core = $core->crop($crop_x, $crop_y, $crop_width, $crop_height); + $image->setCore($core); + } + ); } } diff --git a/src/Commands/WidenCommand.php b/src/Commands/WidenCommand.php index 0312181..a6cacb7 100644 --- a/src/Commands/WidenCommand.php +++ b/src/Commands/WidenCommand.php @@ -27,7 +27,7 @@ public function execute($image): bool $this->arguments[1] = null; - $this->arguments[2] = function ($constraint) use ($constraints) { + $this->arguments[2] = static function ($constraint) use ($constraints) { $constraint->aspectRatio(); if (is_callable($constraints)) { diff --git a/src/Decoder.php b/src/Decoder.php index f6c13b9..edda47e 100644 --- a/src/Decoder.php +++ b/src/Decoder.php @@ -5,10 +5,11 @@ namespace Intervention\Image\Vips; use Imagick; -use Jcupitt\Vips\Image as VipsImage; +use ImagickPixelException; use Intervention\Image\AbstractDecoder; use Intervention\Image\Image as InterventionImage; -use Intervention\Image\Exception\NotSupportedException; +use Jcupitt\Vips\Exception; +use Jcupitt\Vips\Image as VipsImage; class Decoder extends AbstractDecoder { @@ -17,12 +18,13 @@ class Decoder extends AbstractDecoder * * @param string $path * @return \Intervention\Image\Image + * @throws \Jcupitt\Vips\Exception */ public function initFromPath($path): InterventionImage { $options = []; - if ($accessMode = \getenv('VIPS_ACCESS_MODE')) { + if ($accessMode = getenv('VIPS_ACCESS_MODE')) { $options['access'] = $accessMode; } @@ -37,6 +39,7 @@ public function initFromPath($path): InterventionImage * * @param string $data * @return \Intervention\Image\Image + * @throws Exception */ public function initFromBinary($data): InterventionImage { @@ -46,7 +49,7 @@ public function initFromBinary($data): InterventionImage /** * Create a new image from the VIPS object. * - * @param \Jcupitt\Vips\Image $resource + * @param \Jcupitt\Vips\Image $object * @return \Intervention\Image\Image */ public function initFromVips(VipsImage $object): InterventionImage @@ -59,11 +62,22 @@ public function initFromVips(VipsImage $object): InterventionImage * * @param \Resource $resource * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @throws Exception */ public function initFromGdResource($resource) { - throw new NotSupportedException('VIPS driver cannot be initiated from the GD resource.'); + ob_start(); + imagepng($resource); + $stringdata = ob_get_clean(); + $sizes = getimagesize($stringdata); + + VipsImage::newFromMemory( + $stringdata, + $sizes[0], + $sizes[1], + $sizes['channels'], + 'png' + ); } /** @@ -71,10 +85,16 @@ public function initFromGdResource($resource) * * @param \Imagick $object * @return void - * @throws \Intervention\Image\Exception\NotSupportedException + * @throws ImagickPixelException|Exception */ - public function initFromImagick(Imagick $imagick) + public function initFromImagick(Imagick $object) { - throw new NotSupportedException('VIPS driver cannot be initiated from the Imagick object.'); + VipsImage::newFromMemory( + $object->getImageBlob(), + $object->getImageWidth(), + $object->getImageHeight(), + count($object->getImagePixelColor(0, 0)->getColor()), + $object->getFormat() + ); } } diff --git a/src/Driver.php b/src/Driver.php index 5d9f43f..8525d14 100644 --- a/src/Driver.php +++ b/src/Driver.php @@ -4,22 +4,23 @@ namespace Intervention\Image\Vips; -use Jcupitt\Vips\Extend; -use Jcupitt\Vips\BandFormat; -use Jcupitt\Vips\Interpretation; -use Jcupitt\Vips\Image as VipsImage; use Intervention\Image\AbstractColor; use Intervention\Image\AbstractDriver; -use Intervention\Image\Image as InterventionImage; use Intervention\Image\Exception\NotSupportedException; +use Intervention\Image\Image as InterventionImage; +use Jcupitt\Vips\BandFormat; +use Jcupitt\Vips\Exception; +use Jcupitt\Vips\Extend; +use Jcupitt\Vips\Image as VipsImage; +use Jcupitt\Vips\Interpretation; class Driver extends AbstractDriver { /** * Create a new driver instance. * - * @param \Intervention\Image\Vips\Decoder $decoder - * @param \Intervention\Image\Vips\Encoder $encoder + * @param \Intervention\Image\Vips\Decoder|null $decoder + * @param \Intervention\Image\Vips\Encoder|null $encoder * @return void */ public function __construct(Decoder $decoder = null, Encoder $encoder = null) @@ -39,6 +40,7 @@ public function __construct(Decoder $decoder = null, Encoder $encoder = null) * @param int $height * @param mixed $background * @return \Intervention\Image\Image + * @throws Exception */ public function newImage($width, $height, $background = null): InterventionImage { diff --git a/src/Encoder.php b/src/Encoder.php index ad68f52..683470e 100644 --- a/src/Encoder.php +++ b/src/Encoder.php @@ -6,9 +6,12 @@ use Intervention\Image\AbstractEncoder; use Intervention\Image\Exception\NotSupportedException; +use Intervention\Image\Image; class Encoder extends AbstractEncoder { + public $interlace = false; + /** * Get the encoded image as JPEG string. * @@ -21,7 +24,7 @@ protected function processJpeg(): string ->writeToBuffer('.jpg', [ 'optimize_coding' => true, 'strip' => true, - 'interlace' => false, + 'interlace' => $this->interlace, 'Q' => $this->quality, ]); } @@ -37,6 +40,7 @@ protected function processPng(): string ->getCore() ->writeToBuffer('.png', [ 'compression' => (int) round(9 - ($this->quality * 9 / 100) + 0.5), + 'interlace' => $this->interlace, 'strip' => true, ]); } @@ -72,11 +76,15 @@ protected function processGif() * Get the encoded image as TIFF string. * * @return void - * @throws \Intervention\Image\Exception\NotSupportedException */ protected function processTiff() { - throw new NotSupportedException('TIFF format is not supported by VIPS driver.'); + return $this->image + ->getCore() + ->writeToBuffer('.tiff', [ + 'lossless' => false, + 'Q' => $this->quality, + ]); } /** @@ -111,4 +119,13 @@ protected function processPsd() { throw new NotSupportedException('PSD format is not supported by VIPS driver.'); } + + public function process(Image $image, $format = null, $quality = null) + { + $parent = parent::process($image, $format, $quality); + + $this->interlace = false; + + return $parent; + } } diff --git a/src/Font.php b/src/Font.php new file mode 100644 index 0000000..bb548da --- /dev/null +++ b/src/Font.php @@ -0,0 +1,228 @@ +createTextImage(); + + $core = $core->cast(BandFormat::UCHAR) + ->copy(['interpretation' => Interpretation::SRGB]) + ->bandjoin([$core, $core, $core]); + + $color = new Color($this->color); + + $core = $core->multiply( + [ + $color->red / 255, + $color->green / 255, + $color->blue / 255, + $color->alpha / 255, + ] + ); + + $angle = deg2rad($this->angle); + $posy += $core->height * cos($angle); + + $angle = -1 * ($this->angle % 365); + switch ($angle) { + case 0: + break; + + case 90: + case -270: + $core = $core->rot90(); + break; + + case 180: + case -180: + $core = $core->rot180(); + break; + + case -90: + case 270: + $core = $core->rot270(); + break; + + default: + $core = $core->rotate($angle, [ + 'background' => [0], + ]); + } + + $align = is_null($this->align) ? 'left' : strtolower($this->align); + $valign = is_null($this->valign) ? 'bottom' : strtolower($this->valign); + + $box = $this->getBoxSize(); + + $posy -= $core->height; + // correction on position depending on v/h alignment + switch ($align.'-'.$valign) { + + case 'center-top': + $posx -= round(($box[6] + $box[4]) / 2); + $posy -= round(($box[7] + $box[5]) / 2); + break; + + case 'right-top': + $posx -= $box[4]; + $posy -= $box[5]; + break; + + case 'left-top': + $posx -= $box[6]; + $posy -= $box[7]; + break; + + case 'center-center': + case 'center-middle': + $posx -= round(($box[0] + $box[4]) / 2); + $posy -= round(($box[1] + $box[5]) / 2); + break; + + case 'right-center': + case 'right-middle': + $posx -= round(($box[2] + $box[4]) / 2); + $posy -= round(($box[3] + $box[5]) / 2); + break; + + case 'left-center': + case 'left-middle': + $posx -= round(($box[0] + $box[6]) / 2); + $posy -= round(($box[1] + $box[7]) / 2); + break; + + case 'center-bottom': + $posx -= round(($box[0] + $box[2]) / 2); + $posy -= round(($box[1] + $box[3]) / 2); + break; + + case 'right-bottom': + $posx -= $box[2]; + $posy -= $box[3]; + break; + + case 'left-bottom': + $posx -= $box[0]; + $posy -= $box[1]; + break; + } + + /** @var ImageCore $imageCore */ + $imageCore = $image->getCore(); + + if (! $imageCore->hasAlpha()) { + $imageCore = $imageCore->bandjoin_const(255); + } + + $core = $core->embed( + $posx, + $posy, + $imageCore->width, + $imageCore->height, + [ + 'extend' => Extend::BACKGROUND, + 'background' => [0, 0, 0, 0], + ] + ); + + $imageCore = $imageCore->composite2($core, BlendMode::OVER); + + $image->setCore($imageCore); + } + + /** + * {@inheritdoc} + * @throws Exception + */ + public function getBoxSize() + { + $box = []; + + if ($this->text === '') { + // no text -> no boxsize + $box['width'] = 0; + $box['height'] = 0; + } else { + $core = $this->createTextImage(); + $box = [ + $core->xoffset, + $core->height, + $core->width + $core->xoffset, + $core->height, + $core->width + $core->xoffset, + 0, + $core->xoffset, + 0, + ]; + + if ($this->angle !== 0) { + $angle = deg2rad(360 - $this->angle); + + 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'] = (int) abs($box[4] - $box[0]); + $box['height'] = (int) abs($box[5] - $box[1]); + } + + return $box; + } + + /** + * @return ImageCore + * @throws Exception|FontNotFoundException + */ + private function createTextImage() + { + if ($this->hasApplicableFontFile()) { + $font = FontReader::load($this->file); + } else { + throw new RuntimeException( + 'Font file must be provided to apply text to image.' + ); + } + + $align_to_vips = [ + 'left' => Align::LOW, + 'right' => Align::CENTRE, + 'center' => Align::HIGH, + ]; + $core = ImageCore::text( + $this->text, + [ + 'font' => $font->getFontFullName().' '.$this->size, + 'fontfile' => $this->file, + 'align' => $align_to_vips[$this->align ?: 'left'], + ] + ); + + return $core; + } +} diff --git a/src/Shapes/AbstractShape.php b/src/Shapes/AbstractShape.php new file mode 100644 index 0000000..2b0e9e4 --- /dev/null +++ b/src/Shapes/AbstractShape.php @@ -0,0 +1,75 @@ +background) { + $attributes['fill'] = (new Color($this->background))->getRgba(); + } + if ($this->border_width) { + $attributes['stroke'] = (new Color($this->border_color))->getRgba(); + $attributes['stroke-width'] = $this->border_width; + } + + return $attributes; + } + + /** + * @param Image $image + * @param string $shape + * @param array $attributes + * @return bool + */ + protected function applyToImageViaSVG( + Image $image, + $shape, + array $attributes = [] + ): bool { + try { + /** @var \Jcupitt\Vips\Image $core */ + $core = $image->getCore(); + + $xml_attributes = implode( + ' ', + array_map( + static function ($key, $value) { + return sprintf( + '%s="%s"', + $key, + htmlspecialchars((string) $value) + ); + }, + array_keys($attributes), + $attributes + ) + ); + + $svg = << + <{$shape} {$xml_attributes} /> + +EOL; + $svgImage = $image->getDriver()->init($svg)->getCore(); + + $core = $core->composite([$svgImage], [BlendMode::OVER]); + + $image->setCore($core); + } catch (Exception $e) { + return false; + } + + return true; + } +} diff --git a/src/Shapes/CircleShape.php b/src/Shapes/CircleShape.php index 0d50525..51fc402 100644 --- a/src/Shapes/CircleShape.php +++ b/src/Shapes/CircleShape.php @@ -4,15 +4,8 @@ namespace Intervention\Image\Vips\Shapes; -use Intervention\Image\Image; - class CircleShape extends EllipseShape { - /** - * @var int - */ - public $diameter = 100; - /** * Create a new shape instance. * @@ -21,22 +14,6 @@ class CircleShape extends EllipseShape */ public function __construct($diameter = null) { - $this->width = is_numeric($diameter) ? (int) $diameter : $this->diameter; - $this->height = is_numeric($diameter) ? (int) $diameter : $this->diameter; - $this->diameter = is_numeric($diameter) ? (int) $diameter : $this->diameter; - } - - /** - * Draw the shape. - * - * @param \Intervention\Image\Image $image - * @param int $x - * @param int $y - * @return void - * @throws \Intervention\Image\Exception\NotSupportedException - */ - public function applyToImage(Image $image, $x = 0, $y = 0) - { - throw new NotSupportedException('Circle shape is not supported by VIPS driver.'); + parent::__construct($diameter, $diameter); } } diff --git a/src/Shapes/EllipseShape.php b/src/Shapes/EllipseShape.php index c43b51e..8bf56af 100644 --- a/src/Shapes/EllipseShape.php +++ b/src/Shapes/EllipseShape.php @@ -5,8 +5,6 @@ namespace Intervention\Image\Vips\Shapes; use Intervention\Image\Image; -use Intervention\Image\AbstractShape; -use Intervention\Image\Exception\NotSupportedException; class EllipseShape extends AbstractShape { @@ -36,14 +34,22 @@ public function __construct($width = null, $height = null) /** * Draw the shape. * - * @param \Intervention\Image\Image $image - * @param int $x - * @param int $y + * @param \Intervention\Image\Image $image + * @param int $x + * @param int $y * @return void - * @throws \Intervention\Image\Exception\NotSupportedException */ public function applyToImage(Image $image, $x = 0, $y = 0) { - throw new NotSupportedException('Ellipse shape is not supported by VIPS driver.'); + $this->applyToImageViaSVG( + $image, + 'ellipse', + [ + 'cx' => $x, + 'cy' => $y, + 'rx' => $this->width / 2, + 'ry' => $this->height / 2, + ] + $this->getSVGAttributes() + ); } } diff --git a/src/Shapes/LineShape.php b/src/Shapes/LineShape.php index 70305ca..fcc4960 100644 --- a/src/Shapes/LineShape.php +++ b/src/Shapes/LineShape.php @@ -5,8 +5,7 @@ namespace Intervention\Image\Vips\Shapes; use Intervention\Image\Image; -use Intervention\Image\AbstractShape; -use Intervention\Image\Exception\NotSupportedException; +use Intervention\Image\Vips\Color; class LineShape extends AbstractShape { @@ -72,10 +71,20 @@ public function width(int $width) * @param int $x * @param int $y * @return void - * @throws \Intervention\Image\Exception\NotSupportedException */ public function applyToImage(Image $image, $x = 0, $y = 0) { - throw new NotSupportedException('Line shape is not supported by VIPS driver.'); + $this->applyToImageViaSVG( + $image, + 'line', + [ + 'x1' => $this->x, + 'y1' => $this->y, + 'x2' => $x, + 'y2' => $y, + 'stroke-width' => $this->width, + 'stroke' => (new Color($this->color))->getRgba(), + ] + ); } } diff --git a/src/Shapes/PolygonShape.php b/src/Shapes/PolygonShape.php index 932afcf..1ed81f8 100644 --- a/src/Shapes/PolygonShape.php +++ b/src/Shapes/PolygonShape.php @@ -5,8 +5,6 @@ namespace Intervention\Image\Vips\Shapes; use Intervention\Image\Image; -use Intervention\Image\AbstractShape; -use Intervention\Image\Exception\NotSupportedException; class PolygonShape extends AbstractShape { @@ -33,10 +31,15 @@ public function __construct(array $points) * @param int $x * @param int $y * @return void - * @throws \Intervention\Image\Exception\NotSupportedException */ public function applyToImage(Image $image, $x = 0, $y = 0) { - throw new NotSupportedException('Polygon shape is not supported by VIPS driver.'); + $this->applyToImageViaSVG( + $image, + 'polygon', + [ + 'points' => implode(',', $this->points), + ] + $this->getSVGAttributes() + ); } } diff --git a/src/Shapes/RectangleShape.php b/src/Shapes/RectangleShape.php index efa4b83..f3b9e0a 100644 --- a/src/Shapes/RectangleShape.php +++ b/src/Shapes/RectangleShape.php @@ -5,8 +5,6 @@ namespace Intervention\Image\Vips\Shapes; use Intervention\Image\Image; -use Intervention\Image\AbstractShape; -use Intervention\Image\Exception\NotSupportedException; class RectangleShape extends AbstractShape { @@ -54,10 +52,18 @@ public function __construct($x1 = null, $y1 = null, $x2 = null, $y2 = null) * @param int $x * @param int $y * @return void - * @throws \Intervention\Image\Exception\NotSupportedException */ public function applyToImage(Image $image, $x = 0, $y = 0) { - throw new NotSupportedException('Rectangle shape is not supported by VIPS driver.'); + $this->applyToImageViaSVG( + $image, + 'rect', + [ + 'x' => $this->x1, + 'y' => $this->y1, + 'width' => $this->x2 - $this->x1, + 'height' => $this->y2 - $this->y1, + ] + $this->getSVGAttributes() + ); } }