Skip to content

Commit

Permalink
Implement Image::sliceAnimation()
Browse files Browse the repository at this point in the history
  • Loading branch information
olivervogel committed Jan 4, 2024
1 parent ceacb64 commit 80af8ce
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 3 deletions.
21 changes: 21 additions & 0 deletions src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,31 @@ public function pushEach(array $data, ?callable $callback = null): CollectionInt
return $this;
}

/**
* {@inheritdoc}
*
* @see CollectionInterface::empty()
*/
public function empty(): CollectionInterface
{
$this->items = [];

return $this;
}

/**
* {@inheritdoc}
*
* @see CollectionInterface::slice()
*/
public function slice(int $offset, ?int $length = null): CollectionInterface
{
if ($offset >= count($this->items)) {
throw new RuntimeException('Offset exceeds the maximum value.');
}

$this->items = array_slice($this->items, $offset, $length);

return $this;
}
}
20 changes: 20 additions & 0 deletions src/Drivers/Gd/Modifiers/SliceAnimationModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Intervention\Image\Drivers\Gd\Modifiers;

use Intervention\Image\Drivers\DriverSpecializedModifier;
use Intervention\Image\Interfaces\ImageInterface;

/**
* @property int $offset
* @property null|int $length
*/
class SliceAnimationModifier extends DriverSpecializedModifier
{
public function apply(ImageInterface $image): ImageInterface
{
$image->core()->slice($this->offset, $this->length);

return $image;
}
}
87 changes: 87 additions & 0 deletions src/Drivers/Imagick/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Iterator;
use Intervention\Image\Interfaces\CoreInterface;
use Intervention\Image\Exceptions\AnimationException;
use Intervention\Image\Interfaces\CollectionInterface;
use Intervention\Image\Interfaces\FrameInterface;

class Core implements CoreInterface, Iterator
Expand All @@ -17,6 +18,92 @@ 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;
}

foreach ($this->imagick as $key => $native) {
if (!in_array($key, $allowed_indexes)) {
$native->removeImage();
}
}

return $this;
}

public function add(FrameInterface $frame): CoreInterface
{
$imagick = $frame->native();
Expand Down
20 changes: 20 additions & 0 deletions src/Drivers/Imagick/Modifiers/SliceAnimationModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Intervention\Image\Drivers\Imagick\Modifiers;

use Intervention\Image\Drivers\DriverSpecializedModifier;
use Intervention\Image\Interfaces\ImageInterface;

/**
* @property int $offset
* @property null|int $length
*/
class SliceAnimationModifier extends DriverSpecializedModifier
{
public function apply(ImageInterface $image): ImageInterface
{
$image->core()->slice($this->offset, $this->length);

return $image;
}
}
6 changes: 6 additions & 0 deletions src/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
use Intervention\Image\Modifiers\ScaleDownModifier;
use Intervention\Image\Modifiers\ScaleModifier;
use Intervention\Image\Modifiers\SharpenModifier;
use Intervention\Image\Modifiers\SliceAnimationModifier;
use Intervention\Image\Modifiers\TextModifier;
use Intervention\Image\Typography\FontFactory;

Expand Down Expand Up @@ -190,6 +191,11 @@ public function removeAnimation(int|string $position = 0): ImageInterface
return $this->modify(new RemoveAnimationModifier($position));
}

public function sliceAnimation(int $offset = 0, ?int $length = null): ImageInterface
{
return $this->modify(new SliceAnimationModifier($offset, $length));
}

/**
* {@inheritdoc}
*
Expand Down
9 changes: 9 additions & 0 deletions src/Interfaces/CollectionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ public function count(): int;
* @return CollectionInterface
*/
public function empty(): CollectionInterface;

/**
* Keep a slice of items and remove the rest from the collection
*
* @param int $offset
* @param null|int $length
* @return CollectionInterface
*/
public function slice(int $offset, ?int $length = 0): CollectionInterface;
}
9 changes: 6 additions & 3 deletions src/Interfaces/CoreInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

namespace Intervention\Image\Interfaces;

use Traversable;

interface CoreInterface extends Traversable
interface CoreInterface extends CollectionInterface
{
/**
* return driver's representation of the image core.
Expand Down Expand Up @@ -60,5 +58,10 @@ public function loops(): int;
*/
public function setLoops(int $loops): CoreInterface;

/**
* Get first frame in core
*
* @return FrameInterface
*/
public function first(): FrameInterface;
}
9 changes: 9 additions & 0 deletions src/Interfaces/ImageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ public function isAnimated(): bool;
*/
public function removeAnimation(int|string $position = 0): ImageInterface;

