1
0
mirror of https://github.com/Intervention/image.git synced 2025-01-17 12:18:14 +01:00

Merge pull request #1350 from Intervention/feature/config

Configuration API
This commit is contained in:
Oliver Vogel 2024-06-01 09:24:52 +02:00 committed by GitHub
commit 8d50e50dcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 1171 additions and 1204 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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);
}
/**

View File

@ -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);
}
/**

View File

@ -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);
}
/**

View File

@ -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);
}
/**

73
src/Config.php Normal file
View File

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Intervention\Image;
use Intervention\Image\Exceptions\InputException;
class Config
{
/**
* Create config object instance
*
* @param bool $autoOrientation
* @param bool $decodeAnimation
* @param mixed $blendingColor
* @return void
*/
public function __construct(
public bool $autoOrientation = true,
public bool $decodeAnimation = true,
public mixed $blendingColor = 'ffffff',
) {
}
/**
* Set values of given config options
*
* @param mixed $options
* @throws InputException
* @return Config
*/
public function setOptions(mixed ...$options): self
{
foreach ($this->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<mixed> $options
* @return array<string, mixed>
*/
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];
}
}

View File

@ -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)) {

View File

@ -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;
}
}

View File

@ -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
*

View File

@ -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

View File

@ -1,71 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Drivers;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\DecoderInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\InputHandlerInterface;
abstract class AbstractInputHandler implements InputHandlerInterface
{
/**
* Decoder classnames in hierarchical order
*
* @var array<string|DecoderInterface>
*/
protected array $decoders = [];
/**
* Create new input handler instance with given decoder classnames
*
* @param array<string|DecoderInterface> $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;
}
}

View File

@ -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;
}

View File

@ -1,23 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Drivers\Gd\Decoders;
use Intervention\Image\Drivers\AbstractDecoder;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\DecoderInterface;
use Intervention\Image\Interfaces\ImageInterface;
class ColorObjectDecoder extends AbstractDecoder implements DecoderInterface
{
public function decode(mixed $input): ImageInterface|ColorInterface
{
if (!is_a($input, ColorInterface::class)) {
throw new DecoderException('Unable to decode input');
}
return $input;
}
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Intervention\Image\Drivers\Gd\Decoders;
use Intervention\Image\Drivers\Gd\Decoders\Traits\CanDecodeGif;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\DecoderInterface;
@ -13,8 +12,6 @@ use Intervention\Image\Modifiers\AlignRotationModifier;
class FilePathImageDecoder extends NativeObjectDecoder implements DecoderInterface
{
use CanDecodeGif;
public function decode(mixed $input): ImageInterface|ColorInterface
{
if (!$this->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;
}

View File

@ -1,23 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Drivers\Gd\Decoders;
use Intervention\Image\Drivers\AbstractDecoder;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\DecoderInterface;
use Intervention\Image\Interfaces\ImageInterface;
class ImageObjectDecoder extends AbstractDecoder implements DecoderInterface
{
public function decode(mixed $input): ImageInterface|ColorInterface
{
if (!is_a($input, ImageInterface::class)) {
throw new DecoderException('Unable to decode input');
}
return $input;
}
}

View File

@ -5,13 +5,15 @@ declare(strict_types=1);
namespace Intervention\Image\Drivers\Gd\Decoders;
use GdImage;
use Intervention\Gif\Decoder as GifDecoder;
use Intervention\Gif\Splitter as GifSplitter;
use Intervention\Image\Drivers\Gd\Core;
use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\Drivers\Gd\Frame;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Image;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\ImageInterface;
class NativeObjectDecoder extends AbstractDecoder
{
@ -38,10 +40,67 @@ class NativeObjectDecoder extends AbstractDecoder
// build image instance
return new Image(
new Driver(),
$this->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;
}
}

View File

@ -1,54 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Drivers\Gd\Decoders\Traits;
use Intervention\Gif\Decoder as GifDecoder;
use Intervention\Gif\Splitter as GifSplitter;
use Intervention\Image\Drivers\Gd\Core;
use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\Drivers\Gd\Frame;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Image;
use Intervention\Image\Interfaces\ImageInterface;
trait CanDecodeGif
{
/**
* Decode image from given GIF source which can be either a file path or binary data
*
* @param mixed $input
* @throws RuntimeException
* @return ImageInterface
*/
protected function decodeGif(mixed $input): ImageInterface
{
$gif = GifDecoder::decode($input);
$splitter = GifSplitter::create($gif)->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;
}
}

