Files
eCert-MBIP/vendor/intervention/image/src/AnimationFactory.php

175 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Intervention\Image;
use Error;
use Intervention\Gif\DisposalMethod;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Exceptions\FilesystemException;
use Intervention\Image\Interfaces\AnimationFactoryInterface;
use Intervention\Image\Interfaces\DriverInterface;
use Intervention\Image\Interfaces\FrameInterface;
use Intervention\Image\Interfaces\ImageInterface;
class AnimationFactory implements AnimationFactoryInterface
{
/**
* Current frame number.
*/
protected int $currentFrameNumber = 0;
/**
* Image sources of animation frames.
*
* @var array<mixed>
*/
protected array $sources = [];
/**
* Frame delays of animation frames in seconds.
*
* @var array<float>
*/
protected array $delays = [];
/**
* Frame processing call names.
*
* @var array<null|string>
*/
protected array $processingCalls = [];
/**
* Frame processing arguments of calls.
*
* @var array<null|array<mixed>>
*/
protected array $processingArguments = [];
/**
* Create new instance.
*/
public function __construct(
protected int $width,
protected int $height,
null|callable $animation = null,
) {
if (is_callable($animation)) {
$animation($this);
}
}
/**
* {@inheritdoc}
*
* @see AnimationFactoryInterface::build()
*/
public static function build(
int $width,
int $height,
callable $animation,
DriverInterface $driver,
): ImageInterface {
return (new self($width, $height, $animation))->image($driver);
}
/**
* {@inheritdoc}
*
* @see AnimationFactoryInterface::add()
*/
public function add(mixed $source, float $delay = 1): AnimationFactoryInterface
{
$this->currentFrameNumber++;
$this->sources[$this->currentFrameNumber] = $source;
$this->delays[$this->currentFrameNumber] = $delay;
$this->processingCalls[$this->currentFrameNumber] = null;
$this->processingArguments[$this->currentFrameNumber] = null;
return $this;
}
/**
* {@inheritdoc}
*
* @see AnimationFactoryInterface::image()
*/
public function image(DriverInterface $driver): ImageInterface
{
if (count($this->sources) === 0) {
return $driver->createImage($this->width, $this->height);
}
$frames = array_map(
$this->buildFrame(...),
array_fill(0, count($this->sources), $driver),
$this->sources,
$this->delays,
$this->processingCalls,
$this->processingArguments,
);
return new Image($driver, $driver->createCore($frames));
}
/**
* Build frame from given image source and delay.
*
* @param null|array<mixed> $processingArguments
*/
private function buildFrame(
DriverInterface $driver,
mixed $source,
float $delay,
?string $processingCall = null,
?array $processingArguments = null,
): FrameInterface {
try {
// try to decode image source
$image = $driver->decodeImage($source);
} catch (DecoderException | FilesystemException) {
// create empty image with colored background
$image = $driver->createImage($this->width, $this->height)
->fill($driver->decodeColor($source));
}
// adjust size if necessary
if ($image->width() !== $this->width || $image->height() !== $this->height) {
$image->cover($this->width, $this->height);
}
// apply processing call if available
if ($processingCall !== null) {
call_user_func_array([$image, $processingCall], $processingArguments);
}
// return ready-made frame with all attributes
return $image
->core()
->first()
->setDelay($delay)
->setDisposalMethod(DisposalMethod::BACKGROUND->value);
}
/**
* Collect processing calls on frame images.
*
* @param array<null|array<mixed>> $arguments
* @throws Error
*/
public function __call(string $name, array $arguments): self
{
if (!method_exists(Image::class, $name)) {
throw new Error('Call to undefined method ' . Image::class . '::' . $name . '()');
}
$this->processingCalls[$this->currentFrameNumber] = $name;
$this->processingArguments[$this->currentFrameNumber] = $arguments;
return $this;
}
}