diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31947a5d..af59d644 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -68,7 +68,7 @@ testing, it must also run without errors. Check the analyzer by running the following command. ```bash -./vendor/bin/phpstan analyze --memory-limit=512M --level=4 ./src +./vendor/bin/phpstan analyze --memory-limit=512M ./src ``` Or by using the project's Docker environment. diff --git a/readme.md b/readme.md index 6241fd1f..bb082054 100644 --- a/readme.md +++ b/readme.md @@ -66,22 +66,6 @@ $encoded->save('images/example.jpg'); - GD Library - Imagick PHP extension -## Development & Testing - -This package contains a Docker image for building a test suite and an analysis -container. You must have Docker installed on your system to run all tests using -the following command. - -```bash -docker-compose run --rm --build tests -``` - -Run the static analyzer on the code base. - -```bash -docker-compose run --rm --build analysis -``` - ## Security If you discover any security related issues, please email oliver@intervention.io directly. diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index 68c6e0db..5aad25a4 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -10,7 +10,7 @@ use Intervention\Image\Colors\Cmyk\Channels\Magenta; use Intervention\Image\Colors\Cmyk\Channels\Yellow; use Intervention\Image\Colors\Cmyk\Channels\Key; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; -use Intervention\Image\Drivers\AbstractInputHandler; +use Intervention\Image\InputHandler; use Intervention\Image\Interfaces\ColorChannelInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -44,11 +44,9 @@ class Color extends AbstractColor */ public static function create(mixed $input): ColorInterface { - return (new class ([ + return InputHandler::withDecoders([ Decoders\StringColorDecoder::class, - ]) extends AbstractInputHandler - { - })->handle($input); + ])->handle($input); } /** diff --git a/src/Colors/Hsl/Color.php b/src/Colors/Hsl/Color.php index 3cc1d433..e682f9a2 100644 --- a/src/Colors/Hsl/Color.php +++ b/src/Colors/Hsl/Color.php @@ -9,7 +9,7 @@ use Intervention\Image\Colors\Hsl\Channels\Hue; use Intervention\Image\Colors\Hsl\Channels\Luminance; use Intervention\Image\Colors\Hsl\Channels\Saturation; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; -use Intervention\Image\Drivers\AbstractInputHandler; +use Intervention\Image\InputHandler; use Intervention\Image\Interfaces\ColorChannelInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -43,11 +43,9 @@ class Color extends AbstractColor */ public static function create(mixed $input): ColorInterface { - return (new class ([ + return InputHandler::withDecoders([ Decoders\StringColorDecoder::class, - ]) extends AbstractInputHandler - { - })->handle($input); + ])->handle($input); } /** diff --git a/src/Colors/Hsv/Color.php b/src/Colors/Hsv/Color.php index e09a49b8..9c7f285c 100644 --- a/src/Colors/Hsv/Color.php +++ b/src/Colors/Hsv/Color.php @@ -9,7 +9,7 @@ use Intervention\Image\Colors\Hsv\Channels\Hue; use Intervention\Image\Colors\Hsv\Channels\Saturation; use Intervention\Image\Colors\Hsv\Channels\Value; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; -use Intervention\Image\Drivers\AbstractInputHandler; +use Intervention\Image\InputHandler; use Intervention\Image\Interfaces\ColorChannelInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -43,11 +43,9 @@ class Color extends AbstractColor */ public static function create(mixed $input): ColorInterface { - return (new class ([ + return InputHandler::withDecoders([ Decoders\StringColorDecoder::class, - ]) extends AbstractInputHandler - { - })->handle($input); + ])->handle($input); } /** diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index 4473987f..f97a9241 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -9,7 +9,7 @@ use Intervention\Image\Colors\Rgb\Channels\Blue; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Channels\Alpha; -use Intervention\Image\Drivers\AbstractInputHandler; +use Intervention\Image\InputHandler; use Intervention\Image\Interfaces\ColorChannelInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -53,14 +53,12 @@ class Color extends AbstractColor */ public static function create(mixed $input): ColorInterface { - return (new class ([ + return InputHandler::withDecoders([ Decoders\HexColorDecoder::class, Decoders\StringColorDecoder::class, Decoders\TransparentColorDecoder::class, Decoders\HtmlColornameDecoder::class, - ]) extends AbstractInputHandler - { - })->handle($input); + ])->handle($input); } /** diff --git a/src/Config.php b/src/Config.php new file mode 100644 index 00000000..4b69209e --- /dev/null +++ b/src/Config.php @@ -0,0 +1,73 @@ +prepareOptions($options) as $name => $value) { + if (!property_exists($this, $name)) { + throw new InputException('Property ' . $name . ' does not exists for ' . $this::class . '.'); + } + + $this->{$name} = $value; + } + + return $this; + } + + /** + * This method makes it possible to call self::setOptions() with a single + * array instead of named parameters + * + * @param array $options + * @return array + */ + private function prepareOptions(array $options): array + { + if (count($options) === 0) { + return $options; + } + + if (count($options) > 1) { + return $options; + } + + if (!array_key_exists(0, $options)) { + return $options; + } + + if (!is_array($options[0])) { + return $options; + } + + return $options[0]; + } +} diff --git a/src/Drivers/Imagick/Decoders/ColorObjectDecoder.php b/src/Decoders/ColorObjectDecoder.php similarity index 81% rename from src/Drivers/Imagick/Decoders/ColorObjectDecoder.php rename to src/Decoders/ColorObjectDecoder.php index 138046b5..60480361 100644 --- a/src/Drivers/Imagick/Decoders/ColorObjectDecoder.php +++ b/src/Decoders/ColorObjectDecoder.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Intervention\Image\Drivers\Imagick\Decoders; +namespace Intervention\Image\Decoders; use Intervention\Image\Drivers\AbstractDecoder; use Intervention\Image\Exceptions\DecoderException; @@ -11,6 +11,11 @@ use Intervention\Image\Interfaces\ImageInterface; class ColorObjectDecoder extends AbstractDecoder { + /** + * {@inheritdoc} + * + * @see DecoderInterface::decode() + */ public function decode(mixed $input): ImageInterface|ColorInterface { if (!is_a($input, ColorInterface::class)) { diff --git a/src/Decoders/ImageObjectDecoder.php b/src/Decoders/ImageObjectDecoder.php index ea01091c..2a98c2e8 100644 --- a/src/Decoders/ImageObjectDecoder.php +++ b/src/Decoders/ImageObjectDecoder.php @@ -4,8 +4,24 @@ declare(strict_types=1); namespace Intervention\Image\Decoders; -use Intervention\Image\Drivers\SpecializableDecoder; +use Intervention\Image\Drivers\AbstractDecoder; +use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\ColorInterface; -class ImageObjectDecoder extends SpecializableDecoder +class ImageObjectDecoder extends AbstractDecoder { + /** + * {@inheritdoc} + * + * @see DecoderInterface::decode() + */ + public function decode(mixed $input): ImageInterface|ColorInterface + { + if (!is_a($input, ImageInterface::class)) { + throw new DecoderException('Unable to decode input'); + } + + return $input; + } } diff --git a/src/Drivers/AbstractDecoder.php b/src/Drivers/AbstractDecoder.php index 4d736663..27fce6e3 100644 --- a/src/Drivers/AbstractDecoder.php +++ b/src/Drivers/AbstractDecoder.php @@ -6,54 +6,14 @@ namespace Intervention\Image\Drivers; use Exception; use Intervention\Image\Collection; -use Intervention\Image\Exceptions\DecoderException; -use Intervention\Image\Exceptions\RuntimeException; use Intervention\Image\Interfaces\CollectionInterface; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; -use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Traits\CanBuildFilePointer; abstract class AbstractDecoder implements DecoderInterface { use CanBuildFilePointer; - public function __construct(protected ?self $successor = null) - { - } - - /** - * Try to decode given input to image or color object - * - * @param mixed $input - * @throws RuntimeException - * @return ImageInterface|ColorInterface - */ - final public function handle(mixed $input): ImageInterface|ColorInterface - { - try { - $decoded = $this->decode($input); - } catch (DecoderException $e) { - if (!$this->hasSuccessor()) { - throw new DecoderException($e->getMessage()); - } - - return $this->successor->handle($input); - } - - return $decoded; - } - - /** - * Determine if current decoder has a successor - * - * @return bool - */ - protected function hasSuccessor(): bool - { - return $this->successor !== null; - } - /** * Determine if the given input is GIF data format * diff --git a/src/Drivers/AbstractDriver.php b/src/Drivers/AbstractDriver.php index bbef4608..8bf8cf40 100644 --- a/src/Drivers/AbstractDriver.php +++ b/src/Drivers/AbstractDriver.php @@ -4,12 +4,16 @@ declare(strict_types=1); namespace Intervention\Image\Drivers; +use Intervention\Image\Config; use Intervention\Image\Exceptions\DriverException; use Intervention\Image\Exceptions\NotSupportedException; +use Intervention\Image\InputHandler; use Intervention\Image\Interfaces\AnalyzerInterface; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\DriverInterface; use Intervention\Image\Interfaces\EncoderInterface; +use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SpecializableInterface; use Intervention\Image\Interfaces\SpecializedInterface; @@ -17,14 +21,41 @@ use ReflectionClass; abstract class AbstractDriver implements DriverInterface { + /** + * Driver options + */ + protected Config $config; + /** * @throws DriverException + * @return void */ public function __construct() { + $this->config = new Config(); $this->checkHealth(); } + /** + * {@inheritdoc} + * + * @see DriverInterface::config() + */ + public function config(): Config + { + return $this->config; + } + + /** + * {@inheritdoc} + * + * @see DriverInterface::handleInput() + */ + public function handleInput(mixed $input, array $decoders = []): ImageInterface|ColorInterface + { + return InputHandler::withDecoders($decoders, $this)->handle($input); + } + /** * {@inheritdoc} * @@ -38,8 +69,10 @@ abstract class AbstractDriver implements DriverInterface return $object; } - // return directly if object is already specialized - if ($object instanceof SpecializedInterface) { + // return directly and only attach driver if object is already specialized + if (($object instanceof SpecializedInterface)) { + $object->setDriver($this); + return $object; } @@ -54,7 +87,7 @@ abstract class AbstractDriver implements DriverInterface ); } - // create driver specialized object with specializable properties of generic object + // create a driver specialized object with the specializable properties of generic object $specialized = (new $specialized_classname(...$object->specializable())); // attach driver diff --git a/src/Drivers/AbstractInputHandler.php b/src/Drivers/AbstractInputHandler.php deleted file mode 100644 index bb079546..00000000 --- a/src/Drivers/AbstractInputHandler.php +++ /dev/null @@ -1,71 +0,0 @@ - - */ - protected array $decoders = []; - - /** - * Create new input handler instance with given decoder classnames - * - * @param array $decoders - * @return void - */ - public function __construct(array $decoders = []) - { - $this->decoders = count($decoders) ? $decoders : $this->decoders; - } - - /** - * {@inheritdoc} - * - * @see InputHandlerInterface::handle() - */ - public function handle($input): ImageInterface|ColorInterface - { - return $this->chain()->handle($input); - } - - /** - * Stack the decoder array into a nested decoder object - * - * @throws DecoderException - * @return AbstractDecoder - */ - protected function chain(): AbstractDecoder - { - if (count($this->decoders) == 0) { - throw new DecoderException('No decoders found in ' . $this::class); - } - - // get last decoder in stack - list($decoder) = array_slice(array_reverse($this->decoders), 0, 1); - $chain = ($decoder instanceof DecoderInterface) ? $decoder : new $decoder(); - - // only accept DecoderInterface - if (!($chain instanceof DecoderInterface)) { - throw new DecoderException('Decoder must implement in ' . DecoderInterface::class); - } - - // build decoder chain - foreach (array_slice(array_reverse($this->decoders), 1) as $decoder) { - $chain = ($decoder instanceof DecoderInterface) ? new ($decoder::class)($chain) : new $decoder($chain); - } - - return $chain; - } -} diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index c71b3232..5eddf07f 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -8,14 +8,11 @@ use Intervention\Image\Exceptions\RuntimeException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; -use Intervention\Image\Drivers\Gd\Decoders\Traits\CanDecodeGif; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Modifiers\AlignRotationModifier; class BinaryImageDecoder extends NativeObjectDecoder implements DecoderInterface { - use CanDecodeGif; - /** * {@inheritdoc} * @@ -63,7 +60,9 @@ class BinaryImageDecoder extends NativeObjectDecoder implements DecoderInterface } // adjust image orientation - $image->modify(new AlignRotationModifier()); + if ($this->driver()->config()->autoOrientation) { + $image->modify(new AlignRotationModifier()); + } return $image; } diff --git a/src/Drivers/Gd/Decoders/ColorObjectDecoder.php b/src/Drivers/Gd/Decoders/ColorObjectDecoder.php deleted file mode 100644 index b1096aa6..00000000 --- a/src/Drivers/Gd/Decoders/ColorObjectDecoder.php +++ /dev/null @@ -1,23 +0,0 @@ -isFile($input)) { @@ -53,7 +50,9 @@ class FilePathImageDecoder extends NativeObjectDecoder implements DecoderInterfa $image->setExif($this->extractExifData($input)); // adjust image orientation - $image->modify(new AlignRotationModifier()); + if ($this->driver()->config()->autoOrientation) { + $image->modify(new AlignRotationModifier()); + } return $image; } diff --git a/src/Drivers/Gd/Decoders/ImageObjectDecoder.php b/src/Drivers/Gd/Decoders/ImageObjectDecoder.php deleted file mode 100644 index aa5e9eb1..00000000 --- a/src/Drivers/Gd/Decoders/ImageObjectDecoder.php +++ /dev/null @@ -1,23 +0,0 @@ -driver(), new Core([ new Frame($input) ]) ); } + + /** + * Decode image from given GIF source which can be either a file path or binary data + * + * Depending on the configuration, this is taken over by the native GD function + * or, if animations are required, by our own extended decoder. + * + * @param mixed $input + * @throws RuntimeException + * @return ImageInterface + */ + protected function decodeGif(mixed $input): ImageInterface + { + // create non-animated image depending on config + if (!$this->driver()->config()->decodeAnimation) { + $native = match (true) { + $this->isGifFormat($input) => @imagecreatefromstring($input), + default => @imagecreatefromgif($input), + }; + + if ($native === false) { + throw new DecoderException('Unable to decode input.'); + } + + $image = self::decode($native); + $image->origin()->setMediaType('image/gif'); + + return $image; + } + + // create empty core + $core = new Core(); + + $gif = GifDecoder::decode($input); + $splitter = GifSplitter::create($gif)->split(); + $delays = $splitter->getDelays(); + + // set loops on core + if ($loops = $gif->getMainApplicationExtension()?->getLoops()) { + $core->setLoops($loops); + } + + // add GDImage instances to core + foreach ($splitter->coalesceToResources() as $key => $native) { + $core->push( + new Frame($native, $delays[$key] / 100) + ); + } + + // create (possibly) animated image + $image = new Image($this->driver(), $core); + + // set media type + $image->origin()->setMediaType('image/gif'); + + return $image; + } } diff --git a/src/Drivers/Gd/Decoders/Traits/CanDecodeGif.php b/src/Drivers/Gd/Decoders/Traits/CanDecodeGif.php deleted file mode 100644 index 9b1452b9..00000000 --- a/src/Drivers/Gd/Decoders/Traits/CanDecodeGif.php +++ /dev/null @@ -1,54 +0,0 @@ -split(); - $delays = $splitter->getDelays(); - - // build core - $core = new Core(); - - // set loops on core - if ($loops = $gif->getMainApplicationExtension()?->getLoops()) { - $core->setLoops($loops); - } - - // add GDImage instances to core - foreach ($splitter->coalesceToResources() as $key => $native) { - $core->push( - (new Frame($native))->setDelay($delays[$key] / 100) - ); - } - - // create image - $image = new Image(new Driver(), $core); - - // set media type - $image->origin()->setMediaType('image/gif'); - - return $image; - } -} diff --git a/src/Drivers/Gd/Driver.php b/src/Drivers/Gd/Driver.php index 090faebd..0542d220 100644 --- a/src/Drivers/Gd/Driver.php +++ b/src/Drivers/Gd/Driver.php @@ -11,7 +11,6 @@ use Intervention\Image\Exceptions\RuntimeException; use Intervention\Image\Format; use Intervention\Image\FileExtension; use Intervention\Image\Image; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorProcessorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\DriverInterface; @@ -113,16 +112,6 @@ class Driver extends AbstractDriver return call_user_func($animation); } - /** - * {@inheritdoc} - * - * @see DriverInterface::handleInput() - */ - public function handleInput(mixed $input, array $decoders = []): ImageInterface|ColorInterface - { - return (new InputHandler($this->specializeMultiple($decoders)))->handle($input); - } - /** * {@inheritdoc} * diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php index dd0a4e71..b408a197 100644 --- a/src/Drivers/Gd/Encoders/JpegEncoder.php +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -14,7 +14,14 @@ class JpegEncoder extends GenericJpegEncoder implements SpecializedInterface { public function encode(ImageInterface $image): EncodedImage { - $output = Cloner::cloneBlended($image->core()->native(), background: $image->blendingColor()); + $blendingColor = $this->driver()->handleInput( + $this->driver()->config()->blendingColor + ); + + $output = Cloner::cloneBlended( + $image->core()->native(), + background: $blendingColor + ); $data = $this->buffered(function () use ($output) { imageinterlace($output, $this->progressive); diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php deleted file mode 100644 index d1628935..00000000 --- a/src/Drivers/Gd/InputHandler.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ - protected array $decoders = [ - NativeObjectDecoder::class, - ImageObjectDecoder::class, - ColorObjectDecoder::class, - RgbHexColorDecoder::class, - RgbStringColorDecoder::class, - CmykStringColorDecoder::class, - HsvStringColorDecoder::class, - HslStringColorDecoder::class, - TransparentColorDecoder::class, - HtmlColornameDecoder::class, - FilePointerImageDecoder::class, - FilePathImageDecoder::class, - SplFileInfoImageDecoder::class, - BinaryImageDecoder::class, - DataUriImageDecoder::class, - Base64ImageDecoder::class, - ]; -} diff --git a/src/Drivers/Gd/Modifiers/AlignRotationModifier.php b/src/Drivers/Gd/Modifiers/AlignRotationModifier.php index 38383b97..daf152fb 100644 --- a/src/Drivers/Gd/Modifiers/AlignRotationModifier.php +++ b/src/Drivers/Gd/Modifiers/AlignRotationModifier.php @@ -12,7 +12,7 @@ class AlignRotationModifier extends GenericAlignRotationModifier implements Spec { public function apply(ImageInterface $image): ImageInterface { - return match ($image->exif('IFD0.Orientation')) { + $image = match ($image->exif('IFD0.Orientation')) { 2 => $image->flop(), 3 => $image->rotate(180), 4 => $image->rotate(180)->flop(), @@ -22,5 +22,29 @@ class AlignRotationModifier extends GenericAlignRotationModifier implements Spec 8 => $image->rotate(90), default => $image }; + + return $this->markAligned($image); + } + + /** + * Set exif data of image to top-left orientation, marking the image as + * aligned and making sure the rotation correction process is not + * performed again. + * + * @param ImageInterface $image + * @return ImageInterface + */ + private function markAligned(ImageInterface $image): ImageInterface + { + $exif = $image->exif()->map(function ($item) { + if (is_array($item) && array_key_exists('Orientation', $item)) { + $item['Orientation'] = 1; + return $item; + } + + return $item; + }); + + return $image->setExif($exif); } } diff --git a/src/Drivers/Gd/Modifiers/BlendTransparencyModifier.php b/src/Drivers/Gd/Modifiers/BlendTransparencyModifier.php index 33e3abac..c2cec921 100644 --- a/src/Drivers/Gd/Modifiers/BlendTransparencyModifier.php +++ b/src/Drivers/Gd/Modifiers/BlendTransparencyModifier.php @@ -15,7 +15,7 @@ class BlendTransparencyModifier extends GenericBlendTransparencyModifier impleme { // decode blending color $color = $this->driver()->handleInput( - $this->color ? $this->color : $image->blendingColor() + $this->color ? $this->color : $this->driver()->config()->blendingColor ); foreach ($image as $frame) { diff --git a/src/Drivers/Gd/Modifiers/ContainModifier.php b/src/Drivers/Gd/Modifiers/ContainModifier.php index 148d2c9e..8761111e 100644 --- a/src/Drivers/Gd/Modifiers/ContainModifier.php +++ b/src/Drivers/Gd/Modifiers/ContainModifier.php @@ -23,7 +23,9 @@ class ContainModifier extends GenericContainModifier implements SpecializedInter $crop = $this->getCropSize($image); $resize = $this->getResizeSize($image); $background = $this->driver()->handleInput($this->background); - $blendingColor = $image->blendingColor(); + $blendingColor = $this->driver()->handleInput( + $this->driver()->config()->blendingColor + ); foreach ($image as $frame) { $this->modify($frame, $crop, $resize, $background, $blendingColor); diff --git a/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php b/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php index 8d7aa948..36e25496 100644 --- a/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php +++ b/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php @@ -31,9 +31,13 @@ class QuantizeColorsModifier extends GenericQuantizeColorsModifier implements Sp $this->driver()->handleInput($this->background) ); + $blendingColor = $this->driver()->handleInput( + $this->driver()->config()->blendingColor + ); + foreach ($image as $frame) { // create new image for color quantization - $reduced = Cloner::cloneEmpty($frame->native(), background: $image->blendingColor()); + $reduced = Cloner::cloneEmpty($frame->native(), background: $blendingColor); // fill with background imagefill($reduced, 0, 0, $background); diff --git a/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php b/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php deleted file mode 100644 index f9487214..00000000 --- a/src/Drivers/Imagick/Decoders/ImageObjectDecoder.php +++ /dev/null @@ -1,22 +0,0 @@ -coalesceImages(); } + // create image object $image = new Image( - new Driver(), + $this->driver(), new Core($input) ); + // discard animation depending on config + if (!$this->driver()->config()->decodeAnimation) { + $image->modify(new RemoveAnimationModifier()); + } + // adjust image rotatation - $image->modify(new AlignRotationModifier()); + if ($this->driver()->config()->autoOrientation) { + $image->modify(new AlignRotationModifier()); + } // set media type on origin $image->origin()->setMediaType($input->getImageMimeType()); diff --git a/src/Drivers/Imagick/Driver.php b/src/Drivers/Imagick/Driver.php index 2c01f83b..fbd2f2d6 100644 --- a/src/Drivers/Imagick/Driver.php +++ b/src/Drivers/Imagick/Driver.php @@ -13,7 +13,6 @@ use Intervention\Image\Exceptions\RuntimeException; use Intervention\Image\Format; use Intervention\Image\FileExtension; use Intervention\Image\Image; -use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorProcessorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\DriverInterface; @@ -116,16 +115,6 @@ class Driver extends AbstractDriver return call_user_func($animation); } - /** - * {@inheritdoc} - * - * @see DriverInterface::handleInput() - */ - public function handleInput(mixed $input, array $decoders = []): ImageInterface|ColorInterface - { - return (new InputHandler($this->specializeMultiple($decoders)))->handle($input); - } - /** * {@inheritdoc} * diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index b70f0e36..9484f3ca 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -16,11 +16,14 @@ class JpegEncoder extends GenericJpegEncoder implements SpecializedInterface { $format = 'jpeg'; $compression = Imagick::COMPRESSION_JPEG; + $blendingColor = $this->driver()->handleInput( + $this->driver()->config()->blendingColor + ); // resolve blending color because jpeg has no transparency $background = $this->driver() ->colorProcessor($image->colorspace()) - ->colorToNative($image->blendingColor()); + ->colorToNative($blendingColor); // set alpha value to 1 because Imagick renders // possible full transparent colors as black diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php deleted file mode 100644 index 5c7b748b..00000000 --- a/src/Drivers/Imagick/InputHandler.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ - protected array $decoders = [ - NativeObjectDecoder::class, - ImageObjectDecoder::class, - ColorObjectDecoder::class, - RgbHexColorDecoder::class, - RgbStringColorDecoder::class, - CmykStringColorDecoder::class, - HsvStringColorDecoder::class, - HslStringColorDecoder::class, - TransparentColorDecoder::class, - HtmlColornameDecoder::class, - FilePointerImageDecoder::class, - FilePathImageDecoder::class, - SplFileInfoImageDecoder::class, - BinaryImageDecoder::class, - DataUriImageDecoder::class, - Base64ImageDecoder::class, - ]; -} diff --git a/src/Drivers/Imagick/Modifiers/AlignRotationModifier.php b/src/Drivers/Imagick/Modifiers/AlignRotationModifier.php index 3427d4ad..6b84b304 100644 --- a/src/Drivers/Imagick/Modifiers/AlignRotationModifier.php +++ b/src/Drivers/Imagick/Modifiers/AlignRotationModifier.php @@ -19,30 +19,30 @@ class AlignRotationModifier extends GenericAlignRotationModifier implements Spec break; case Imagick::ORIENTATION_BOTTOMRIGHT: // 3 - $image->core()->native()->rotateimage("#000", 180); + $image->core()->native()->rotateImage("#000", 180); break; case Imagick::ORIENTATION_BOTTOMLEFT: // 4 - $image->core()->native()->rotateimage("#000", 180); + $image->core()->native()->rotateImage("#000", 180); $image->core()->native()->flopImage(); break; case Imagick::ORIENTATION_LEFTTOP: // 5 - $image->core()->native()->rotateimage("#000", -270); + $image->core()->native()->rotateImage("#000", -270); $image->core()->native()->flopImage(); break; case Imagick::ORIENTATION_RIGHTTOP: // 6 - $image->core()->native()->rotateimage("#000", -270); + $image->core()->native()->rotateImage("#000", -270); break; case Imagick::ORIENTATION_RIGHTBOTTOM: // 7 - $image->core()->native()->rotateimage("#000", -90); + $image->core()->native()->rotateImage("#000", -90); $image->core()->native()->flopImage(); break; case Imagick::ORIENTATION_LEFTBOTTOM: // 8 - $image->core()->native()->rotateimage("#000", -90); + $image->core()->native()->rotateImage("#000", -90); break; } diff --git a/src/Drivers/Imagick/Modifiers/BlendTransparencyModifier.php b/src/Drivers/Imagick/Modifiers/BlendTransparencyModifier.php index f6586793..72c9e653 100644 --- a/src/Drivers/Imagick/Modifiers/BlendTransparencyModifier.php +++ b/src/Drivers/Imagick/Modifiers/BlendTransparencyModifier.php @@ -15,7 +15,7 @@ class BlendTransparencyModifier extends GenericBlendTransparencyModifier impleme { // decode blending color $color = $this->driver()->handleInput( - $this->color ? $this->color : $image->blendingColor() + $this->color ? $this->color : $this->driver()->config()->blendingColor ); // get imagickpixel from color diff --git a/src/Image.php b/src/Image.php index 0c1731c7..6e7eb7d6 100644 --- a/src/Image.php +++ b/src/Image.php @@ -13,7 +13,6 @@ use Intervention\Image\Analyzers\PixelColorsAnalyzer; use Intervention\Image\Analyzers\ProfileAnalyzer; use Intervention\Image\Analyzers\ResolutionAnalyzer; use Intervention\Image\Analyzers\WidthAnalyzer; -use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Encoders\AutoEncoder; use Intervention\Image\Encoders\AvifEncoder; use Intervention\Image\Encoders\BmpEncoder; @@ -50,6 +49,7 @@ use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\ProfileInterface; use Intervention\Image\Interfaces\ResolutionInterface; use Intervention\Image\Interfaces\SizeInterface; +use Intervention\Image\Modifiers\AlignRotationModifier; use Intervention\Image\Modifiers\BlendTransparencyModifier; use Intervention\Image\Modifiers\BlurModifier; use Intervention\Image\Modifiers\BrightnessModifier; @@ -101,14 +101,6 @@ final class Image implements ImageInterface */ protected Origin $origin; - /** - * Color is mixed with transparent areas when converting to a format which - * does not support transparency. - * - * @var ColorInterface - */ - protected ColorInterface $blendingColor; - /** * Create new instance * @@ -124,9 +116,6 @@ final class Image implements ImageInterface protected CollectionInterface $exif = new Collection() ) { $this->origin = new Origin(); - $this->blendingColor = $this->colorspace()->importColor( - new Color(255, 255, 255, 0) - ); } /** @@ -418,7 +407,9 @@ final class Image implements ImageInterface */ public function blendingColor(): ColorInterface { - return $this->blendingColor; + return $this->driver()->handleInput( + $this->driver()->config()->blendingColor + ); } /** @@ -428,7 +419,9 @@ final class Image implements ImageInterface */ public function setBlendingColor(mixed $color): ImageInterface { - $this->blendingColor = $this->driver()->handleInput($color); + $this->driver()->config()->setOptions( + blendingColor: $this->driver()->handleInput($color) + ); return $this; } @@ -603,6 +596,16 @@ final class Image implements ImageInterface return $this->modify(new RotateModifier($angle, $background)); } + /** + * {@inheritdoc} + * + * @see ImageInterface::orient() + */ + public function orient(): ImageInterface + { + return $this->modify(new AlignRotationModifier()); + } + /** * {@inheritdoc} * diff --git a/src/ImageManager.php b/src/ImageManager.php index 383dea5f..64c919a7 100644 --- a/src/ImageManager.php +++ b/src/ImageManager.php @@ -8,6 +8,7 @@ use Intervention\Image\Interfaces\DriverInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Drivers\Gd\Driver as GdDriver; use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver; +use Intervention\Image\Exceptions\DriverException; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageManagerInterface; @@ -18,10 +19,11 @@ final class ImageManager implements ImageManagerInterface /** * @link https://image.intervention.io/v3/basics/image-manager#create-a-new-image-manager-instance * @param string|DriverInterface $driver + * @param mixed $options */ - public function __construct(string|DriverInterface $driver) + public function __construct(string|DriverInterface $driver, mixed ...$options) { - $this->driver = $this->resolveDriver($driver); + $this->driver = $this->resolveDriver($driver, ...$options); } /** @@ -29,33 +31,38 @@ final class ImageManager implements ImageManagerInterface * * @link https://image.intervention.io/v3/basics/image-manager * @param string|DriverInterface $driver + * @param mixed $options * @return ImageManager */ - public static function withDriver(string|DriverInterface $driver): self + public static function withDriver(string|DriverInterface $driver, mixed ...$options): self { - return new self(self::resolveDriver($driver)); + return new self(self::resolveDriver($driver, ...$options)); } /** * Create image manager with GD driver * * @link https://image.intervention.io/v3/basics/image-manager#static-gd-driver-constructor + * @param mixed $options + * @throws DriverException * @return ImageManager */ - public static function gd(): self + public static function gd(mixed ...$options): self { - return self::withDriver(GdDriver::class); + return self::withDriver(new GdDriver(), ...$options); } /** * Create image manager with Imagick driver * * @link https://image.intervention.io/v3/basics/image-manager#static-imagick-driver-constructor + * @param mixed $options + * @throws DriverException * @return ImageManager */ - public static function imagick(): self + public static function imagick(mixed ...$options): self { - return self::withDriver(ImagickDriver::class); + return self::withDriver(new ImagickDriver(), ...$options); } /** @@ -108,14 +115,14 @@ final class ImageManager implements ImageManagerInterface * Return driver object * * @param string|DriverInterface $driver + * @param mixed $options * @return DriverInterface */ - private static function resolveDriver(string|DriverInterface $driver): DriverInterface + private static function resolveDriver(string|DriverInterface $driver, mixed ...$options): DriverInterface { - if (is_object($driver)) { - return $driver; - } + $driver = is_string($driver) ? new $driver() : $driver; + $driver->config()->setOptions(...$options); - return new $driver(); + return $driver; } } diff --git a/src/InputHandler.php b/src/InputHandler.php new file mode 100644 index 00000000..4c9eeed7 --- /dev/null +++ b/src/InputHandler.php @@ -0,0 +1,135 @@ + + */ + protected array $decoders = [ + NativeObjectDecoder::class, + ImageObjectDecoder::class, + ColorObjectDecoder::class, + RgbHexColorDecoder::class, + RgbStringColorDecoder::class, + CmykStringColorDecoder::class, + HsvStringColorDecoder::class, + HslStringColorDecoder::class, + TransparentColorDecoder::class, + HtmlColornameDecoder::class, + FilePointerImageDecoder::class, + FilePathImageDecoder::class, + SplFileInfoImageDecoder::class, + BinaryImageDecoder::class, + DataUriImageDecoder::class, + Base64ImageDecoder::class, + ]; + + /** + * Driver with which the decoder classes are specialized + * + * @var null|DriverInterface + */ + protected ?DriverInterface $driver = null; + + /** + * Create new input handler instance with given decoder classnames + * + * @param array $decoders + * @param DriverInterface $driver + * @return void + */ + public function __construct(array $decoders = [], ?DriverInterface $driver = null) + { + $this->decoders = count($decoders) ? $decoders : $this->decoders; + $this->driver = $driver; + } + + /** + * Static factory method + * + * @param array $decoders + * @param null|DriverInterface $driver + * @return InputHandler + */ + public static function withDecoders(array $decoders, ?DriverInterface $driver = null): self + { + return new self($decoders, $driver); + } + + /** + * {@inheritdoc} + * + * @see InputHandlerInterface::handle() + */ + public function handle($input): ImageInterface|ColorInterface + { + foreach ($this->decoders as $decoder) { + // resolve river specialized decoder + $decoder = $this->resolve($decoder); + + try { + return $decoder->decode($input); + } catch (DecoderException $e) { + // let next decoder try + } + } + + throw new DecoderException(isset($e) ? $e->getMessage() : ''); + } + + /** + * Resolve the given classname to an decoder object + * + * @param string|DecoderInterface $decoder + * @throws DriverException + * @throws NotSupportedException + * @return DecoderInterface + */ + private function resolve(string|DecoderInterface $decoder): DecoderInterface + { + if (($decoder instanceof DecoderInterface) && empty($this->driver)) { + return $decoder; + } + + if (($decoder instanceof DecoderInterface) && !empty($this->driver)) { + return $this->driver->specialize($decoder); + } + + if (empty($this->driver)) { + return new $decoder(); + } + + return $this->driver->specialize(new $decoder()); + } +} diff --git a/src/Interfaces/DriverInterface.php b/src/Interfaces/DriverInterface.php index 466f4b22..95503160 100644 --- a/src/Interfaces/DriverInterface.php +++ b/src/Interfaces/DriverInterface.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Intervention\Image\Interfaces; +use Intervention\Image\Config; use Intervention\Image\Exceptions\DriverException; use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\Exceptions\RuntimeException; @@ -20,11 +21,19 @@ interface DriverInterface */ public function id(): string; + /** + * Get driver configuration + * + * @return Config + */ + public function config(): Config; + /** * Resolve given object into a specialized version for the current driver * * @param ModifierInterface|AnalyzerInterface|EncoderInterface|DecoderInterface $object * @throws NotSupportedException + * @throws DriverException * @return ModifierInterface|AnalyzerInterface|EncoderInterface|DecoderInterface */ public function specialize( @@ -36,6 +45,7 @@ interface DriverInterface * * @param array $objects * @throws NotSupportedException + * @throws DriverException * @return array */ public function specializeMultiple(array $objects): array; diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 4315ac8d..b70980ed 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -247,7 +247,8 @@ interface ImageInterface extends IteratorAggregate, Countable * Return color that is mixed with transparent areas when converting to a format which * does not support transparency. * - * @link https://image.intervention.io/v3/basics/colors#transparency + * @deprecated Use configuration options of image manager instead + * @throws RuntimeException * @return ColorInterface */ public function blendingColor(): ColorInterface; @@ -256,7 +257,7 @@ interface ImageInterface extends IteratorAggregate, Countable * Set blending color will have no effect unless image is converted into a format * which does not support transparency. * - * @link https://image.intervention.io/v3/basics/colors#transparency + * @deprecated Use configuration options of image manager instead * @param mixed $color * @throws RuntimeException * @return ImageInterface @@ -430,10 +431,19 @@ interface ImageInterface extends IteratorAggregate, Countable */ public function rotate(float $angle, mixed $background = 'ffffff'): self; + /** + * Rotate the image to be upright according to exif information + * + * @link https://image.intervention.io/v3/modifying/effects#image-orientation-according-to-exif-data + * @throws RuntimeException + * @return ImageInterface + */ + public function orient(): self; + /** * Draw text on image * - * @ink https://image.intervention.io/v3/modifying/text-fonts + * @link https://image.intervention.io/v3/modifying/text-fonts * @param string $text * @param int $x * @param int $y diff --git a/src/Interfaces/SpecializableInterface.php b/src/Interfaces/SpecializableInterface.php index e04ee6dc..5c188d01 100644 --- a/src/Interfaces/SpecializableInterface.php +++ b/src/Interfaces/SpecializableInterface.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace Intervention\Image\Interfaces; +use Intervention\Image\Exceptions\DriverException; + interface SpecializableInterface { /** @@ -18,6 +20,7 @@ interface SpecializableInterface * Set the driver for which the object is specialized * * @param DriverInterface $driver + * @throws DriverException * @return SpecializableInterface */ public function setDriver(DriverInterface $driver): self; diff --git a/src/Traits/CanBeDriverSpecialized.php b/src/Traits/CanBeDriverSpecialized.php index 463050f4..eb597be3 100644 --- a/src/Traits/CanBeDriverSpecialized.php +++ b/src/Traits/CanBeDriverSpecialized.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Intervention\Image\Traits; +use Intervention\Image\Exceptions\DriverException; use Intervention\Image\Interfaces\DriverInterface; use Intervention\Image\Interfaces\SpecializableInterface; use ReflectionClass; @@ -53,8 +54,30 @@ trait CanBeDriverSpecialized */ public function setDriver(DriverInterface $driver): SpecializableInterface { + if (!$this->belongsToDriver($driver)) { + throw new DriverException( + "Class '" . $this::class . "' can not be used with " . $driver->id() . " driver." + ); + } + $this->driver = $driver; return $this; } + + /** + * Determine if the given object belongs to the driver's namespace + * + * @param object $object + * @return bool + */ + protected function belongsToDriver(object $object): bool + { + $driverId = function (object $object): string|bool { + $id = substr($object::class, 27); + return strstr($id, "\\", true); + }; + + return $driverId($this) === $driverId($object); + } } diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php index 7416909f..41caa2aa 100644 --- a/tests/BaseTestCase.php +++ b/tests/BaseTestCase.php @@ -15,20 +15,20 @@ use PHPUnit\Framework\ExpectationFailedException; abstract class BaseTestCase extends MockeryTestCase { - public function getTestResourcePath($filename = 'test.jpg'): string + public static function getTestResourcePath($filename = 'test.jpg'): string { return sprintf('%s/resources/%s', __DIR__, $filename); } - public function getTestResourceData($filename = 'test.jpg'): string + public static function getTestResourceData($filename = 'test.jpg'): string { - return file_get_contents($this->getTestResourcePath($filename)); + return file_get_contents(self::getTestResourcePath($filename)); } public function getTestResourcePointer($filename = 'test.jpg') { $pointer = fopen('php://temp', 'rw'); - fputs($pointer, $this->getTestResourceData($filename)); + fputs($pointer, self::getTestResourceData($filename)); rewind($pointer); return $pointer; diff --git a/tests/GdTestCase.php b/tests/GdTestCase.php index b4ea3f49..2cfa9527 100644 --- a/tests/GdTestCase.php +++ b/tests/GdTestCase.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace Intervention\Image\Tests; +use Intervention\Image\Decoders\FilePathImageDecoder; use Intervention\Image\Drivers\Gd\Core; -use Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder; use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Image; @@ -14,7 +14,7 @@ abstract class GdTestCase extends BaseTestCase { public function readTestImage($filename = 'test.jpg'): Image { - return (new FilePathImageDecoder())->handle( + return (new Driver())->specialize(new FilePathImageDecoder())->decode( $this->getTestResourcePath($filename) ); } diff --git a/tests/ImagickTestCase.php b/tests/ImagickTestCase.php index c312b5a4..cbd24dcc 100644 --- a/tests/ImagickTestCase.php +++ b/tests/ImagickTestCase.php @@ -6,8 +6,8 @@ namespace Intervention\Image\Tests; use Imagick; use ImagickPixel; +use Intervention\Image\Decoders\FilePathImageDecoder; use Intervention\Image\Drivers\Imagick\Core; -use Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder; use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Image; @@ -15,7 +15,7 @@ abstract class ImagickTestCase extends BaseTestCase { public function readTestImage($filename = 'test.jpg'): Image { - return (new FilePathImageDecoder())->handle( + return (new Driver())->specialize(new FilePathImageDecoder())->decode( $this->getTestResourcePath($filename) ); } diff --git a/tests/Unit/ConfigTest.php b/tests/Unit/ConfigTest.php new file mode 100644 index 00000000..6a1683bd --- /dev/null +++ b/tests/Unit/ConfigTest.php @@ -0,0 +1,83 @@ +assertInstanceOf(Config::class, $config); + + $this->assertTrue($config->autoOrientation); + $this->assertTrue($config->decodeAnimation); + $this->assertEquals('ffffff', $config->blendingColor); + + $config = new Config( + autoOrientation: false, + decodeAnimation: false, + blendingColor: 'f00', + ); + $this->assertInstanceOf(Config::class, $config); + + $this->assertFalse($config->autoOrientation); + $this->assertFalse($config->decodeAnimation); + $this->assertEquals('f00', $config->blendingColor); + } + + public function testGetSetOptions(): void + { + $config = new Config(); + $this->assertTrue($config->autoOrientation); + $this->assertTrue($config->decodeAnimation); + $this->assertEquals('ffffff', $config->blendingColor); + + $result = $config->setOptions( + autoOrientation: false, + decodeAnimation: false, + blendingColor: 'f00', + ); + + $this->assertFalse($config->autoOrientation); + $this->assertFalse($config->decodeAnimation); + $this->assertEquals('f00', $config->blendingColor); + + $this->assertFalse($result->autoOrientation); + $this->assertFalse($result->decodeAnimation); + $this->assertEquals('f00', $result->blendingColor); + + $result = $config->setOptions(blendingColor: '000'); + + $this->assertFalse($config->autoOrientation); + $this->assertFalse($config->decodeAnimation); + $this->assertEquals('000', $config->blendingColor); + + $this->assertFalse($result->autoOrientation); + $this->assertFalse($result->decodeAnimation); + $this->assertEquals('000', $result->blendingColor); + } + + public function testSetOptionsWithArray(): void + { + $config = new Config(); + $result = $config->setOptions([ + 'autoOrientation' => false, + 'decodeAnimation' => false, + 'blendingColor' => 'f00', + ]); + + $this->assertFalse($config->autoOrientation); + $this->assertFalse($config->decodeAnimation); + $this->assertEquals('f00', $config->blendingColor); + $this->assertFalse($result->autoOrientation); + $this->assertFalse($result->decodeAnimation); + $this->assertEquals('f00', $result->blendingColor); + } +} diff --git a/tests/Unit/Drivers/AbstractDecoderTest.php b/tests/Unit/Drivers/AbstractDecoderTest.php index e156db8f..2a9ff77f 100644 --- a/tests/Unit/Drivers/AbstractDecoderTest.php +++ b/tests/Unit/Drivers/AbstractDecoderTest.php @@ -7,7 +7,6 @@ namespace Intervention\Image\Tests\Unit\Drivers; use PHPUnit\Framework\Attributes\CoversClass; use Exception; use Intervention\Image\Drivers\AbstractDecoder; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\CollectionInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -18,35 +17,6 @@ use stdClass; #[CoversClass(\Intervention\Image\Drivers\AbstractDecoder::class)] final class AbstractDecoderTest extends BaseTestCase { - public function testHandle(): void - { - $result = Mockery::mock(ColorInterface::class); - $decoder = Mockery::mock(AbstractDecoder::class); - $decoder->shouldReceive('decode')->with('test input')->andReturn($result); - $decoder->handle('test input'); - } - - public function testHandleFail(): void - { - $decoder = Mockery::mock(AbstractDecoder::class, []); - $decoder->shouldReceive('decode')->with('test input')->andThrow(DecoderException::class); - $this->expectException(DecoderException::class); - $decoder->handle('test input'); - } - - public function testHandleFailWithSuccessor(): void - { - $result = Mockery::mock(ColorInterface::class); - $successor = Mockery::mock(AbstractDecoder::class); - $successor->shouldReceive('decode')->with('test input')->andReturn($result); - $decoder = Mockery::mock( - AbstractDecoder::class, - [$successor] - ); - $decoder->shouldReceive('decode')->with('test input')->andThrow(DecoderException::class); - $decoder->handle('test input'); - } - public function testIsGifFormat(): void { $decoder = Mockery::mock(AbstractDecoder::class); diff --git a/tests/Unit/Drivers/AbstractInputHandlerTest.php b/tests/Unit/Drivers/AbstractInputHandlerTest.php deleted file mode 100644 index 0e0e1929..00000000 --- a/tests/Unit/Drivers/AbstractInputHandlerTest.php +++ /dev/null @@ -1,55 +0,0 @@ -shouldReceive('handle')->with('test image')->andReturn($image); - $chain->shouldReceive('decode')->with('test image')->andReturn(Mockery::mock(ImageInterface::class)); - - $modifier = $this->getModifier($chain); - $modifier->handle('test image'); - } - - public function testChainNoItems(): void - { - $handler = new class () extends AbstractInputHandler - { - }; - - $this->expectException(DecoderException::class); - $handler->handle('test'); - } - - private function getModifier(AbstractDecoder $chain): AbstractInputHandler - { - return new class ([$chain]) extends AbstractInputHandler - { - public function __construct(protected array $decoders = []) - { - // - } - - protected function chain(): AbstractDecoder - { - return $this->decoders[0]; - } - }; - } -} diff --git a/tests/Unit/Drivers/Gd/Decoders/Base64ImageDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/Base64ImageDecoderTest.php index 51853e44..ff1bcc81 100644 --- a/tests/Unit/Drivers/Gd/Decoders/Base64ImageDecoderTest.php +++ b/tests/Unit/Drivers/Gd/Decoders/Base64ImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Gd\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Gd\Decoders\Base64ImageDecoder; +use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; @@ -20,6 +21,7 @@ final class Base64ImageDecoderTest extends BaseTestCase protected function setUp(): void { $this->decoder = new Base64ImageDecoder(); + $this->decoder->setDriver(new Driver()); } public function testDecode(): void diff --git a/tests/Unit/Drivers/Gd/Decoders/BinaryImageDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/BinaryImageDecoderTest.php index d09690a3..f7309f40 100644 --- a/tests/Unit/Drivers/Gd/Decoders/BinaryImageDecoderTest.php +++ b/tests/Unit/Drivers/Gd/Decoders/BinaryImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Gd\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder; +use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; @@ -14,10 +15,17 @@ use Intervention\Image\Tests\BaseTestCase; #[CoversClass(\Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder::class)] final class BinaryImageDecoderTest extends BaseTestCase { + protected BinaryImageDecoder $decoder; + + protected function setUp(): void + { + $this->decoder = new BinaryImageDecoder(); + $this->decoder->setDriver(new Driver()); + } + public function testDecodePng(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('tile.png'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('tile.png'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->width()); $this->assertEquals(16, $image->height()); @@ -26,8 +34,7 @@ final class BinaryImageDecoderTest extends BaseTestCase public function testDecodeGif(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('red.gif'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('red.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->width()); $this->assertEquals(16, $image->height()); @@ -36,8 +43,7 @@ final class BinaryImageDecoderTest extends BaseTestCase public function testDecodeAnimatedGif(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('cats.gif'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('cats.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(75, $image->width()); $this->assertEquals(50, $image->height()); @@ -46,8 +52,7 @@ final class BinaryImageDecoderTest extends BaseTestCase public function testDecodeJpegWithExif(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('exif.jpg'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('exif.jpg'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->width()); $this->assertEquals(16, $image->height()); diff --git a/tests/Unit/Drivers/Gd/Decoders/DataUriImageDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/DataUriImageDecoderTest.php index 100fd982..8c2f604e 100644 --- a/tests/Unit/Drivers/Gd/Decoders/DataUriImageDecoderTest.php +++ b/tests/Unit/Drivers/Gd/Decoders/DataUriImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Gd\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Gd\Decoders\DataUriImageDecoder; +use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; @@ -21,6 +22,7 @@ final class DataUriImageDecoderTest extends BaseTestCase protected function setUp(): void { $this->decoder = new DataUriImageDecoder(); + $this->decoder->setDriver(new Driver()); } public function testDecode(): void diff --git a/tests/Unit/Drivers/Gd/Decoders/FilePathImageDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/FilePathImageDecoderTest.php index 0a1a3d95..9a76bd2f 100644 --- a/tests/Unit/Drivers/Gd/Decoders/FilePathImageDecoderTest.php +++ b/tests/Unit/Drivers/Gd/Decoders/FilePathImageDecoderTest.php @@ -7,10 +7,11 @@ namespace Intervention\Image\Tests\Unit\Drivers\Gd\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder; +use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; -use stdClass; +use PHPUnit\Framework\Attributes\DataProvider; #[RequiresPhpExtension('gd')] #[CoversClass(\Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder::class)] @@ -21,32 +22,35 @@ final class FilePathImageDecoderTest extends BaseTestCase protected function setUp(): void { $this->decoder = new FilePathImageDecoder(); + $this->decoder->setDriver(new Driver()); } - public function testDecode(): void + #[DataProvider('validFormatPathsProvider')] + public function testDecode(string $path, bool $result): void { - $result = $this->decoder->decode( - $this->getTestResourcePath() - ); + if ($result === false) { + $this->expectException(DecoderException::class); + } - $this->assertInstanceOf(Image::class, $result); + $result = $this->decoder->decode($path); + + if ($result === true) { + $this->assertInstanceOf(Image::class, $result); + } } - public function testDecoderNonString(): void + public static function validFormatPathsProvider(): array { - $this->expectException(DecoderException::class); - $this->decoder->decode(new stdClass()); - } - - public function testDecoderNoPath(): void - { - $this->expectException(DecoderException::class); - $this->decoder->decode('no-path'); - } - - public function testDecoderTooLongPath(): void - { - $this->expectException(DecoderException::class); - $this->decoder->decode(str_repeat('x', PHP_MAXPATHLEN + 1)); + return [ + [self::getTestResourcePath('cats.gif'), true], + [self::getTestResourcePath('animation.gif'), true], + [self::getTestResourcePath('red.gif'), true], + [self::getTestResourcePath('green.gif'), true], + [self::getTestResourcePath('blue.gif'), true], + [self::getTestResourcePath('gradient.bmp'), true], + [self::getTestResourcePath('circle.png'), true], + ['no-path', false], + [str_repeat('x', PHP_MAXPATHLEN + 1), false], + ]; } } diff --git a/tests/Unit/Drivers/Gd/Decoders/FilePointerImageDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/FilePointerImageDecoderTest.php index bf2281bc..e40777ef 100644 --- a/tests/Unit/Drivers/Gd/Decoders/FilePointerImageDecoderTest.php +++ b/tests/Unit/Drivers/Gd/Decoders/FilePointerImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Gd\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Gd\Decoders\FilePointerImageDecoder; +use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Image; use Intervention\Image\Tests\GdTestCase; @@ -17,6 +18,8 @@ final class FilePointerImageDecoderTest extends GdTestCase public function testDecode(): void { $decoder = new FilePointerImageDecoder(); + $decoder->setDriver(new Driver()); + $fp = fopen($this->getTestResourcePath('test.jpg'), 'r'); $result = $decoder->decode($fp); $this->assertInstanceOf(Image::class, $result); diff --git a/tests/Unit/Drivers/Gd/Decoders/ImageObjectDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/ImageObjectDecoderTest.php index fb432118..2f8cbcd6 100644 --- a/tests/Unit/Drivers/Gd/Decoders/ImageObjectDecoderTest.php +++ b/tests/Unit/Drivers/Gd/Decoders/ImageObjectDecoderTest.php @@ -6,12 +6,12 @@ namespace Intervention\Image\Tests\Unit\Drivers\Gd\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; -use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; +use Intervention\Image\Decoders\ImageObjectDecoder; use Intervention\Image\Image; use Intervention\Image\Tests\GdTestCase; #[RequiresPhpExtension('gd')] -#[CoversClass(\Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder::class)] +#[CoversClass(\Intervention\Image\Decoders\ImageObjectDecoder::class)] final class ImageObjectDecoderTest extends GdTestCase { public function testDecode(): void diff --git a/tests/Unit/Drivers/Gd/Decoders/NativeObjectDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/NativeObjectDecoderTest.php new file mode 100644 index 00000000..0b77f263 --- /dev/null +++ b/tests/Unit/Drivers/Gd/Decoders/NativeObjectDecoderTest.php @@ -0,0 +1,34 @@ +decoder = new NativeObjectDecoder(); + $this->decoder->setDriver(new Driver()); + } + + public function testDecode(): void + { + $result = $this->decoder->decode( + imagecreatetruecolor(3, 2) + ); + + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Unit/Drivers/Gd/Decoders/SplFileInfoImageDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/SplFileInfoImageDecoderTest.php index e7517235..d0a5617e 100644 --- a/tests/Unit/Drivers/Gd/Decoders/SplFileInfoImageDecoderTest.php +++ b/tests/Unit/Drivers/Gd/Decoders/SplFileInfoImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Gd\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Gd\Decoders\SplFileInfoImageDecoder; +use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; use SplFileInfo; @@ -18,6 +19,8 @@ final class SplFileInfoImageDecoderTest extends BaseTestCase public function testDecode(): void { $decoder = new SplFileInfoImageDecoder(); + $decoder->setDriver(new Driver()); + $result = $decoder->decode( new SplFileInfo($this->getTestResourcePath('blue.gif')) ); diff --git a/tests/Unit/Drivers/Gd/ImageTest.php b/tests/Unit/Drivers/Gd/ImageTest.php index 68530ac6..89c0f550 100644 --- a/tests/Unit/Drivers/Gd/ImageTest.php +++ b/tests/Unit/Drivers/Gd/ImageTest.php @@ -7,7 +7,6 @@ namespace Intervention\Image\Tests\Unit\Drivers\Gd; use Intervention\Image\Analyzers\WidthAnalyzer; use Intervention\Image\Collection; use Intervention\Image\Colors\Hsl\Colorspace; -use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Core; use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Drivers\Gd\Frame; @@ -280,17 +279,16 @@ final class ImageTest extends GdTestCase $this->assertInstanceOf(Image::class, $this->image->text('test', 0, 0, new Font())); } - public function testSetGetBlendingColor(): void + public function testBlendTransparencyDefault(): void { $image = $this->readTestImage('gradient.gif'); - $this->assertInstanceOf(ColorInterface::class, $image->blendingColor()); - $this->assertColor(255, 255, 255, 0, $image->blendingColor()); - $result = $image->setBlendingColor(new Color(1, 2, 3, 4)); - $this->assertColor(1, 2, 3, 4, $result->blendingColor()); - $this->assertColor(1, 2, 3, 4, $image->blendingColor()); + $this->assertColor(0, 0, 0, 0, $image->pickColor(1, 0)); + $result = $image->blendTransparency(); + $this->assertColor(255, 255, 255, 255, $image->pickColor(1, 0)); + $this->assertColor(255, 255, 255, 255, $result->pickColor(1, 0)); } - public function testBlendTransparency(): void + public function testBlendTransparencyArgument(): void { $image = $this->readTestImage('gradient.gif'); $this->assertColor(0, 0, 0, 0, $image->pickColor(1, 0)); diff --git a/tests/Unit/Drivers/Gd/InputHandlerTest.php b/tests/Unit/Drivers/Gd/InputHandlerTest.php deleted file mode 100644 index f2e2e475..00000000 --- a/tests/Unit/Drivers/Gd/InputHandlerTest.php +++ /dev/null @@ -1,153 +0,0 @@ -expectException(DecoderException::class); - $handler->handle(''); - } - - public function testHandleBinaryImage(): void - { - $handler = new InputHandler(); - $input = file_get_contents($this->getTestResourcePath('test.jpg')); - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleGdImage(): void - { - $handler = new InputHandler(); - $result = $handler->handle(imagecreatetruecolor(3, 2)); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleSplFileInfo(): void - { - $handler = new InputHandler(); - $input = new SplFileInfo($this->getTestResourcePath('test.jpg')); - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleFilePathImage(): void - { - $handler = new InputHandler(); - $input = $this->getTestResourcePath('animation.gif'); - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleBase64Image(): void - { - $handler = new InputHandler(); - $input = base64_encode(file_get_contents($this->getTestResourcePath('animation.gif'))); - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleDataUriImage(): void - { - $handler = new InputHandler(); - $input = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNb' . - 'yblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleHexColor(): void - { - $handler = new InputHandler(); - $input = 'ccff33'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51, 255], $result->toArray()); - - $handler = new InputHandler(); - $input = 'cf3'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51, 255], $result->toArray()); - - $handler = new InputHandler(); - $input = '#123456'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([18, 52, 86, 255], $result->toArray()); - - $handler = new InputHandler(); - $input = '#333'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51, 255], $result->toArray()); - - $handler = new InputHandler(); - $input = '#3333'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51, 51], $result->toArray()); - - $handler = new InputHandler(); - $input = '#33333333'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51, 51], $result->toArray()); - } - - public function testHandleRgbString(): void - { - $handler = new InputHandler(); - $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([10, 20, 30, 255], $result->toArray()); - - $handler = new InputHandler(); - $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([10, 20, 30, 255], $result->toArray()); - } - - public function testHandleHsvString(): void - { - $handler = new InputHandler(); - $result = $handler->handle('hsv(10, 20, 30)'); - $this->assertInstanceOf(HsvColor::class, $result); - $this->assertEquals([10, 20, 30], $result->toArray()); - } - - public function testHandleCmykString(): void - { - $handler = new InputHandler(); - $result = $handler->handle('cmyk(10, 20, 30, 40)'); - $this->assertInstanceOf(CmykColor::class, $result); - $this->assertEquals([10, 20, 30, 40], $result->toArray()); - } - - public function testHandleTransparent(): void - { - $handler = new InputHandler(); - $input = 'transparent'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([255, 255, 255, 0], $result->toArray()); - } -} diff --git a/tests/Unit/Drivers/Gd/Traits/CanDecodeGifTest.php b/tests/Unit/Drivers/Gd/Traits/CanDecodeGifTest.php deleted file mode 100644 index f8cdafe8..00000000 --- a/tests/Unit/Drivers/Gd/Traits/CanDecodeGifTest.php +++ /dev/null @@ -1,57 +0,0 @@ -makePartial(); - - $result = $decoder->decodeGif($this->getTestResourceData('animation.gif')); - $this->assertInstanceOf(ImageInterface::class, $result); - $this->assertEquals('image/gif', $result->origin()->mediaType()); - } - - public function testDecodeGifFromBinaryStatic(): void - { - $decoder = Mockery::mock(new class () { - use CanDecodeGif; - })->makePartial(); - - $result = $decoder->decodeGif($this->getTestResourceData('red.gif')); - $this->assertInstanceOf(ImageInterface::class, $result); - $this->assertEquals('image/gif', $result->origin()->mediaType()); - } - - public function testDecodeGifFromPathAnimation(): void - { - $decoder = Mockery::mock(new class () { - use CanDecodeGif; - })->makePartial(); - - $result = $decoder->decodeGif($this->getTestResourcePath('animation.gif')); - $this->assertInstanceOf(ImageInterface::class, $result); - $this->assertEquals('image/gif', $result->origin()->mediaType()); - } - - public function testDecodeGifFromPathStatic(): void - { - $decoder = Mockery::mock(new class () { - use CanDecodeGif; - })->makePartial(); - - $result = $decoder->decodeGif($this->getTestResourcePath('red.gif')); - $this->assertInstanceOf(ImageInterface::class, $result); - $this->assertEquals('image/gif', $result->origin()->mediaType()); - } -} diff --git a/tests/Unit/Drivers/Imagick/Decoders/Base64ImageDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/Base64ImageDecoderTest.php index 82910d26..d4f622eb 100644 --- a/tests/Unit/Drivers/Imagick/Decoders/Base64ImageDecoderTest.php +++ b/tests/Unit/Drivers/Imagick/Decoders/Base64ImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Imagick\Decoders\Base64ImageDecoder; +use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; @@ -20,6 +21,7 @@ final class Base64ImageDecoderTest extends BaseTestCase protected function setUp(): void { $this->decoder = new Base64ImageDecoder(); + $this->decoder->setDriver(new Driver()); } public function testDecode(): void diff --git a/tests/Unit/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php index 6cab39a4..2b221166 100644 --- a/tests/Unit/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php +++ b/tests/Unit/Drivers/Imagick/Decoders/BinaryImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Decoders; use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Drivers\Imagick\Decoders\BinaryImageDecoder; +use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; @@ -14,10 +15,17 @@ use stdClass; final class BinaryImageDecoderTest extends BaseTestCase { + protected BinaryImageDecoder $decoder; + + protected function setUp(): void + { + $this->decoder = new BinaryImageDecoder(); + $this->decoder->setDriver(new Driver()); + } + public function testDecodePng(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('tile.png'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('tile.png'))); $this->assertInstanceOf(Image::class, $image); $this->assertInstanceOf(RgbColorspace::class, $image->colorspace()); $this->assertEquals(16, $image->width()); @@ -27,8 +35,7 @@ final class BinaryImageDecoderTest extends BaseTestCase public function testDecodeGif(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('red.gif'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('red.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->width()); $this->assertEquals(16, $image->height()); @@ -37,8 +44,7 @@ final class BinaryImageDecoderTest extends BaseTestCase public function testDecodeAnimatedGif(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('cats.gif'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('cats.gif'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(75, $image->width()); $this->assertEquals(50, $image->height()); @@ -47,8 +53,7 @@ final class BinaryImageDecoderTest extends BaseTestCase public function testDecodeJpegWithExif(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('exif.jpg'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('exif.jpg'))); $this->assertInstanceOf(Image::class, $image); $this->assertEquals(16, $image->width()); $this->assertEquals(16, $image->height()); @@ -58,16 +63,14 @@ final class BinaryImageDecoderTest extends BaseTestCase public function testDecodeCmykImage(): void { - $decoder = new BinaryImageDecoder(); - $image = $decoder->decode(file_get_contents($this->getTestResourcePath('cmyk.jpg'))); + $image = $this->decoder->decode(file_get_contents($this->getTestResourcePath('cmyk.jpg'))); $this->assertInstanceOf(Image::class, $image); $this->assertInstanceOf(CmykColorspace::class, $image->colorspace()); } public function testDecodeNonString(): void { - $decoder = new BinaryImageDecoder(); $this->expectException(DecoderException::class); - $decoder->decode(new stdClass()); + $this->decoder->decode(new stdClass()); } } diff --git a/tests/Unit/Drivers/Imagick/Decoders/DataUriImageDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/DataUriImageDecoderTest.php index 1a4ca500..db8de4d5 100644 --- a/tests/Unit/Drivers/Imagick/Decoders/DataUriImageDecoderTest.php +++ b/tests/Unit/Drivers/Imagick/Decoders/DataUriImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Imagick\Decoders\DataUriImageDecoder; +use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; @@ -21,6 +22,7 @@ final class DataUriImageDecoderTest extends BaseTestCase protected function setUp(): void { $this->decoder = new DataUriImageDecoder(); + $this->decoder->setDriver(new Driver()); } public function testDecode(): void diff --git a/tests/Unit/Drivers/Imagick/Decoders/FilePathImageDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/FilePathImageDecoderTest.php index 2a8ffcd6..8e5cd768 100644 --- a/tests/Unit/Drivers/Imagick/Decoders/FilePathImageDecoderTest.php +++ b/tests/Unit/Drivers/Imagick/Decoders/FilePathImageDecoderTest.php @@ -7,10 +7,11 @@ namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder; +use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; -use stdClass; +use PHPUnit\Framework\Attributes\DataProvider; #[RequiresPhpExtension('imagick')] #[CoversClass(\Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder::class)] @@ -21,32 +22,35 @@ final class FilePathImageDecoderTest extends BaseTestCase protected function setUp(): void { $this->decoder = new FilePathImageDecoder(); + $this->decoder->setDriver(new Driver()); } - public function testDecode(): void + #[DataProvider('validFormatPathsProvider')] + public function testDecode(string $path, bool $result): void { - $result = $this->decoder->decode( - $this->getTestResourcePath() - ); + if ($result === false) { + $this->expectException(DecoderException::class); + } - $this->assertInstanceOf(Image::class, $result); + $result = $this->decoder->decode($path); + + if ($result === true) { + $this->assertInstanceOf(Image::class, $result); + } } - public function testDecoderNonString(): void + public static function validFormatPathsProvider(): array { - $this->expectException(DecoderException::class); - $this->decoder->decode(new stdClass()); - } - - public function testDecoderNoPath(): void - { - $this->expectException(DecoderException::class); - $this->decoder->decode('no-path'); - } - - public function testDecoderTooLongPath(): void - { - $this->expectException(DecoderException::class); - $this->decoder->decode(str_repeat('x', PHP_MAXPATHLEN + 1)); + return [ + [self::getTestResourcePath('cats.gif'), true], + [self::getTestResourcePath('animation.gif'), true], + [self::getTestResourcePath('red.gif'), true], + [self::getTestResourcePath('green.gif'), true], + [self::getTestResourcePath('blue.gif'), true], + [self::getTestResourcePath('gradient.bmp'), true], + [self::getTestResourcePath('circle.png'), true], + ['no-path', false], + [str_repeat('x', PHP_MAXPATHLEN + 1), false], + ]; } } diff --git a/tests/Unit/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php index 8165b3c2..15724014 100644 --- a/tests/Unit/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php +++ b/tests/Unit/Drivers/Imagick/Decoders/FilePointerImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Imagick\Decoders\FilePointerImageDecoder; +use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Image; use Intervention\Image\Tests\ImagickTestCase; @@ -17,6 +18,7 @@ final class FilePointerImageDecoderTest extends ImagickTestCase public function testDecode(): void { $decoder = new FilePointerImageDecoder(); + $decoder->setDriver(new Driver()); $fp = fopen($this->getTestResourcePath('test.jpg'), 'r'); $result = $decoder->decode($fp); $this->assertInstanceOf(Image::class, $result); diff --git a/tests/Unit/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php index 7adf976b..70efd2b3 100644 --- a/tests/Unit/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php +++ b/tests/Unit/Drivers/Imagick/Decoders/ImageObjectDecoderTest.php @@ -6,12 +6,12 @@ namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; -use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; +use Intervention\Image\Decoders\ImageObjectDecoder; use Intervention\Image\Image; use Intervention\Image\Tests\ImagickTestCase; #[RequiresPhpExtension('imagick')] -#[CoversClass(\Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder::class)] +#[CoversClass(\Intervention\Image\Decoders\ImageObjectDecoder::class)] final class ImageObjectDecoderTest extends ImagickTestCase { public function testDecode(): void diff --git a/tests/Unit/Drivers/Imagick/Decoders/NativeObjectDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/NativeObjectDecoderTest.php new file mode 100644 index 00000000..e397c36e --- /dev/null +++ b/tests/Unit/Drivers/Imagick/Decoders/NativeObjectDecoderTest.php @@ -0,0 +1,36 @@ +decoder = new NativeObjectDecoder(); + $this->decoder->setDriver(new Driver()); + } + + public function testDecode(): void + { + $native = new Imagick(); + $native->newImage(3, 2, new ImagickPixel('red'), 'png'); + $result = $this->decoder->decode($native); + + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Unit/Drivers/Imagick/Decoders/SplFileInfoImageDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/SplFileInfoImageDecoderTest.php index 3e549909..c4c26c86 100644 --- a/tests/Unit/Drivers/Imagick/Decoders/SplFileInfoImageDecoderTest.php +++ b/tests/Unit/Drivers/Imagick/Decoders/SplFileInfoImageDecoderTest.php @@ -7,6 +7,7 @@ namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Decoders; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use Intervention\Image\Drivers\Imagick\Decoders\SplFileInfoImageDecoder; +use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Image; use Intervention\Image\Tests\BaseTestCase; use SplFileInfo; @@ -18,6 +19,7 @@ final class SplFileInfoImageDecoderTest extends BaseTestCase public function testDecode(): void { $decoder = new SplFileInfoImageDecoder(); + $decoder->setDriver(new Driver()); $result = $decoder->decode( new SplFileInfo($this->getTestResourcePath('blue.gif')) ); diff --git a/tests/Unit/Drivers/Imagick/ImageTest.php b/tests/Unit/Drivers/Imagick/ImageTest.php index 777fbc3e..cc92b349 100644 --- a/tests/Unit/Drivers/Imagick/ImageTest.php +++ b/tests/Unit/Drivers/Imagick/ImageTest.php @@ -8,7 +8,6 @@ use Imagick; use Intervention\Image\Analyzers\WidthAnalyzer; use Intervention\Image\Collection; use Intervention\Image\Colors\Cmyk\Colorspace as CmykColorspace; -use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Drivers\Imagick\Core; use Intervention\Image\Drivers\Imagick\Driver; @@ -24,6 +23,7 @@ use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ResolutionInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Modifiers\GreyscaleModifier; +use Intervention\Image\Origin; use Intervention\Image\Tests\ImagickTestCase; final class ImageTest extends ImagickTestCase @@ -97,6 +97,16 @@ final class ImageTest extends ImagickTestCase $this->assertEquals(10, $this->image->loops()); } + public function testSetGetOrigin(): void + { + $origin = $this->image->origin(); + $this->assertInstanceOf(Origin::class, $origin); + $this->image->setOrigin(new Origin('test1', 'test2')); + $this->assertInstanceOf(Origin::class, $this->image->origin()); + $this->assertEquals('test1', $this->image->origin()->mimetype()); + $this->assertEquals('test2', $this->image->origin()->filePath()); + } + public function testRemoveAnimation(): void { $this->assertTrue($this->image->isAnimated()); @@ -263,17 +273,16 @@ final class ImageTest extends ImagickTestCase $this->assertInstanceOf(Image::class, $this->image->sharpen(12)); } - public function testSetGetBlendingColor(): void + public function testBlendTransparencyDefault(): void { $image = $this->readTestImage('gradient.gif'); - $this->assertInstanceOf(ColorInterface::class, $image->blendingColor()); - $this->assertColor(255, 255, 255, 0, $image->blendingColor()); - $result = $image->setBlendingColor(new Color(1, 2, 3, 4)); - $this->assertColor(1, 2, 3, 4, $result->blendingColor()); - $this->assertColor(1, 2, 3, 4, $image->blendingColor()); + $this->assertColor(0, 0, 0, 0, $image->pickColor(1, 0)); + $result = $image->blendTransparency(); + $this->assertColor(255, 255, 255, 255, $image->pickColor(1, 0)); + $this->assertColor(255, 255, 255, 255, $result->pickColor(1, 0)); } - public function testBlendTransparency(): void + public function testBlendTransparencyArgument(): void { $image = $this->readTestImage('gradient.gif'); $this->assertColor(0, 0, 0, 0, $image->pickColor(1, 0)); diff --git a/tests/Unit/Drivers/Imagick/InputHandlerTest.php b/tests/Unit/Drivers/Imagick/InputHandlerTest.php deleted file mode 100644 index 1b4fbf39..00000000 --- a/tests/Unit/Drivers/Imagick/InputHandlerTest.php +++ /dev/null @@ -1,157 +0,0 @@ -expectException(DecoderException::class); - $handler->handle(''); - } - - public function testHandleBinaryImage(): void - { - $handler = new InputHandler(); - $input = file_get_contents($this->getTestResourcePath('animation.gif')); - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleImagick(): void - { - $imagick = new Imagick(); - $imagick->newImage(3, 2, new ImagickPixel('rgba(255, 255, 255, 255)'), 'png'); - $handler = new InputHandler(); - $result = $handler->handle($imagick); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleSplFileInfo(): void - { - $handler = new InputHandler(); - $input = new SplFileInfo($this->getTestResourcePath('test.jpg')); - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleFilePathImage(): void - { - $handler = new InputHandler(); - $input = $this->getTestResourcePath('animation.gif'); - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleBase64Image(): void - { - $handler = new InputHandler(); - $input = base64_encode(file_get_contents($this->getTestResourcePath('animation.gif'))); - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleDataUriImage(): void - { - $handler = new InputHandler(); - $input = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACN' . - 'byblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; - $result = $handler->handle($input); - $this->assertInstanceOf(Image::class, $result); - } - - public function testHandleHexColor(): void - { - $handler = new InputHandler(); - $input = 'ccff33'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51, 255], $result->toArray()); - - $handler = new InputHandler(); - $input = 'cf3'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([204, 255, 51, 255], $result->toArray()); - - $handler = new InputHandler(); - $input = '#123456'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([18, 52, 86, 255], $result->toArray()); - - $handler = new InputHandler(); - $input = '#333'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51, 255], $result->toArray()); - - $handler = new InputHandler(); - $input = '#3333'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51, 51], $result->toArray()); - - $handler = new InputHandler(); - $input = '#33333333'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([51, 51, 51, 51], $result->toArray()); - } - - public function testHandleRgbString(): void - { - $handler = new InputHandler(); - $result = $handler->handle('rgb(10, 20, 30)'); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([10, 20, 30, 255], $result->toArray()); - - $handler = new InputHandler(); - $result = $handler->handle('rgba(10, 20, 30, 1.0)'); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([10, 20, 30, 255], $result->toArray()); - } - - public function testHandleCmykString(): void - { - $handler = new InputHandler(); - $result = $handler->handle('cmyk(10, 20, 30, 40)'); - $this->assertInstanceOf(CmykColor::class, $result); - $this->assertEquals([10, 20, 30, 40], $result->toArray()); - } - - public function testHandleHsvString(): void - { - $handler = new InputHandler(); - $result = $handler->handle('hsv(10, 20, 30)'); - $this->assertInstanceOf(HsvColor::class, $result); - $this->assertEquals([10, 20, 30], $result->toArray()); - } - - public function testHandleTransparent(): void - { - $handler = new InputHandler(); - $input = 'transparent'; - $result = $handler->handle($input); - $this->assertInstanceOf(RgbColor::class, $result); - $this->assertEquals([255, 255, 255, 0], $result->toArray()); - } -} diff --git a/tests/Unit/ImageManagerTest.php b/tests/Unit/ImageManagerTest.php deleted file mode 100644 index 2b28e09d..00000000 --- a/tests/Unit/ImageManagerTest.php +++ /dev/null @@ -1,207 +0,0 @@ -assertInstanceOf(ImageManager::class, $manager); - - $manager = new ImageManager(GdDriver::class); - $this->assertInstanceOf(ImageManager::class, $manager); - } - - public function testWithDriver(): void - { - $manager = ImageManager::withDriver(new GdDriver()); - $this->assertInstanceOf(ImageManager::class, $manager); - - $manager = ImageManager::withDriver(GdDriver::class); - $this->assertInstanceOf(ImageManager::class, $manager); - } - - public function testDriver(): void - { - $driver = new GdDriver(); - $manager = ImageManager::withDriver($driver); - $this->assertEquals($driver, $manager->driver()); - } - - public function testDriverStatics(): void - { - $manager = ImageManager::gd(); - $this->assertInstanceOf(ImageManager::class, $manager); - - $manager = ImageManager::imagick(); - $this->assertInstanceOf(ImageManager::class, $manager); - } - - #[RequiresPhpExtension('gd')] - public function testCreateGd(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->create(5, 4); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('gd')] - public function testAnimateGd(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->animate(function ($animation) { - $animation->add($this->getTestResourcePath('red.gif'), .25); - }); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('gd')] - public function testReadGd(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif')); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('gd')] - public function testReadGdWithDecoderClassname(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), FilePathImageDecoder::class); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('gd')] - public function testReadGdWithDecoderInstance(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), new FilePathImageDecoder()); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('gd')] - public function testReadGdWithDecoderClassnameArray(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), [FilePathImageDecoder::class]); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('gd')] - public function testReadGdWithDecoderInstanceArray(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), [new FilePathImageDecoder()]); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('gd')] - public function testReadGdWithDecoderInstanceArrayMultiple(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), [ - new BinaryImageDecoder(), - new FilePathImageDecoder(), - ]); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('gd')] - public function testReadGdWithRotationAdjustment(): void - { - $manager = new ImageManager(GdDriver::class); - $image = $manager->read($this->getTestResourcePath('orientation.jpg')); - $this->assertColor(255, 255, 255, 255, $image->pickColor(0, 24)); - } - - #[RequiresPhpExtension('imagick')] - public function testCreateImagick(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->create(5, 4); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('imagick')] - public function testAnimateImagick(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->animate(function ($animation) { - $animation->add($this->getTestResourcePath('red.gif'), .25); - }); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('imagick')] - public function testReadImagick(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif')); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('imagick')] - public function testReadImagickWithDecoderClassname(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), FilePathImageDecoder::class); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('imagick')] - public function testReadImagickWithDecoderInstance(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), new FilePathImageDecoder()); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('imagick')] - public function testReadImagickWithDecoderClassnameArray(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), [FilePathImageDecoder::class]); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('imagick')] - public function testReadImagickWithDecoderInstanceArray(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), [new FilePathImageDecoder()]); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('imagick')] - public function testReadImagickWithDecoderInstanceArrayMultiple(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->read($this->getTestResourcePath('red.gif'), [ - new BinaryImageDecoder(), - new FilePathImageDecoder(), - ]); - $this->assertInstanceOf(ImageInterface::class, $image); - } - - #[RequiresPhpExtension('imagick')] - public function testReadImagickWithRotationAdjustment(): void - { - $manager = new ImageManager(ImagickDriver::class); - $image = $manager->read($this->getTestResourcePath('orientation.jpg')); - $this->assertColor(255, 255, 255, 255, $image->pickColor(0, 24)); - } -} diff --git a/tests/Unit/ImageManagerTestGd.php b/tests/Unit/ImageManagerTestGd.php new file mode 100644 index 00000000..78352096 --- /dev/null +++ b/tests/Unit/ImageManagerTestGd.php @@ -0,0 +1,157 @@ +assertInstanceOf(ImageManager::class, $manager); + + $manager = new ImageManager(Driver::class); + $this->assertInstanceOf(ImageManager::class, $manager); + } + + public function testWithDriver(): void + { + $manager = ImageManager::withDriver(new Driver()); + $this->assertInstanceOf(ImageManager::class, $manager); + + $manager = ImageManager::withDriver(Driver::class); + $this->assertInstanceOf(ImageManager::class, $manager); + } + + public function testDriver(): void + { + $driver = new Driver(); + $manager = ImageManager::withDriver($driver); + $this->assertEquals($driver, $manager->driver()); + } + + public function testDriverStatic(): void + { + $manager = ImageManager::gd(); + $this->assertInstanceOf(ImageManager::class, $manager); + } + + public function testCreate(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->create(5, 4); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testAnimate(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->animate(function ($animation) { + $animation->add($this->getTestResourcePath('red.gif'), .25); + }); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testRead(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif')); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderClassname(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), FilePathImageDecoder::class); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderInstance(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), new FilePathImageDecoder()); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderClassnameArray(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), [FilePathImageDecoder::class]); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderInstanceArray(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), [new FilePathImageDecoder()]); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderInstanceArrayMultiple(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), [ + new BinaryImageDecoder(), + new FilePathImageDecoder(), + ]); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithRotationAdjustment(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('orientation.jpg')); + $this->assertColor(1, 0, 254, 255, $image->pickColor(3, 3)); + } + + public function testReadWithoutRotationAdjustment(): void + { + $manager = new ImageManager(Driver::class, autoOrientation: false); + $image = $manager->read($this->getTestResourcePath('orientation.jpg')); + $this->assertColor(250, 2, 3, 255, $image->pickColor(3, 3)); + } + + public function testReadAnimation(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('animation.gif')); + $this->assertTrue($image->isAnimated()); + } + + public function testReadAnimationDiscarded(): void + { + $manager = new ImageManager(Driver::class, decodeAnimation: false); + $image = $manager->read($this->getTestResourcePath('animation.gif')); + $this->assertFalse($image->isAnimated()); + } + + public function testApplyBlendingColorDefault(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('blocks.png')); + $result = $image->blendTransparency(); + $this->assertColor(255, 255, 255, 255, $image->pickColor(530, 0)); + $this->assertColor(255, 255, 255, 255, $result->pickColor(530, 0)); + } + + public function testApplyBlendingColorConfigured(): void + { + $manager = new ImageManager(Driver::class, blendingColor: 'ff5500'); + $image = $manager->read($this->getTestResourcePath('blocks.png')); + $result = $image->blendTransparency(); + $this->assertColor(255, 85, 0, 255, $image->pickColor(530, 0)); + $this->assertColor(255, 85, 0, 255, $result->pickColor(530, 0)); + } +} diff --git a/tests/Unit/ImageManagerTestImagick.php b/tests/Unit/ImageManagerTestImagick.php new file mode 100644 index 00000000..422229b1 --- /dev/null +++ b/tests/Unit/ImageManagerTestImagick.php @@ -0,0 +1,157 @@ +assertInstanceOf(ImageManager::class, $manager); + + $manager = new ImageManager(Driver::class); + $this->assertInstanceOf(ImageManager::class, $manager); + } + + public function testWithDriver(): void + { + $manager = ImageManager::withDriver(new Driver()); + $this->assertInstanceOf(ImageManager::class, $manager); + + $manager = ImageManager::withDriver(Driver::class); + $this->assertInstanceOf(ImageManager::class, $manager); + } + + public function testDriver(): void + { + $driver = new Driver(); + $manager = ImageManager::withDriver($driver); + $this->assertEquals($driver, $manager->driver()); + } + + public function testDriverStatic(): void + { + $manager = ImageManager::imagick(); + $this->assertInstanceOf(ImageManager::class, $manager); + } + + public function testCreate(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->create(5, 4); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testAnimate(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->animate(function ($animation) { + $animation->add($this->getTestResourcePath('red.gif'), .25); + }); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testRead(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif')); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderClassname(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), FilePathImageDecoder::class); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderInstance(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), new FilePathImageDecoder()); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderClassnameArray(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), [FilePathImageDecoder::class]); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderInstanceArray(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), [new FilePathImageDecoder()]); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithDecoderInstanceArrayMultiple(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('red.gif'), [ + new BinaryImageDecoder(), + new FilePathImageDecoder(), + ]); + $this->assertInstanceOf(ImageInterface::class, $image); + } + + public function testReadWithRotationAdjustment(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('orientation.jpg')); + $this->assertColor(1, 0, 254, 255, $image->pickColor(3, 3)); + } + + public function testReadWithoutRotationAdjustment(): void + { + $manager = new ImageManager(Driver::class, autoOrientation: false); + $image = $manager->read($this->getTestResourcePath('orientation.jpg')); + $this->assertColor(250, 2, 3, 255, $image->pickColor(3, 3)); + } + + public function testReadAnimation(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('animation.gif')); + $this->assertTrue($image->isAnimated()); + } + + public function testReadAnimationDiscarded(): void + { + $manager = new ImageManager(Driver::class, decodeAnimation: false); + $image = $manager->read($this->getTestResourcePath('animation.gif')); + $this->assertFalse($image->isAnimated()); + } + + public function testApplyBlendingColor(): void + { + $manager = new ImageManager(Driver::class); + $image = $manager->read($this->getTestResourcePath('blocks.png')); + $result = $image->blendTransparency(); + $this->assertColor(255, 255, 255, 255, $image->pickColor(530, 0)); + $this->assertColor(255, 255, 255, 255, $result->pickColor(530, 0)); + } + + public function testApplyBlendingColorConfigured(): void + { + $manager = new ImageManager(Driver::class, blendingColor: 'ff5500'); + $image = $manager->read($this->getTestResourcePath('blocks.png')); + $result = $image->blendTransparency(); + $this->assertColor(255, 85, 0, 255, $image->pickColor(530, 0)); + $this->assertColor(255, 85, 0, 255, $result->pickColor(530, 0)); + } +} diff --git a/tests/Unit/InputHandlerTest.php b/tests/Unit/InputHandlerTest.php new file mode 100644 index 00000000..f231d8f1 --- /dev/null +++ b/tests/Unit/InputHandlerTest.php @@ -0,0 +1,74 @@ +assertInstanceOf($outputClassname, $handler->handle($input)); + } else { + $this->expectException($outputClassname); + $handler->handle($input); + } + } + + public static function testHandleProvider(): array + { + $base = [ + [null, DecoderException::class], + ['', DecoderException::class], + ['fff', ColorInterface::class], + ['rgba(0, 0, 0, 0)', ColorInterface::class], + ['cmyk(0, 0, 0, 0)', ColorInterface::class], + ['hsv(0, 0, 0)', ColorInterface::class], + ['hsl(0, 0, 0)', ColorInterface::class], + ['transparent', ColorInterface::class], + ['steelblue', ColorInterface::class], + [self::getTestResourcePath(), ImageInterface::class], + [file_get_contents(self::getTestResourcePath()), ImageInterface::class], + ]; + + $data = []; + $drivers = [GdDriver::class, ImagickDriver::class]; + foreach ($drivers as $driver) { + foreach ($base as $line) { + array_unshift($line, $driver); // prepend driver + $data[] = $line; + } + } + + return $data; + } + + public function testResolveWithoutDriver(): void + { + $handler = new InputHandler([new HexColorDecoder()]); + $result = $handler->handle('fff'); + $this->assertInstanceOf(ColorInterface::class, $result); + + $handler = new InputHandler([HexColorDecoder::class]); + $result = $handler->handle('fff'); + $this->assertInstanceOf(ColorInterface::class, $result); + } +}