View File

@ -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}
*

View File

@ -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);

View File

@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Drivers\Gd;
use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder;
use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder;
use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder;
use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder;
use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder;
use Intervention\Image\Colors\Hsv\Decoders\StringColorDecoder as HsvStringColorDecoder;
use Intervention\Image\Colors\Hsl\Decoders\StringColorDecoder as HslStringColorDecoder;
use Intervention\Image\Drivers\AbstractInputHandler;
use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder;
use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder;
use Intervention\Image\Drivers\Gd\Decoders\FilePointerImageDecoder;
use Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder;
use Intervention\Image\Drivers\Gd\Decoders\BinaryImageDecoder;
use Intervention\Image\Drivers\Gd\Decoders\DataUriImageDecoder;
use Intervention\Image\Drivers\Gd\Decoders\Base64ImageDecoder;
use Intervention\Image\Drivers\Gd\Decoders\NativeObjectDecoder;
use Intervention\Image\Drivers\Gd\Decoders\SplFileInfoImageDecoder;
class InputHandler extends AbstractInputHandler
{
/**
* Decoders in hierarchical order
*
* @var array<string>
*/
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,
];
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Drivers\Imagick\Decoders;
use Intervention\Image\Drivers\SpecializableDecoder;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\ImageInterface;
class ImageObjectDecoder extends SpecializableDecoder
{
public function decode(mixed $input): ImageInterface|ColorInterface
{
if (!is_a($input, ImageInterface::class)) {
throw new DecoderException('Unable to decode input');
}
return $input;
}
}

View File

@ -6,13 +6,13 @@ namespace Intervention\Image\Drivers\Imagick\Decoders;
use Imagick;
use Intervention\Image\Drivers\Imagick\Core;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\Drivers\SpecializableDecoder;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Image;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Modifiers\AlignRotationModifier;
use Intervention\Image\Modifiers\RemoveAnimationModifier;
class NativeObjectDecoder extends SpecializableDecoder
{
@ -33,13 +33,21 @@ class NativeObjectDecoder extends SpecializableDecoder
$input = $input->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());

View File

@ -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}
*

View File

@ -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

View File

@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Drivers\Imagick;
use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder;
use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder;
use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder;
use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder;
use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder;
use Intervention\Image\Colors\Hsv\Decoders\StringColorDecoder as HsvStringColorDecoder;
use Intervention\Image\Colors\Hsl\Decoders\StringColorDecoder as HslStringColorDecoder;
use Intervention\Image\Drivers\AbstractInputHandler;
use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder;
use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder;
use Intervention\Image\Drivers\Imagick\Decoders\FilePointerImageDecoder;
use Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder;
use Intervention\Image\Drivers\Imagick\Decoders\BinaryImageDecoder;
use Intervention\Image\Drivers\Imagick\Decoders\DataUriImageDecoder;
use Intervention\Image\Drivers\Imagick\Decoders\Base64ImageDecoder;
use Intervention\Image\Drivers\Imagick\Decoders\NativeObjectDecoder;
use Intervention\Image\Drivers\Imagick\Decoders\SplFileInfoImageDecoder;
class InputHandler extends AbstractInputHandler
{
/**
* Decoders in hierarchical order
*
* @var array<string>
*/
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,
];
}

View File

@ -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;
}

View File

@ -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

View File

@ -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}
*

View File

@ -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;
}
}

135
src/InputHandler.php Normal file
View File

