mirror of
https://github.com/Intervention/image.git
synced 2025-01-16 19:58:14 +01:00
Optimize & Refactor Crop- and ResizeCanvasModifiers (#1418)
* Refactor and simplify CropModifier::class * Refactor and simplify ResizeCanvasModifier::class
This commit is contained in:
parent
e07119bc97
commit
58f0afd3c7
@ -185,7 +185,7 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable
|
|||||||
|
|
||||||
return new self(
|
return new self(
|
||||||
array_map(
|
array_map(
|
||||||
fn($item) => $callback($item),
|
fn(mixed $item) => $callback($item),
|
||||||
$this->items,
|
$this->items,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -202,7 +202,7 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable
|
|||||||
return new self(
|
return new self(
|
||||||
array_filter(
|
array_filter(
|
||||||
$this->items,
|
$this->items,
|
||||||
fn($item) => $callback($item),
|
fn(mixed $item) => $callback($item),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class Colorspace implements ColorspaceInterface
|
|||||||
public function colorFromNormalized(array $normalized): ColorInterface
|
public function colorFromNormalized(array $normalized): ColorInterface
|
||||||
{
|
{
|
||||||
return new Color(...array_map(
|
return new Color(...array_map(
|
||||||
fn($classname, $value_normalized) => (new $classname(normalized: $value_normalized))->value(),
|
fn(string $classname, float $value_normalized) => (new $classname(normalized: $value_normalized))->value(),
|
||||||
self::$channels,
|
self::$channels,
|
||||||
$normalized,
|
$normalized,
|
||||||
));
|
));
|
||||||
|
@ -34,7 +34,7 @@ class Colorspace implements ColorspaceInterface
|
|||||||
public function colorFromNormalized(array $normalized): ColorInterface
|
public function colorFromNormalized(array $normalized): ColorInterface
|
||||||
{
|
{
|
||||||
return new Color(...array_map(
|
return new Color(...array_map(
|
||||||
fn($classname, $value_normalized) => (new $classname(normalized: $value_normalized))->value(),
|
fn(string $classname, float $value_normalized) => (new $classname(normalized: $value_normalized))->value(),
|
||||||
self::$channels,
|
self::$channels,
|
||||||
$normalized
|
$normalized
|
||||||
));
|
));
|
||||||
|
@ -34,7 +34,7 @@ class Colorspace implements ColorspaceInterface
|
|||||||
public function colorFromNormalized(array $normalized): ColorInterface
|
public function colorFromNormalized(array $normalized): ColorInterface
|
||||||
{
|
{
|
||||||
return new Color(...array_map(
|
return new Color(...array_map(
|
||||||
fn($classname, $value_normalized) => (new $classname(normalized: $value_normalized))->value(),
|
fn(string $classname, float $value_normalized) => (new $classname(normalized: $value_normalized))->value(),
|
||||||
self::$channels,
|
self::$channels,
|
||||||
$normalized
|
$normalized
|
||||||
));
|
));
|
||||||
|
@ -34,7 +34,7 @@ class Colorspace implements ColorspaceInterface
|
|||||||
public function colorFromNormalized(array $normalized): ColorInterface
|
public function colorFromNormalized(array $normalized): ColorInterface
|
||||||
{
|
{
|
||||||
return new Color(...array_map(
|
return new Color(...array_map(
|
||||||
fn($classname, $value_normalized) => (new $classname(normalized: $value_normalized))->value(),
|
fn($classname, float $value_normalized) => (new $classname(normalized: $value_normalized))->value(),
|
||||||
self::$channels,
|
self::$channels,
|
||||||
$normalized,
|
$normalized,
|
||||||
));
|
));
|
||||||
|
@ -6,6 +6,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers;
|
|||||||
|
|
||||||
use Intervention\Image\Drivers\Gd\Cloner;
|
use Intervention\Image\Drivers\Gd\Cloner;
|
||||||
use Intervention\Image\Exceptions\ColorException;
|
use Intervention\Image\Exceptions\ColorException;
|
||||||
|
use Intervention\Image\Interfaces\ColorInterface;
|
||||||
use Intervention\Image\Interfaces\FrameInterface;
|
use Intervention\Image\Interfaces\FrameInterface;
|
||||||
use Intervention\Image\Interfaces\ImageInterface;
|
use Intervention\Image\Interfaces\ImageInterface;
|
||||||
use Intervention\Image\Interfaces\SizeInterface;
|
use Intervention\Image\Interfaces\SizeInterface;
|
||||||
@ -23,9 +24,7 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
|||||||
{
|
{
|
||||||
$originalSize = $image->size();
|
$originalSize = $image->size();
|
||||||
$crop = $this->crop($image);
|
$crop = $this->crop($image);
|
||||||
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
$background = $this->driver()->handleInput($this->background);
|
||||||
$this->driver()->handleInput($this->background)
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($image as $frame) {
|
foreach ($image as $frame) {
|
||||||
$this->cropFrame($frame, $originalSize, $crop, $background);
|
$this->cropFrame($frame, $originalSize, $crop, $background);
|
||||||
@ -41,10 +40,10 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
|||||||
FrameInterface $frame,
|
FrameInterface $frame,
|
||||||
SizeInterface $originalSize,
|
SizeInterface $originalSize,
|
||||||
SizeInterface $resizeTo,
|
SizeInterface $resizeTo,
|
||||||
int $background
|
ColorInterface $background
|
||||||
): void {
|
): void {
|
||||||
// create new image with transparent background
|
// create new image with transparent background
|
||||||
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo);
|
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo, $background);
|
||||||
|
|
||||||
// define offset
|
// define offset
|
||||||
$offset_x = $resizeTo->pivot()->x() + $this->offset_x;
|
$offset_x = $resizeTo->pivot()->x() + $this->offset_x;
|
||||||
@ -56,6 +55,9 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
|||||||
$targetWidth = $targetWidth < $originalSize->width() ? $targetWidth + $offset_x : $targetWidth;
|
$targetWidth = $targetWidth < $originalSize->width() ? $targetWidth + $offset_x : $targetWidth;
|
||||||
$targetHeight = $targetHeight < $originalSize->height() ? $targetHeight + $offset_y : $targetHeight;
|
$targetHeight = $targetHeight < $originalSize->height() ? $targetHeight + $offset_y : $targetHeight;
|
||||||
|
|
||||||
|
// don't alpha blend for copy operation to keep transparent areas of original image
|
||||||
|
imagealphablending($modified, false);
|
||||||
|
|
||||||
// copy content from resource
|
// copy content from resource
|
||||||
imagecopyresampled(
|
imagecopyresampled(
|
||||||
$modified,
|
$modified,
|
||||||
@ -70,57 +72,6 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
|||||||
$targetHeight
|
$targetHeight
|
||||||
);
|
);
|
||||||
|
|
||||||
// don't alpha blend for covering areas
|
|
||||||
imagealphablending($modified, false);
|
|
||||||
|
|
||||||
// cover the possible newly created areas with background color
|
|
||||||
if ($resizeTo->width() > $originalSize->width() || $this->offset_x > 0) {
|
|
||||||
imagefilledrectangle(
|
|
||||||
$modified,
|
|
||||||
$originalSize->width() + ($this->offset_x * -1) - $resizeTo->pivot()->x(),
|
|
||||||
0,
|
|
||||||
$resizeTo->width(),
|
|
||||||
$resizeTo->height(),
|
|
||||||
$background
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cover the possible newly created areas with background color
|
|
||||||
if ($resizeTo->height() > $originalSize->height() || $this->offset_y > 0) {
|
|
||||||
imagefilledrectangle(
|
|
||||||
$modified,
|
|
||||||
($this->offset_x * -1) - $resizeTo->pivot()->x(),
|
|
||||||
$originalSize->height() + ($this->offset_y * -1) - $resizeTo->pivot()->y(),
|
|
||||||
($this->offset_x * -1) + $originalSize->width() - 1 - $resizeTo->pivot()->x(),
|
|
||||||
$resizeTo->height(),
|
|
||||||
$background
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cover the possible newly created areas with background color
|
|
||||||
if ((($this->offset_x * -1) - $resizeTo->pivot()->x() - 1) > 0) {
|
|
||||||
imagefilledrectangle(
|
|
||||||
$modified,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
($this->offset_x * -1) - $resizeTo->pivot()->x() - 1,
|
|
||||||
$resizeTo->height(),
|
|
||||||
$background
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cover the possible newly created areas with background color
|
|
||||||
if ((($this->offset_y * -1) - $resizeTo->pivot()->y() - 1) > 0) {
|
|
||||||
imagefilledrectangle(
|
|
||||||
$modified,
|
|
||||||
($this->offset_x * -1) - $resizeTo->pivot()->x(),
|
|
||||||
0,
|
|
||||||
($this->offset_x * -1) + $originalSize->width() - $resizeTo->pivot()->x() - 1,
|
|
||||||
($this->offset_y * -1) - $resizeTo->pivot()->y() - 1,
|
|
||||||
$background
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set new content as resource
|
// set new content as resource
|
||||||
$frame->setNative($modified);
|
$frame->setNative($modified);
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||||
|
|
||||||
use Intervention\Image\Colors\Rgb\Channels\Blue;
|
|
||||||
use Intervention\Image\Colors\Rgb\Channels\Green;
|
|
||||||
use Intervention\Image\Colors\Rgb\Channels\Red;
|
|
||||||
use Intervention\Image\Drivers\Gd\Cloner;
|
|
||||||
use Intervention\Image\Exceptions\ColorException;
|
|
||||||
use Intervention\Image\Interfaces\ColorInterface;
|
|
||||||
use Intervention\Image\Interfaces\FrameInterface;
|
|
||||||
use Intervention\Image\Interfaces\ImageInterface;
|
use Intervention\Image\Interfaces\ImageInterface;
|
||||||
use Intervention\Image\Interfaces\SizeInterface;
|
|
||||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||||
use Intervention\Image\Modifiers\ResizeCanvasModifier as GenericResizeCanvasModifier;
|
use Intervention\Image\Modifiers\ResizeCanvasModifier as GenericResizeCanvasModifier;
|
||||||
|
|
||||||
@ -25,62 +17,16 @@ class ResizeCanvasModifier extends GenericResizeCanvasModifier implements Specia
|
|||||||
*/
|
*/
|
||||||
public function apply(ImageInterface $image): ImageInterface
|
public function apply(ImageInterface $image): ImageInterface
|
||||||
{
|
{
|
||||||
$resize = $this->cropSize($image);
|
$cropSize = $this->cropSize($image);
|
||||||
$background = $this->driver()->handleInput($this->background);
|
|
||||||
|
|
||||||
foreach ($image as $frame) {
|
$image->modify(new CropModifier(
|
||||||
$this->modify($frame, $resize, $background);
|
$cropSize->width(),
|
||||||
}
|
$cropSize->height(),
|
||||||
|
$cropSize->pivot()->x(),
|
||||||
|
$cropSize->pivot()->y(),
|
||||||
|
$this->background,
|
||||||
|
));
|
||||||
|
|
||||||
return $image;
|
return $image;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ColorException
|
|
||||||
*/
|
|
||||||
protected function modify(
|
|
||||||
FrameInterface $frame,
|
|
||||||
SizeInterface $resize,
|
|
||||||
ColorInterface $background,
|
|
||||||
): void {
|
|
||||||
// create new canvas with target size & transparent background color
|
|
||||||
$modified = Cloner::cloneEmpty($frame->native(), $resize, $background);
|
|
||||||
|
|
||||||
// make image area transparent to keep transparency
|
|
||||||
// even if background-color is set
|
|
||||||
$transparent = imagecolorallocatealpha(
|
|
||||||
$modified,
|
|
||||||
$background->channel(Red::class)->value(),
|
|
||||||
$background->channel(Green::class)->value(),
|
|
||||||
$background->channel(Blue::class)->value(),
|
|
||||||
127,
|
|
||||||
);
|
|
||||||
|
|
||||||
// create transparent area to place the original on top
|
|
||||||
imagealphablending($modified, false); // do not blend / just overwrite
|
|
||||||
imagecolortransparent($modified, $transparent);
|
|
||||||
imagefilledrectangle(
|
|
||||||
$modified,
|
|
||||||
$resize->pivot()->x() * -1,
|
|
||||||
$resize->pivot()->y() * -1,
|
|
||||||
abs($resize->pivot()->x()) + $frame->size()->width() - 1,
|
|
||||||
abs($resize->pivot()->y()) + $frame->size()->height() - 1,
|
|
||||||
$transparent,
|
|
||||||
);
|
|
||||||
|
|
||||||
// place original
|
|
||||||
imagecopy(
|
|
||||||
$modified,
|
|
||||||
$frame->native(),
|
|
||||||
$resize->pivot()->x() * -1,
|
|
||||||
$resize->pivot()->y() * -1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
$frame->size()->width(),
|
|
||||||
$frame->size()->height(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// set new content as resource
|
|
||||||
$frame->setNative($modified);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||||
|
|
||||||
use ImagickDraw;
|
use Imagick;
|
||||||
use ImagickPixel;
|
use Intervention\Image\Drivers\Imagick\Driver;
|
||||||
use Intervention\Image\Interfaces\ImageInterface;
|
use Intervention\Image\Interfaces\ImageInterface;
|
||||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||||
use Intervention\Image\Modifiers\CropModifier as GenericCropModifier;
|
use Intervention\Image\Modifiers\CropModifier as GenericCropModifier;
|
||||||
@ -14,79 +14,54 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
|||||||
{
|
{
|
||||||
public function apply(ImageInterface $image): ImageInterface
|
public function apply(ImageInterface $image): ImageInterface
|
||||||
{
|
{
|
||||||
$originalSize = $image->size();
|
|
||||||
$crop = $this->crop($image);
|
$crop = $this->crop($image);
|
||||||
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
||||||
$this->driver()->handleInput($this->background)
|
$this->driver()->handleInput($this->background)
|
||||||
);
|
);
|
||||||
|
|
||||||
$transparent = new ImagickPixel('transparent');
|
// create empty container imagick to rebuild core
|
||||||
|
$imagick = new Imagick();
|
||||||
$draw = new ImagickDraw();
|
$resolution = $image->resolution()->perInch();
|
||||||
$draw->setFillColor($background);
|
|
||||||
|
|
||||||
foreach ($image as $frame) {
|
foreach ($image as $frame) {
|
||||||
$frame->native()->setBackgroundColor($transparent);
|
// create new frame canvas with modifiers background
|
||||||
$frame->native()->setImageBackgroundColor($transparent);
|
$canvas = new Imagick();
|
||||||
|
$canvas->newImage($crop->width(), $crop->height(), $background, 'png');
|
||||||
|
$canvas->setImageResolution($resolution->x(), $resolution->y());
|
||||||
|
|
||||||
// crop image
|
// set animation details
|
||||||
$frame->native()->extentImage(
|
if ($image->isAnimated()) {
|
||||||
$crop->width(),
|
$canvas->setImageDelay($frame->native()->getImageDelay());
|
||||||
$crop->height(),
|
$canvas->setImageIterations($frame->native()->getImageIterations());
|
||||||
$crop->pivot()->x() + $this->offset_x,
|
$canvas->setImageDispose($frame->native()->getImageDispose());
|
||||||
$crop->pivot()->y() + $this->offset_y
|
}
|
||||||
|
|
||||||
|
// place original frame content onto the empty colored frame canvas
|
||||||
|
$canvas->compositeImage(
|
||||||
|
$frame->native(),
|
||||||
|
Imagick::COMPOSITE_DEFAULT,
|
||||||
|
($crop->pivot()->x() + $this->offset_x) * -1,
|
||||||
|
($crop->pivot()->y() + $this->offset_y) * -1,
|
||||||
);
|
);
|
||||||
|
|
||||||
// repage
|
// copy alpha channel if available
|
||||||
$frame->native()->setImagePage(
|
if ($frame->native()->getImageAlphaChannel()) {
|
||||||
$crop->width(),
|
$canvas->compositeImage(
|
||||||
$crop->height(),
|
$frame->native(),
|
||||||
0,
|
version_compare(Driver::version(), '7.0.0', '>=') ?
|
||||||
0,
|
Imagick::COMPOSITE_COPYOPACITY :
|
||||||
);
|
Imagick::COMPOSITE_DSTIN,
|
||||||
|
($crop->pivot()->x() + $this->offset_x) * -1,
|
||||||
// cover the possible newly created areas with background color
|
($crop->pivot()->y() + $this->offset_y) * -1,
|
||||||
if ($crop->width() > $originalSize->width() || $this->offset_x > 0) {
|
|
||||||
$draw->rectangle(
|
|
||||||
$originalSize->width() + ($this->offset_x * -1) - $crop->pivot()->x(),
|
|
||||||
0,
|
|
||||||
$crop->width(),
|
|
||||||
$crop->height()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// cover the possible newly created areas with background color
|
// add newly built frame to container imagick
|
||||||
if ($crop->height() > $originalSize->height() || $this->offset_y > 0) {
|
$imagick->addImage($canvas);
|
||||||
$draw->rectangle(
|
|
||||||
($this->offset_x * -1) - $crop->pivot()->x(),
|
|
||||||
$originalSize->height() + ($this->offset_y * -1) - $crop->pivot()->y(),
|
|
||||||
($this->offset_x * -1) + $originalSize->width() - 1 - $crop->pivot()->x(),
|
|
||||||
$crop->height()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cover the possible newly created areas with background color
|
// replace imagick
|
||||||
if ((($this->offset_x * -1) - $crop->pivot()->x() - 1) > 0) {
|
$image->core()->setNative($imagick);
|
||||||
$draw->rectangle(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
($this->offset_x * -1) - $crop->pivot()->x() - 1,
|
|
||||||
$crop->height()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cover the possible newly created areas with background color
|
|
||||||
if ((($this->offset_y * -1) - $crop->pivot()->y() - 1) > 0) {
|
|
||||||
$draw->rectangle(
|
|
||||||
($this->offset_x * -1) - $crop->pivot()->x(),
|
|
||||||
0,
|
|
||||||
($this->offset_x * -1) + $originalSize->width() - $crop->pivot()->x() - 1,
|
|
||||||
($this->offset_y * -1) - $crop->pivot()->y() - 1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$frame->native()->drawImage($draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $image;
|
return $image;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
|||||||
use Imagick;
|
use Imagick;
|
||||||
use ImagickDraw;
|
use ImagickDraw;
|
||||||
use ImagickPixel;
|
use ImagickPixel;
|
||||||
use Intervention\Image\Interfaces\FrameInterface;
|
|
||||||
use Intervention\Image\Interfaces\ImageInterface;
|
use Intervention\Image\Interfaces\ImageInterface;
|
||||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||||
use Intervention\Image\Modifiers\FillModifier as ModifiersFillModifier;
|
use Intervention\Image\Modifiers\FillModifier as ModifiersFillModifier;
|
||||||
@ -16,12 +15,11 @@ class FillModifier extends ModifiersFillModifier implements SpecializedInterface
|
|||||||
{
|
{
|
||||||
public function apply(ImageInterface $image): ImageInterface
|
public function apply(ImageInterface $image): ImageInterface
|
||||||
{
|
{
|
||||||
$color = $this->driver()->handleInput($this->color);
|
$pixel = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
||||||
$pixel = $this->driver()
|
$this->driver()->handleInput($this->color)
|
||||||
->colorProcessor($image->colorspace())
|
);
|
||||||
->colorToNative($color);
|
|
||||||
|
|
||||||
foreach ($image as $frame) {
|
foreach ($image->core()->native() as $frame) {
|
||||||
if ($this->hasPosition()) {
|
if ($this->hasPosition()) {
|
||||||
$this->floodFillWithColor($frame, $pixel);
|
$this->floodFillWithColor($frame, $pixel);
|
||||||
} else {
|
} else {
|
||||||
@ -32,14 +30,14 @@ class FillModifier extends ModifiersFillModifier implements SpecializedInterface
|
|||||||
return $image;
|
return $image;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function floodFillWithColor(FrameInterface $frame, ImagickPixel $pixel): void
|
private function floodFillWithColor(Imagick $frame, ImagickPixel $pixel): void
|
||||||
{
|
{
|
||||||
$target = $frame->native()->getImagePixelColor(
|
$target = $frame->getImagePixelColor(
|
||||||
$this->position->x(),
|
$this->position->x(),
|
||||||
$this->position->y()
|
$this->position->y()
|
||||||
);
|
);
|
||||||
|
|
||||||
$frame->native()->floodfillPaintImage(
|
$frame->floodfillPaintImage(
|
||||||
$pixel,
|
$pixel,
|
||||||
100,
|
100,
|
||||||
$target,
|
$target,
|
||||||
@ -50,16 +48,16 @@ class FillModifier extends ModifiersFillModifier implements SpecializedInterface
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fillAllWithColor(FrameInterface $frame, ImagickPixel $pixel): void
|
private function fillAllWithColor(Imagick $frame, ImagickPixel $pixel): void
|
||||||
{
|
{
|
||||||
$draw = new ImagickDraw();
|
$draw = new ImagickDraw();
|
||||||
$draw->setFillColor($pixel);
|
$draw->setFillColor($pixel);
|
||||||
$draw->rectangle(
|
$draw->rectangle(0, 0, $frame->getImageWidth(), $frame->getImageHeight());
|
||||||
0,
|
$frame->drawImage($draw);
|
||||||
0,
|
|
||||||
$frame->native()->getImageWidth(),
|
// deactive alpha channel when image was filled with opaque color
|
||||||
$frame->native()->getImageHeight()
|
if ($pixel->getColorValue(Imagick::COLOR_ALPHA) == 1) {
|
||||||
);
|
$frame->setImageAlphaChannel(Imagick::ALPHACHANNEL_DEACTIVATE);
|
||||||
$frame->native()->drawImage($draw);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||||
|
|
||||||
use ImagickDraw;
|
|
||||||
use Intervention\Image\Interfaces\ImageInterface;
|
use Intervention\Image\Interfaces\ImageInterface;
|
||||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||||
use Intervention\Image\Modifiers\ResizeCanvasModifier as GenericResizeCanvasModifier;
|
use Intervention\Image\Modifiers\ResizeCanvasModifier as GenericResizeCanvasModifier;
|
||||||
@ -13,74 +12,15 @@ class ResizeCanvasModifier extends GenericResizeCanvasModifier implements Specia
|
|||||||
{
|
{
|
||||||
public function apply(ImageInterface $image): ImageInterface
|
public function apply(ImageInterface $image): ImageInterface
|
||||||
{
|
{
|
||||||
$size = $image->size();
|
$cropSize = $this->cropSize($image);
|
||||||
$resize = $this->cropSize($image);
|
|
||||||
|
|
||||||
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
$image->modify(new CropModifier(
|
||||||
$this->driver()->handleInput($this->background)
|
$cropSize->width(),
|
||||||
);
|
$cropSize->height(),
|
||||||
|
$cropSize->pivot()->x(),
|
||||||
foreach ($image as $frame) {
|
$cropSize->pivot()->y(),
|
||||||
$frame->native()->extentImage(
|
$this->background,
|
||||||
$resize->width(),
|
));
|
||||||
$resize->height(),
|
|
||||||
$resize->pivot()->x(),
|
|
||||||
$resize->pivot()->y()
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($resize->width() > $size->width()) {
|
|
||||||
// fill new emerged background
|
|
||||||
$draw = new ImagickDraw();
|
|
||||||
$draw->setFillColor($background);
|
|
||||||
|
|
||||||
$delta_width = abs($resize->pivot()->x());
|
|
||||||
$delta_height = $resize->pivot()->y() * -1;
|
|
||||||
|
|
||||||
if ($delta_width > 0) {
|
|
||||||
$draw->rectangle(
|
|
||||||
0,
|
|
||||||
$delta_height,
|
|
||||||
$delta_width - 1,
|
|
||||||
$delta_height + $size->height() - 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$draw->rectangle(
|
|
||||||
$size->width() + $delta_width,
|
|
||||||
$delta_height,
|
|
||||||
$resize->width(),
|
|
||||||
$delta_height + $size->height() - 1
|
|
||||||
);
|
|
||||||
|
|
||||||
$frame->native()->drawImage($draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($resize->height() > $size->height()) {
|
|
||||||
// fill new emerged background
|
|
||||||
$draw = new ImagickDraw();
|
|
||||||
$draw->setFillColor($background);
|
|
||||||
|
|
||||||
$delta = abs($resize->pivot()->y());
|
|
||||||
|
|
||||||
if ($delta > 0) {
|
|
||||||
$draw->rectangle(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
$resize->width(),
|
|
||||||
$delta - 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$draw->rectangle(
|
|
||||||
0,
|
|
||||||
$size->height() + $delta,
|
|
||||||
$resize->width(),
|
|
||||||
$resize->height()
|
|
||||||
);
|
|
||||||
|
|
||||||
$frame->native()->drawImage($draw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $image;
|
return $image;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ use Intervention\Image\Encoders\WebpEncoder;
|
|||||||
use Intervention\Image\Exceptions\NotSupportedException;
|
use Intervention\Image\Exceptions\NotSupportedException;
|
||||||
use Intervention\Image\Interfaces\EncoderInterface;
|
use Intervention\Image\Interfaces\EncoderInterface;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
|
use ReflectionParameter;
|
||||||
|
|
||||||
enum Format
|
enum Format
|
||||||
{
|
{
|
||||||
@ -155,7 +156,7 @@ enum Format
|
|||||||
$reflectionClass = new ReflectionClass($classname);
|
$reflectionClass = new ReflectionClass($classname);
|
||||||
if ($constructor = $reflectionClass->getConstructor()) {
|
if ($constructor = $reflectionClass->getConstructor()) {
|
||||||
$parameters = array_map(
|
$parameters = array_map(
|
||||||
fn($parameter): string => $parameter->getName(),
|
fn(ReflectionParameter $parameter): string => $parameter->getName(),
|
||||||
$constructor->getParameters(),
|
$constructor->getParameters(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -163,7 +164,7 @@ enum Format
|
|||||||
// filter out unavailable options of target encoder
|
// filter out unavailable options of target encoder
|
||||||
$options = array_filter(
|
$options = array_filter(
|
||||||
$options,
|
$options,
|
||||||
fn($key): bool => in_array($key, $parameters),
|
fn(mixed $key): bool => in_array($key, $parameters),
|
||||||
ARRAY_FILTER_USE_KEY,
|
ARRAY_FILTER_USE_KEY,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Intervention\Image\Tests;
|
namespace Intervention\Image\Tests;
|
||||||
|
|
||||||
use Imagick;
|
use Imagick;
|
||||||
|
use ImagickException;
|
||||||
use ImagickPixel;
|
use ImagickPixel;
|
||||||
use Intervention\Image\Decoders\FilePathImageDecoder;
|
use Intervention\Image\Decoders\FilePathImageDecoder;
|
||||||
use Intervention\Image\Drivers\Imagick\Core;
|
use Intervention\Image\Drivers\Imagick\Core;
|
||||||
@ -20,6 +21,14 @@ abstract class ImagickTestCase extends BaseTestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create test image with red (#ff0000) background
|
||||||
|
*
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return Image
|
||||||
|
* @throws ImagickException
|
||||||
|
*/
|
||||||
public static function createTestImage(int $width, int $height): Image
|
public static function createTestImage(int $width, int $height): Image
|
||||||
{
|
{
|
||||||
$background = new ImagickPixel('rgb(255, 0, 0)');
|
$background = new ImagickPixel('rgb(255, 0, 0)');
|
||||||
@ -29,6 +38,7 @@ abstract class ImagickTestCase extends BaseTestCase
|
|||||||
$imagick->setImageType(Imagick::IMGTYPE_UNDEFINED);
|
$imagick->setImageType(Imagick::IMGTYPE_UNDEFINED);
|
||||||
$imagick->setColorspace(Imagick::COLORSPACE_SRGB);
|
$imagick->setColorspace(Imagick::COLORSPACE_SRGB);
|
||||||
$imagick->setImageResolution(96, 96);
|
$imagick->setImageResolution(96, 96);
|
||||||
|
$imagick->setImageBackgroundColor($background);
|
||||||
|
|
||||||
return new Image(
|
return new Image(
|
||||||
new Driver(),
|
new Driver(),
|
||||||
|
@ -36,4 +36,55 @@ final class CropModifierTest extends GdTestCase
|
|||||||
$this->assertColor(0, 0, 255, 255, $image->pickColor(445, 16));
|
$this->assertColor(0, 0, 255, 255, $image->pickColor(445, 16));
|
||||||
$this->assertTransparency($image->pickColor(460, 16));
|
$this->assertTransparency($image->pickColor(460, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testModifySinglePixel(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(1, 1);
|
||||||
|
$this->assertEquals(1, $image->width());
|
||||||
|
$this->assertEquals(1, $image->height());
|
||||||
|
$image->modify(new CropModifier(3, 3, 0, 0, 'ff0', 'center'));
|
||||||
|
$this->assertEquals(3, $image->width());
|
||||||
|
$this->assertEquals(3, $image->height());
|
||||||
|
$this->assertColor(255, 255, 0, 255, $image->pickColor(0, 0));
|
||||||
|
$this->assertColor(255, 0, 0, 255, $image->pickColor(1, 1));
|
||||||
|
$this->assertColor(255, 255, 0, 255, $image->pickColor(2, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testModifyKeepsResolution(): void
|
||||||
|
{
|
||||||
|
$image = $this->readTestImage('300dpi.png');
|
||||||
|
$this->assertEquals(300, round($image->resolution()->perInch()->x()));
|
||||||
|
$image = $image->modify(new CropModifier(800, 100, -10, -10, 'ff0000'));
|
||||||
|
$this->assertEquals(300, round($image->resolution()->perInch()->x()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHalfTransparent(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(16, 16);
|
||||||
|
$image->modify(new CropModifier(32, 32, 0, 0, '00f5', 'center'));
|
||||||
|
$this->assertEquals(32, $image->width());
|
||||||
|
$this->assertEquals(32, $image->height());
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(5, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(16, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(30, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(5, 16));
|
||||||
|
$this->assertColor(255, 0, 0, 255, $image->pickColor(16, 16));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(30, 16));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(5, 30));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(16, 30));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(30, 30));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMergeTransparentBackgrounds(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(1, 1)->fill('f00');
|
||||||
|
$this->assertEquals(1, $image->width());
|
||||||
|
$this->assertEquals(1, $image->height());
|
||||||
|
$image->modify(new CropModifier(3, 3, 0, 0, '00f7', 'center'));
|
||||||
|
$this->assertEquals(3, $image->width());
|
||||||
|
$this->assertEquals(3, $image->height());
|
||||||
|
$this->assertColor(0, 0, 255, 119, $image->pickColor(0, 0));
|
||||||
|
$this->assertColor(255, 0, 0, 255, $image->pickColor(1, 1));
|
||||||
|
$this->assertColor(0, 0, 255, 119, $image->pickColor(2, 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,20 @@ final class ResizeCanvasModifierTest extends GdTestCase
|
|||||||
$this->assertColor(180, 224, 0, 255, $image->pickColor(2, 2));
|
$this->assertColor(180, 224, 0, 255, $image->pickColor(2, 2));
|
||||||
$this->assertColor(255, 255, 0, 255, $image->pickColor(17, 17));
|
$this->assertColor(255, 255, 0, 255, $image->pickColor(17, 17));
|
||||||
$this->assertTransparency($image->pickColor(12, 1));
|
$this->assertTransparency($image->pickColor(12, 1));
|
||||||
|
|
||||||
|
$image = $this->createTestImage(16, 16);
|
||||||
|
$image->modify(new ResizeCanvasModifier(32, 32, '00f5', 'center'));
|
||||||
|
$this->assertEquals(32, $image->width());
|
||||||
|
$this->assertEquals(32, $image->height());
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(5, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(16, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(30, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(5, 16));
|
||||||
|
$this->assertColor(255, 0, 0, 255, $image->pickColor(16, 16));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(30, 16));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(5, 30));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(16, 30));
|
||||||
|
$this->assertColor(0, 0, 255, 85, $image->pickColor(30, 30));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testModifyEdge(): void
|
public function testModifyEdge(): void
|
||||||
|
@ -64,7 +64,7 @@ final class PngEncoderTest extends ImagickTestCase
|
|||||||
yield [
|
yield [
|
||||||
static::createTestImage(3, 2)->fill('ccc'), // new grayscale
|
static::createTestImage(3, 2)->fill('ccc'), // new grayscale
|
||||||
new PngEncoder(indexed: true),
|
new PngEncoder(indexed: true),
|
||||||
'grayscale', // result should be 'indexed' but there seems to be no way to force this with imagick
|
'indexed',
|
||||||
];
|
];
|
||||||
yield [
|
yield [
|
||||||
static::readTestImage('circle.png'), // truecolor-alpha
|
static::readTestImage('circle.png'), // truecolor-alpha
|
||||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Modifiers;
|
namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Modifiers;
|
||||||
|
|
||||||
|
use Intervention\Image\Colors\Cmyk\Colorspace;
|
||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
||||||
use Intervention\Image\Modifiers\CropModifier;
|
use Intervention\Image\Modifiers\CropModifier;
|
||||||
@ -36,4 +37,63 @@ final class CropModifierTest extends ImagickTestCase
|
|||||||
$this->assertColor(0, 0, 255, 255, $image->pickColor(445, 16));
|
$this->assertColor(0, 0, 255, 255, $image->pickColor(445, 16));
|
||||||
$this->assertTransparency($image->pickColor(460, 16));
|
$this->assertTransparency($image->pickColor(460, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testModifySinglePixel(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(1, 1);
|
||||||
|
$this->assertEquals(1, $image->width());
|
||||||
|
$this->assertEquals(1, $image->height());
|
||||||
|
$image->modify(new CropModifier(3, 3, 0, 0, 'ff0', 'center'));
|
||||||
|
$this->assertEquals(3, $image->width());
|
||||||
|
$this->assertEquals(3, $image->height());
|
||||||
|
$this->assertColor(255, 255, 0, 255, $image->pickColor(0, 0));
|
||||||
|
$this->assertColor(255, 0, 0, 255, $image->pickColor(1, 1));
|
||||||
|
$this->assertColor(255, 255, 0, 255, $image->pickColor(2, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testModifyKeepsColorspace(): void
|
||||||
|
{
|
||||||
|
$image = $this->readTestImage('cmyk.jpg');
|
||||||
|
$this->assertInstanceOf(Colorspace::class, $image->colorspace());
|
||||||
|
$image = $image->modify(new CropModifier(800, 100, -10, -10, 'ff0000'));
|
||||||
|
$this->assertInstanceOf(Colorspace::class, $image->colorspace());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testModifyKeepsResolution(): void
|
||||||
|
{
|
||||||
|
$image = $this->readTestImage('300dpi.png');
|
||||||
|
$this->assertEquals(300, round($image->resolution()->perInch()->x()));
|
||||||
|
$image = $image->modify(new CropModifier(800, 100, -10, -10, 'ff0000'));
|
||||||
|
$this->assertEquals(300, round($image->resolution()->perInch()->x()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHalfTransparent(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(16, 16);
|
||||||
|
$image->modify(new CropModifier(32, 32, 0, 0, '00f5', 'center'));
|
||||||
|
$this->assertEquals(32, $image->width());
|
||||||
|
$this->assertEquals(32, $image->height());
|
||||||
|
$this->assertColor(0, 0, 255, 77, $image->pickColor(5, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 77, $image->pickColor(16, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 77, $image->pickColor(30, 5));
|
||||||
|
$this->assertColor(0, 0, 255, 77, $image->pickColor(5, 16));
|
||||||
|
$this->assertColor(255, 0, 0, 255, $image->pickColor(16, 16));
|
||||||
|
$this->assertColor(0, 0, 255, 77, $image->pickColor(30, 16));
|
||||||
|
$this->assertColor(0, 0, 255, 77, $image->pickColor(5, 30));
|
||||||
|
$this->assertColor(0, 0, 255, 77, $image->pickColor(16, 30));
|
||||||
|
$this->assertColor(0, 0, 255, 77, $image->pickColor(30, 30));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMergeTransparentBackgrounds(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(1, 1)->fill('f00');
|
||||||
|
$this->assertEquals(1, $image->width());
|
||||||
|
$this->assertEquals(1, $image->height());
|
||||||
|
$image->modify(new CropModifier(3, 3, 0, 0, '00f7', 'center'));
|
||||||
|
$this->assertEquals(3, $image->width());
|
||||||
|
$this->assertEquals(3, $image->height());
|
||||||
|
$this->assertColor(0, 0, 255, 127, $image->pickColor(0, 0), 1);
|
||||||
|
$this->assertColor(255, 0, 0, 255, $image->pickColor(1, 1));
|
||||||
|
$this->assertColor(0, 0, 255, 127, $image->pickColor(2, 2), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ final class ResizeCanvasModifierTest extends ImagickTestCase
|
|||||||
$this->assertColor(255, 255, 0, 255, $image->pickColor(17, 17));
|
$this->assertColor(255, 255, 0, 255, $image->pickColor(17, 17));
|
||||||
$this->assertTransparency($image->pickColor(12, 1));
|
$this->assertTransparency($image->pickColor(12, 1));
|
||||||
|
|
||||||
$image = $this->createTestImage(16, 16)->fill('f00');
|
$image = $this->createTestImage(16, 16);
|
||||||
$image->modify(new ResizeCanvasModifier(32, 32, '00f5', 'center'));
|
$image->modify(new ResizeCanvasModifier(32, 32, '00f5', 'center'));
|
||||||
$this->assertEquals(32, $image->width());
|
$this->assertEquals(32, $image->width());
|
||||||
$this->assertEquals(32, $image->height());
|
$this->assertEquals(32, $image->height());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user