/**
* Keep a slice of the animated frame and remove the rest
*
* @param int $offset
* @param null|int $length
* @return ImageInterface
*/
public function sliceAnimation(int $offset = 0, ?int $length = null): ImageInterface;

/**
* Return loop count of animated image
*
Expand Down
10 changes: 10 additions & 0 deletions src/Modifiers/SliceAnimationModifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Intervention\Image\Modifiers;

class SliceAnimationModifier extends AbstractModifier
{
public function __construct(public int $offset = 0, public ?int $length = null)
{
}
}
25 changes: 25 additions & 0 deletions tests/CollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Intervention\Image\Tests;

use Intervention\Image\Collection;
use Intervention\Image\Exceptions\RuntimeException;

/**
* @covers \Intervention\Image\Collection
Expand Down Expand Up @@ -144,4 +145,28 @@ public function testEmpty(): void
$this->assertEquals(0, $collection->count());
$this->assertEquals(0, $result->count());
}

public function testSlice(): void
{
$collection = new Collection(['a', 'b', 'c', 'd', 'e', 'f']);
$this->assertEquals(6, $collection->count());
$result = $collection->slice(0, 3);
$this->assertEquals(['a', 'b', 'c'], $collection->toArray());
$this->assertEquals(['a', 'b', 'c'], $result->toArray());
$this->assertEquals('a', $result->get(0));
$this->assertEquals('b', $result->get(1));
$this->assertEquals('c', $result->get(2));

$result = $collection->slice(2, 1);
$this->assertEquals(['c'], $collection->toArray());
$this->assertEquals(['c'], $result->toArray());
$this->assertEquals('c', $result->get(0));
}

public function testSliceOutOfBounds(): void
{
$this->expectException(RuntimeException::class);
$collection = new Collection(['a', 'b', 'c']);
$collection->slice(6);
}
}
73 changes: 73 additions & 0 deletions tests/Drivers/Imagick/CoreTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,77 @@ public function testSetGetLoops(): void
$this->assertEquals(12, $core->loops());
$this->assertInstanceOf(Core::class, $result);
}

public function testHas(): void
{
$imagick = new Imagick();
$imagick->newImage(100, 100, new ImagickPixel('red'));
$imagick->addImage(clone $imagick);
$core = new Core($imagick);
$this->assertTrue($core->has(0));
$this->assertTrue($core->has(1));
$this->assertFalse($core->has(2));
}

public function testPush(): void
{
$imagick = new Imagick();
$imagick->newImage(100, 100, new ImagickPixel('red'));
$imagick->addImage(clone $imagick);
$core = new Core($imagick);
$this->assertEquals(2, $core->count());

$im = new Imagick();
$im->newImage(100, 100, new ImagickPixel('green'));
$result = $core->push(new Frame($im));
$this->assertEquals(3, $core->count());
$this->assertEquals(3, $result->count());
}

public function testGet(): void
{
$imagick = new Imagick();
$imagick->newImage(100, 100, new ImagickPixel('red'));
$imagick->addImage(clone $imagick);
$core = new Core($imagick);
$this->assertInstanceOf(Frame::class, $core->get(0));
$this->assertInstanceOf(Frame::class, $core->get(1));
$this->assertNull($core->get(2));
$this->assertEquals('foo', $core->get(2, 'foo'));
}

public function testEmpty(): void
{
$imagick = new Imagick();
$imagick->newImage(100, 100, new ImagickPixel('red'));
$imagick->addImage(clone $imagick);
$core = new Core($imagick);
$this->assertEquals(2, $core->count());
$result = $core->empty();
$this->assertEquals(0, $core->count());
$this->assertEquals(0, $result->count());
}

public function testSlice(): void
{
$imagick = new Imagick();

$im = new Imagick();
$im->newImage(10, 10, new ImagickPixel('red'));
$imagick->addImage($im);

$im = new Imagick();
$im->newImage(10, 10, new ImagickPixel('green'));
$imagick->addImage($im);

$im = new Imagick();
$im->newImage(10, 10, new ImagickPixel('blue'));
$imagick->addImage($im);

$core = new Core($imagick);
$this->assertEquals(3, $core->count());
$result = $core->slice(1, 2);
$this->assertEquals(2, $core->count());
$this->assertEquals(2, $result->count());
}
}

0 comments on commit 80af8ce

Please sign in to comment.