@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
namespace Intervention\Image;
use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder;
use Intervention\Image\Colors\Hsl\Decoders\StringColorDecoder as HslStringColorDecoder;
use Intervention\Image\Colors\Hsv\Decoders\StringColorDecoder as HsvStringColorDecoder;
use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder as RgbHexColorDecoder;
use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder;
use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorDecoder;
use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder;
use Intervention\Image\Decoders\Base64ImageDecoder;
use Intervention\Image\Decoders\BinaryImageDecoder;
use Intervention\Image\Decoders\ColorObjectDecoder;
use Intervention\Image\Decoders\DataUriImageDecoder;
use Intervention\Image\Decoders\FilePathImageDecoder;
use Intervention\Image\Decoders\FilePointerImageDecoder;
use Intervention\Image\Decoders\ImageObjectDecoder;
use Intervention\Image\Decoders\NativeObjectDecoder;
use Intervention\Image\Decoders\SplFileInfoImageDecoder;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Exceptions\DriverException;
use Intervention\Image\Exceptions\NotSupportedException;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\DecoderInterface;
use Intervention\Image\Interfaces\DriverInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\InputHandlerInterface;
class InputHandler implements InputHandlerInterface
{
/**
* Decoder classnames in hierarchical order
*
* @var array<string|DecoderInterface>
*/
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<string|DecoderInterface> $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<string|DecoderInterface> $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());
}
}

View File

@ -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<string|object> $objects
* @throws NotSupportedException
* @throws DriverException
* @return array<object>
*/
public function specializeMultiple(array $objects): array;

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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)
);
}

View File

@ -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)
);
}

83
tests/Unit/ConfigTest.php Normal file
View File

@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit;
use Intervention\Image\Config;
use Intervention\Image\Tests\BaseTestCase;
use PHPUnit\Framework\Attributes\CoversClass;
#[CoversClass(Config::class)]
final class ConfigTest extends BaseTestCase
{
public function testConstructor(): void
{
$config = new Config();
$this->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);
}
}

View File

@ -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);

View File

@ -1,55 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit\Drivers;
use PHPUnit\Framework\Attributes\CoversClass;
use Intervention\Image\Drivers\AbstractDecoder;
use Intervention\Image\Drivers\AbstractInputHandler;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Tests\BaseTestCase;
use Mockery;
#[CoversClass(\Intervention\Image\Drivers\AbstractInputHandler::class)]
final class AbstractInputHandlerTest extends BaseTestCase
{
public function testHandle(): void
{
$image = Mockery::mock(ImageInterface::class);
$chain = Mockery::mock(AbstractDecoder::class);
$chain->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];
}
};
}
}

View File

@ -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

View File

@ -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());

View File

@ -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

View File

@ -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],
];
}
}

View File

@ -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);

View File

@ -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

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit\Drivers\Gd\Decoders;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Intervention\Image\Drivers\Gd\Decoders\NativeObjectDecoder;
use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\Image;
use Intervention\Image\Tests\BaseTestCase;
#[RequiresPhpExtension('gd')]
#[CoversClass(NativeObjectDecoder::class)]
final class NativeObjectDecoderTest extends BaseTestCase
{
protected NativeObjectDecoder $decoder;
protected function setUp(): void
{
$this->decoder = new NativeObjectDecoder();
$this->decoder->setDriver(new Driver());
}
public function testDecode(): void
{
$result = $this->decoder->decode(
imagecreatetruecolor(3, 2)
);
$this->assertInstanceOf(Image::class, $result);
}
}

View File

@ -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'))
);

View File

@ -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));

View File

@ -1,153 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit\Drivers\Gd;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Intervention\Image\Colors\Cmyk\Color as CmykColor;
use Intervention\Image\Colors\Hsv\Color as HsvColor;
use Intervention\Image\Colors\Rgb\Color as RgbColor;
use Intervention\Image\Image;
use Intervention\Image\Drivers\Gd\InputHandler;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Tests\BaseTestCase;
use SplFileInfo;
#[RequiresPhpExtension('gd')]
#[CoversClass(\Intervention\Image\Drivers\Gd\InputHandler::class)]
final class InputHandlerTest extends BaseTestCase
{
public function testHandleEmptyString(): void
{
$handler = new InputHandler();
$this->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());
}
}

