Skip to content

Commit

Permalink
Add percent values as input for ImageInterface::removeAnimation()
Browse files Browse the repository at this point in the history
  • Loading branch information
olivervogel committed Nov 4, 2023
1 parent 8f73dd8 commit a299100
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 34 deletions.
2 changes: 1 addition & 1 deletion src/Drivers/Abstract/AbstractImage.php
Expand Up @@ -374,7 +374,7 @@ public function crop(
);
}

public function removeAnimation(int $position = 0): ImageInterface
public function removeAnimation(int|string $position = 0): ImageInterface
{
return $this->modify(
$this->resolveDriverClass('Modifiers\RemoveAnimationModifier', $position)
Expand Down
29 changes: 29 additions & 0 deletions src/Drivers/Abstract/Modifiers/AbstractRemoveAnimationModifier.php
@@ -0,0 +1,29 @@
<?php

namespace Intervention\Image\Drivers\Abstract\Modifiers;

use Intervention\Image\Exceptions\InputException;
use Intervention\Image\Interfaces\FrameInterface;
use Intervention\Image\Interfaces\ModifierInterface;

abstract class AbstractRemoveAnimationModifier implements ModifierInterface
{
protected function chosenFrame($image, int|string $position): FrameInterface
{
if (is_int($position)) {
return $image->frame($position);
}

if (preg_match("/^(?P<percent>[0-9]{1,3})%$/", $position, $matches) != 1) {
throw new InputException(
'Input value of Image::removeAnimation() must be either integer or a percent value as string.'
);
}

$total = count($image);
$position = intval(round($total / 100 * intval($matches['percent'])));
$position = $position == $total ? $position - 1 : $position;

return $image->frame($position);
}
}
25 changes: 7 additions & 18 deletions src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php
Expand Up @@ -3,36 +3,25 @@
namespace Intervention\Image\Drivers\Gd\Modifiers;

use Intervention\Image\Collection;
use Intervention\Image\Drivers\Gd\Image;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRemoveAnimationModifier;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ModifierInterface;
use Intervention\Image\Traits\CanCheckType;
use Intervention\Image\Drivers\Gd\Image;

class RemoveAnimationModifier implements ModifierInterface
class RemoveAnimationModifier extends AbstractRemoveAnimationModifier
{
use CanCheckType;

public function __construct(protected int $position = 0)
public function __construct(protected int|string $position = 0)
{
//
}

public function apply(ImageInterface $image): ImageInterface
{
if (!$image->isAnimated()) {
throw new RuntimeException('Image is not animated.');
}

$image = $this->failIfNotClass($image, Image::class);

$frames = new Collection();
foreach ($image as $key => $frame) {
if ($this->position == $key) {
$frames->push($frame);
}
}

return $image->setFrames($frames);
return $image->setFrames(new Collection([
$this->chosenFrame($image, $this->position)
]));
}
}
18 changes: 5 additions & 13 deletions src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php
Expand Up @@ -3,36 +3,28 @@
namespace Intervention\Image\Drivers\Imagick\Modifiers;

use Imagick;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Drivers\Abstract\Modifiers\AbstractRemoveAnimationModifier;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ModifierInterface;
use Intervention\Image\Drivers\Imagick\Image;
use Intervention\Image\Traits\CanCheckType;

class RemoveAnimationModifier implements ModifierInterface
class RemoveAnimationModifier extends AbstractRemoveAnimationModifier
{
use CanCheckType;

public function __construct(protected int $position = 0)
public function __construct(protected int|string $position = 0)
{
//
}

public function apply(ImageInterface $image): ImageInterface
{
if (!$image->isAnimated()) {
throw new RuntimeException('Image is not animated.');
}

$image = $this->failIfNotClass($image, Image::class);

// create new imagick with just one image
$imagick = new Imagick();
foreach ($image->getImagick() as $key => $core) {
if ($key == $this->position) {
$imagick->addImage($core->getImage());
}
}
$frame = $this->chosenFrame($image, $this->position);
$imagick->addImage($frame->core()->getImage());

return $image->setImagick($imagick);
}
Expand Down
8 changes: 8 additions & 0 deletions src/Exceptions/InputException.php
@@ -0,0 +1,8 @@
<?php

namespace Intervention\Image\Exceptions;

class InputException extends \RuntimeException
{
//
}
4 changes: 2 additions & 2 deletions src/Interfaces/ImageInterface.php
Expand Up @@ -79,10 +79,10 @@ public function isAnimated(): bool;
/**
* Remove all frames but keep the one at the specified position
*
* @param int $position
* @param int|string $position
* @return ImageInterface
*/
public function removeAnimation(int $position = 0): ImageInterface;
public function removeAnimation(int|string $position = 0): ImageInterface;

/**
* Apply given modifier to current image
Expand Down
9 changes: 9 additions & 0 deletions tests/Drivers/Gd/Modifiers/RemoveAnimationModifierTest.php
Expand Up @@ -22,4 +22,13 @@ public function testApply(): void
$this->assertEquals(1, count($image));
$this->assertEquals(1, count($result));
}

public function testApplyPercent(): void
{
$image = $this->createTestImage('animation.gif');
$this->assertEquals(8, count($image));
$result = $image->modify(new RemoveAnimationModifier('20%'));
$this->assertEquals(1, count($image));
$this->assertEquals(1, count($result));
}
}
Expand Up @@ -22,4 +22,13 @@ public function testApply(): void
$this->assertEquals(1, count($image));
$this->assertEquals(1, count($result));
}

public function testApplyPercent(): void
{
$image = $this->createTestImage('animation.gif');
$this->assertEquals(8, count($image));
$result = $image->modify(new RemoveAnimationModifier('20%'));
$this->assertEquals(1, count($image));
$this->assertEquals(1, count($result));
}
}

0 comments on commit a299100

Please sign in to comment.