<?php
declare(strict_types=1);
namespace Intervention\Image\Drivers\Imagick;
use Imagick;
use ImagickException;
use Iterator;
use Intervention\Image\Interfaces\CoreInterface;
use Intervention\Image\Exceptions\AnimationException;
use Intervention\Image\Interfaces\CollectionInterface;
use Intervention\Image\Interfaces\FrameInterface;
/**
* @implements Iterator<FrameInterface>
*/
class Core implements CoreInterface, Iterator
{
protected int $iteratorIndex = 0;
/**
* Create new core instance
*
* @param Imagick $imagick
* @return void
*/
public function __construct(protected Imagick $imagick)
{
}
/**
* {@inheritdoc}
*
* @see CollectionInterface::has()
*/
public function has(int|string $key): bool
{
try {
$result = $this->imagick->setIteratorIndex($key);
} catch (ImagickException) {
return false;
}
return $result;
}
/**
* {@inheritdoc}
*
* @see CollectionInterface::push()
*/
public function push($item): CollectionInterface
{
return $this->add($item);
}
/**
* {@inheritdoc}
*
* @see CollectionInterface::get()
*/
public function get(int|string $key, $default = null): mixed
{
try {
$this->imagick->setIteratorIndex($key);
} catch (ImagickException) {
return $default;
}
return new Frame($this->imagick->current());
}
/**
* {@inheritdoc}
*
* @see CollectionInterface::getAtPosition()
*/
public function getAtPosition(int $key = 0, $default = null): mixed
{
return $this->get($key, $default);
}
/**
* {@inheritdoc}
*
* @see CollectionInterface::empty()
*/
public function empty(): CollectionInterface
{
$this->imagick->clear();
return $this;
}
/**
* {@inheritdoc}
*
* @see CollectionInterface::slice()
*/
public function slice(int $offset, ?int $length = null): CollectionInterface
{
$allowed_indexes = [];
$length = is_null($length) ? $this->count() : $length;
for ($i = $offset; $i < $offset + $length; $i++) {
$allowed_indexes[] = $i;
}
$sliced = new Imagick();
foreach ($this->imagick as $key => $native) {
if (in_array($key, $allowed_indexes)) {
$sliced->addImage($native->getImage());
}
}
$sliced = $sliced->coalesceImages();
$sliced->setImageIterations($this->imagick->getImageIterations());
$this->imagick = $sliced;
return $this;
}
/**
* {@inheritdoc}
*
* @see CoreInterface::add()
*/
public function add(FrameInterface $frame): CoreInterface
{
$imagick = $frame->native();
$imagick->setImageDelay(
(int) round($frame->delay() * 100)
);
$imagick->setImageDispose($frame->dispose());
$size = $frame->size();
$imagick->setImagePage(
$size->width(),
$size->height(),
$frame->offsetLeft(),
$frame->offsetTop()
);
$this->imagick->addImage($imagick);
return $this;
}
/**
* {@inheritdoc}
*
* @see CoreInterface::count()
*/
public function count(): int
{
return $this->imagick->getNumberImages();
}
/**
* {@inheritdoc}
*
* @see Iterator::rewind()
*/
public function current(): mixed
{
$this->imagick->setIteratorIndex($this->iteratorIndex);
return new Frame($this->imagick->current());
}
/**
* {@inheritdoc}
*
* @see Iterator::rewind()
*/
public function next(): void
{
$this->iteratorIndex += 1;
}
/**
* {@inheritdoc}
*
* @see Iterator::rewind()
*/
public function key(): mixed
{
return $this->iteratorIndex;
}
/**
* {@inheritdoc}
*
* @see Iterator::rewind()
*/
public function valid(): bool
{
try {
$result = $this->imagick->setIteratorIndex($this->iteratorIndex);
} catch (ImagickException) {
return false;
}
return $result;
}
/**
* {@inheritdoc}
*
* @see Iterator::rewind()
*/
public function rewind(): void
{
$this->iteratorIndex = 0;
}
/**
* {@inheritdoc}
*
* @see CoreInterface::native()
*/
public function native(): mixed
{
return $this->imagick;
}
/**
* {@inheritdoc}
*
* @see CoreInterface::setNative()
*/
public function setNative(mixed $native): CoreInterface
{
$this->imagick = $native;
return $this;
}
/**
* {@inheritdoc}
*
* @see CoreInterface::frame()
*/
public function frame(int $position): FrameInterface
{
foreach ($this->imagick as $core) {
if ($core->getIteratorIndex() === $position) {
return new Frame($core);
}
}
throw new AnimationException('Frame #' . $position . ' could not be found in the image.');
}
/**
* {@inheritdoc}
*
* @see CoreInterface::loops()
*/
public function loops(): int
{
return $this->imagick->getImageIterations();
}
/**
* {@inheritdoc}
*
* @see CoreInterface::setLoops()
*/
public function setLoops(int $loops): CoreInterface
{
$this->imagick = $this->imagick->coalesceImages();
$this->imagick->setImageIterations($loops);
return $this;
}
/**
* {@inheritdoc}
*
* @see CollectionInterface::first()
*/
public function first(): FrameInterface
{
return $this->frame(0);
}
/**
* {@inheritdoc}
*
* @see CollectableInterface::last()
*/
public function last(): FrameInterface
{
return $this->frame($this->count() - 1);
}
/**
* {@inheritdoc}
*
* @see CollectionInterface::toArray()
*/
public function toArray(): array
{
$frames = [];
foreach ($this as $frame) {
$frames[] = $frame;
}
return $frames;
}
/**
* Clone instance
*
* @return void
*/
public function __clone(): void
{
$this->imagick = clone $this->imagick;
}
}