View File

@ -1,57 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit\Drivers\Gd\Traits;
use Intervention\Image\Drivers\Gd\Decoders\Traits\CanDecodeGif;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Tests\BaseTestCase;
use Mockery;
final class CanDecodeGifTest extends BaseTestCase
{
public function testDecodeGifFromBinaryAnimation(): void
{
$decoder = Mockery::mock(new class () {
use CanDecodeGif;
})->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());
}
}

View File

@ -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

View File

@ -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());
}
}

View File

@ -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

View File

@ -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],
];
}
}

View File

@ -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);

View File

@ -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

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Decoders;
use Imagick;
use ImagickPixel;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Intervention\Image\Drivers\Imagick\Decoders\NativeObjectDecoder;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\Image;
use Intervention\Image\Tests\BaseTestCase;
#[RequiresPhpExtension('imagick')]
#[CoversClass(NativeObjectDecoder::class)]
final class NativeObjectDecoderTest extends BaseTestCase
{
protected NativeObjectDecoder $decoder;
protected function setUp(): void
{
$this->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);
}
}

View File

@ -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'))
);

View File

@ -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));

View File

@ -1,157 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit\Drivers\Imagick;
use Imagick;
use ImagickPixel;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Intervention\Image\Colors\Cmyk\Color as CmykColor;
use Intervention\Image\Colors\Hsv\Color as HsvColor;
use Intervention\Image\Colors\Rgb\Color as RgbColor;
use Intervention\Image\Drivers\Imagick\InputHandler;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Image;
use Intervention\Image\Tests\BaseTestCase;
use SplFileInfo;
#[RequiresPhpExtension('imagick')]
#[CoversClass(\Intervention\Image\Drivers\Imagick\InputHandler::class)]
final class InputHandlerTest extends BaseTestCase
{
public function testHandleEmptyString(): void
{
$handler = new InputHandler();
$this->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());
}
}

View File

@ -1,207 +0,0 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Intervention\Image\Decoders\BinaryImageDecoder;
use Intervention\Image\Decoders\FilePathImageDecoder;
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver;
use Intervention\Image\ImageManager;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Tests\BaseTestCase;
#[CoversClass(\Intervention\Image\ImageManager::class)]
final class ImageManagerTest extends BaseTestCase
{
public function testConstructor(): void
{
$manager = new ImageManager(new GdDriver());
$this->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));
}
}

View File

@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Intervention\Image\Decoders\BinaryImageDecoder;
use Intervention\Image\Decoders\FilePathImageDecoder;
use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\ImageManager;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Tests\BaseTestCase;
#[CoversClass(ImageManager::class)]
#[RequiresPhpExtension('gd')]
final class ImageManagerTestGd extends BaseTestCase
{
public function testConstructor(): void
{
$manager = new ImageManager(new Driver());
$this->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));
}
}

View File

@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use Intervention\Image\Decoders\BinaryImageDecoder;
use Intervention\Image\Decoders\FilePathImageDecoder;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\ImageManager;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Tests\BaseTestCase;
#[CoversClass(ImageManager::class)]
#[RequiresPhpExtension('imagick')]
final class ImageManagerTestImagick extends BaseTestCase
{
public function testConstructor(): void
{
$manager = new ImageManager(new Driver());
$this->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));
}
}

View File

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Intervention\Image\Tests\Unit;
use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder;
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\InputHandler;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Tests\BaseTestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
#[RequiresPhpExtension('gd')]
#[RequiresPhpExtension('imagick')]
#[CoversClass(InputHandler::class)]
class InputHandlerTest extends BaseTestCase
{
#[DataProvider('testHandleProvider')]
public function testHandleDefaultDecoders(string $driver, mixed $input, string $outputClassname): void
{
$handler = new InputHandler(driver: new $driver());
if ($outputClassname === ImageInterface::class || $outputClassname === ColorInterface::class) {
$this->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);
}
}