mirror of
https://github.com/Intervention/image.git
synced 2025-01-16 11:49:02 +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(
|
||||
array_map(
|
||||
fn($item) => $callback($item),
|
||||
fn(mixed $item) => $callback($item),
|
||||
$this->items,
|
||||
)
|
||||
);
|
||||
@ -202,7 +202,7 @@ class Collection implements CollectionInterface, IteratorAggregate, Countable
|
||||
return new self(
|
||||
array_filter(
|
||||
$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
|
||||
{
|
||||
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,
|
||||
$normalized,
|
||||
));
|
||||
|
@ -34,7 +34,7 @@ class Colorspace implements ColorspaceInterface
|
||||
public function colorFromNormalized(array $normalized): ColorInterface
|
||||
{
|
||||
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,
|
||||
$normalized
|
||||
));
|
||||
|
@ -34,7 +34,7 @@ class Colorspace implements ColorspaceInterface
|
||||
public function colorFromNormalized(array $normalized): ColorInterface
|
||||
{
|
||||
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,
|
||||
$normalized
|
||||
));
|
||||
|
@ -34,7 +34,7 @@ class Colorspace implements ColorspaceInterface
|
||||
public function colorFromNormalized(array $normalized): ColorInterface
|
||||
{
|
||||
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,
|
||||
$normalized,
|
||||
));
|
||||
|
@ -6,6 +6,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
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\SizeInterface;
|
||||
@ -23,9 +24,7 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
||||
{
|
||||
$originalSize = $image->size();
|
||||
$crop = $this->crop($image);
|
||||
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
||||
$this->driver()->handleInput($this->background)
|
||||
);
|
||||
$background = $this->driver()->handleInput($this->background);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$this->cropFrame($frame, $originalSize, $crop, $background);
|
||||
@ -41,10 +40,10 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
||||
FrameInterface $frame,
|
||||
SizeInterface $originalSize,
|
||||
SizeInterface $resizeTo,
|
||||
int $background
|
||||
ColorInterface $background
|
||||
): void {
|
||||
// create new image with transparent background
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo);
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo, $background);
|
||||
|
||||
// define offset
|
||||
$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;
|
||||
$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
|
||||
imagecopyresampled(
|
||||
$modified,
|
||||
@ -70,57 +72,6 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
||||
$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
|
||||
$frame->setNative($modified);
|
||||
}
|
||||
|
@ -4,15 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
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\SizeInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ResizeCanvasModifier as GenericResizeCanvasModifier;
|
||||
|
||||
@ -25,62 +17,16 @@ class ResizeCanvasModifier extends GenericResizeCanvasModifier implements Specia
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$resize = $this->cropSize($image);
|
||||
$background = $this->driver()->handleInput($this->background);
|
||||
$cropSize = $this->cropSize($image);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$this->modify($frame, $resize, $background);
|
||||
}
|
||||
$image->modify(new CropModifier(
|
||||
$cropSize->width(),
|
||||
$cropSize->height(),
|
||||
$cropSize->pivot()->x(),
|
||||
$cropSize->pivot()->y(),
|
||||
$this->background,
|
||||
));
|
||||
|
||||
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;
|
||||
|
||||
use ImagickDraw;
|
||||
use ImagickPixel;
|
||||
use Imagick;
|
||||
use Intervention\Image\Drivers\Imagick\Driver;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\CropModifier as GenericCropModifier;
|
||||
@ -14,80 +14,55 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$originalSize = $image->size();
|
||||
$crop = $this->crop($image);
|
||||
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
||||
$this->driver()->handleInput($this->background)
|
||||
);
|
||||
|
||||
$transparent = new ImagickPixel('transparent');
|
||||
|
||||
$draw = new ImagickDraw();
|
||||
$draw->setFillColor($background);
|
||||
// create empty container imagick to rebuild core
|
||||
$imagick = new Imagick();
|
||||
$resolution = $image->resolution()->perInch();
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$frame->native()->setBackgroundColor($transparent);
|
||||
$frame->native()->setImageBackgroundColor($transparent);
|
||||
// create new frame canvas with modifiers background
|
||||
$canvas = new Imagick();
|
||||
$canvas->newImage($crop->width(), $crop->height(), $background, 'png');
|
||||
$canvas->setImageResolution($resolution->x(), $resolution->y());
|
||||
|
||||
// crop image
|
||||
$frame->native()->extentImage(
|
||||
$crop->width(),
|
||||
$crop->height(),
|
||||
$crop->pivot()->x() + $this->offset_x,
|
||||
$crop->pivot()->y() + $this->offset_y
|
||||
// set animation details
|
||||
if ($image->isAnimated()) {
|
||||
$canvas->setImageDelay($frame->native()->getImageDelay());
|
||||
$canvas->setImageIterations($frame->native()->getImageIterations());
|
||||
$canvas->setImageDispose($frame->native()->getImageDispose());
|
||||
}
|
||||
|
||||
// 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
|
||||
$frame->native()->setImagePage(
|
||||
$crop->width(),
|
||||
$crop->height(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
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()
|
||||
// copy alpha channel if available
|
||||
if ($frame->native()->getImageAlphaChannel()) {
|
||||
$canvas->compositeImage(
|
||||
$frame->native(),
|
||||
version_compare(Driver::version(), '7.0.0', '>=') ?
|
||||
Imagick::COMPOSITE_COPYOPACITY :
|
||||
Imagick::COMPOSITE_DSTIN,
|
||||
($crop->pivot()->x() + $this->offset_x) * -1,
|
||||
($crop->pivot()->y() + $this->offset_y) * -1,
|
||||
);
|
||||
}
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ($crop->height() > $originalSize->height() || $this->offset_y > 0) {
|
||||
$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
|
||||
if ((($this->offset_x * -1) - $crop->pivot()->x() - 1) > 0) {
|
||||
$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);
|
||||
// add newly built frame to container imagick
|
||||
$imagick->addImage($canvas);
|
||||
}
|
||||
|
||||
// replace imagick
|
||||
$image->core()->setNative($imagick);
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||
use Imagick;
|
||||
use ImagickDraw;
|
||||
use ImagickPixel;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\FillModifier as ModifiersFillModifier;
|
||||
@ -16,12 +15,11 @@ class FillModifier extends ModifiersFillModifier implements SpecializedInterface
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$color = $this->driver()->handleInput($this->color);
|
||||
$pixel = $this->driver()
|
||||
->colorProcessor($image->colorspace())
|
||||
->colorToNative($color);
|
||||
$pixel = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
||||
$this->driver()->handleInput($this->color)
|
||||
);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
foreach ($image->core()->native() as $frame) {
|
||||
if ($this->hasPosition()) {
|
||||
$this->floodFillWithColor($frame, $pixel);
|
||||
} else {
|
||||
@ -32,14 +30,14 @@ class FillModifier extends ModifiersFillModifier implements SpecializedInterface
|
||||
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->y()
|
||||
);
|
||||
|
||||
$frame->native()->floodfillPaintImage(
|
||||
$frame->floodfillPaintImage(
|
||||
$pixel,
|
||||
100,
|
||||
$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->setFillColor($pixel);
|
||||
$draw->rectangle(
|
||||
0,
|
||||
0,
|
||||
$frame->native()->getImageWidth(),
|
||||
$frame->native()->getImageHeight()
|
||||
);
|
||||
$frame->native()->drawImage($draw);
|
||||
$draw->rectangle(0, 0, $frame->getImageWidth(), $frame->getImageHeight());
|
||||
$frame->drawImage($draw);
|
||||
|
||||
// deactive alpha channel when image was filled with opaque color
|
||||
if ($pixel->getColorValue(Imagick::COLOR_ALPHA) == 1) {
|
||||
$frame->setImageAlphaChannel(Imagick::ALPHACHANNEL_DEACTIVATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||
|
||||
use ImagickDraw;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ResizeCanvasModifier as GenericResizeCanvasModifier;
|
||||
@ -13,74 +12,15 @@ class ResizeCanvasModifier extends GenericResizeCanvasModifier implements Specia
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$size = $image->size();
|
||||
$resize = $this->cropSize($image);
|
||||
$cropSize = $this->cropSize($image);
|
||||
|
||||
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
|
||||
$this->driver()->handleInput($this->background)
|
||||
);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$frame->native()->extentImage(
|
||||
$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);
|
||||
}
|
||||
}
|
||||
$image->modify(new CropModifier(
|
||||
$cropSize->width(),
|
||||
$cropSize->height(),
|
||||
$cropSize->pivot()->x(),
|
||||
$cropSize->pivot()->y(),
|
||||
$this->background,
|
||||
));
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ use Intervention\Image\Encoders\WebpEncoder;
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Interfaces\EncoderInterface;
|
||||
use ReflectionClass;
|
||||
use ReflectionParameter;
|
||||
|
||||
enum Format
|
||||
{
|
||||
@ -155,7 +156,7 @@ enum Format
|
||||
$reflectionClass = new ReflectionClass($classname);
|
||||
if ($constructor = $reflectionClass->getConstructor()) {
|
||||
$parameters = array_map(
|
||||
fn($parameter): string => $parameter->getName(),
|
||||
fn(ReflectionParameter $parameter): string => $parameter->getName(),
|
||||
$constructor->getParameters(),
|
||||
);
|
||||
}
|
||||
@ -163,7 +164,7 @@ enum Format
|
||||
// filter out unavailable options of target encoder
|
||||
$options = array_filter(
|
||||
$options,
|
||||
fn($key): bool => in_array($key, $parameters),
|
||||
fn(mixed $key): bool => in_array($key, $parameters),
|
||||
ARRAY_FILTER_USE_KEY,
|
||||
);
|
||||
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Intervention\Image\Tests;
|
||||
|
||||
use Imagick;
|
||||
use ImagickException;
|
||||
use ImagickPixel;
|
||||
use Intervention\Image\Decoders\FilePathImageDecoder;
|
||||
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
|
||||
{
|
||||
$background = new ImagickPixel('rgb(255, 0, 0)');
|
||||
@ -29,6 +38,7 @@ abstract class ImagickTestCase extends BaseTestCase
|
||||
$imagick->setImageType(Imagick::IMGTYPE_UNDEFINED);
|
||||
$imagick->setColorspace(Imagick::COLORSPACE_SRGB);
|
||||
$imagick->setImageResolution(96, 96);
|
||||
$imagick->setImageBackgroundColor($background);
|
||||
|
||||
return new Image(
|
||||
new Driver(),
|
||||
|
@ -36,4 +36,55 @@ final class CropModifierTest extends GdTestCase
|
||||
$this->assertColor(0, 0, 255, 255, $image->pickColor(445, 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(255, 255, 0, 255, $image->pickColor(17, 17));
|
||||
$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
|
||||
|
@ -64,7 +64,7 @@ final class PngEncoderTest extends ImagickTestCase
|
||||
yield [
|
||||
static::createTestImage(3, 2)->fill('ccc'), // new grayscale
|
||||
new PngEncoder(indexed: true),
|
||||
'grayscale', // result should be 'indexed' but there seems to be no way to force this with imagick
|
||||
'indexed',
|
||||
];
|
||||
yield [
|
||||
static::readTestImage('circle.png'), // truecolor-alpha
|
||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Modifiers;
|
||||
|
||||
use Intervention\Image\Colors\Cmyk\Colorspace;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
||||
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->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->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'));
|
||||
$this->assertEquals(32, $image->width());
|
||||
$this->assertEquals(32, $image->height());
|
||||
|
Loading…
x
Reference in New Issue
Block a user