1
0
mirror of https://github.com/Intervention/image.git synced 2025-03-15 22:49:40 +01:00

Add ResizeCanvasModifiers

This commit is contained in:
Oliver Vogel 2023-12-06 16:11:53 +01:00
parent 8c7fb0cb46
commit 1e0de6501a
7 changed files with 292 additions and 0 deletions

View File

@ -0,0 +1,85 @@
<?php
namespace Intervention\Image\Drivers\Gd\Modifiers;
use Intervention\Image\Colors\Rgb\Channels\Blue;
use Intervention\Image\Colors\Rgb\Channels\Green;
use Intervention\Image\Colors\Rgb\Channels\Red;
use Intervention\Image\Drivers\DriverSpecializedModifier;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\FrameInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SizeInterface;
use Intervention\Image\Modifiers\FillModifier;
/**
* @method SizeInterface cropSize(ImageInterface $image)
* @property mixed $background
*/
class ResizeCanvasModifier extends DriverSpecializedModifier
{
public function apply(ImageInterface $image): ImageInterface
{
$resize = $this->cropSize($image);
$background = $this->driver()->handleInput($this->background);
foreach ($image as $frame) {
$this->modify($frame, $resize, $background);
}
return $image;
}
protected function modify(
FrameInterface $frame,
SizeInterface $resize,
ColorInterface $background,
): void {
// create new gd image
$modified = $this->driver()->createImage(
$resize->width(),
$resize->height()
)->modify(
new FillModifier($background)
)->core()->native();
// make image area transparent to keep transparency
// even if background-color is set
$transparent = imagecolorallocatealpha(
$modified,
$background->channel(Red::class)->value(),
$background->channel(Green::class)->value(),
$background->channel(Blue::class)->value(),
127,
);
imagealphablending($modified, false); // do not blend / just overwrite
imagecolortransparent($modified, $transparent);
imagefilledrectangle(
$modified,
$resize->pivot()->x() * -1,
$resize->pivot()->y() * -1,
$resize->pivot()->x() * -1 + $frame->size()->width() - 1,
$resize->pivot()->y() * -1 + $frame->size()->height() - 1,
$transparent
);
// copy image from original with blending alpha
imagealphablending($modified, true);
imagecopyresampled(
$modified,
$frame->native(),
$resize->pivot()->x() * -1,
$resize->pivot()->y() * -1,
0,
0,
$frame->size()->width(),
$frame->size()->height(),
$frame->size()->width(),
$frame->size()->height()
);
// set new content as recource
$frame->setNative($modified);
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace Intervention\Image\Drivers\Imagick\Modifiers;
use ImagickDraw;
use Intervention\Image\Drivers\DriverSpecializedModifier;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SizeInterface;
/**
* @method SizeInterface cropSize(ImageInterface $image)
* @property mixed $background
*/
class ResizeCanvasModifier extends DriverSpecializedModifier
{
public function apply(ImageInterface $image): ImageInterface
{
$size = $image->size();
$resize = $this->cropSize($image);
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->background)
);
foreach ($image as $frame) {
$frame->native()->extentImage(
$resize->width(),
$resize->height(),
$resize->pivot()->x(),
$resize->pivot()->y()
);
if ($resize->width() > $size->width()) {
// fill new emerged background
$draw = new ImagickDraw();
$draw->setFillColor($background);
$delta = abs($resize->pivot()->x());
if ($delta > 0) {
$draw->rectangle(
0,
0,
$delta - 1,
$resize->height()
);
}
$draw->rectangle(
$size->width() + $delta,
0,
$resize->width(),
$resize->height()
);
$frame->native()->drawImage($draw);
}
if ($resize->height() > $size->height()) {
// fill new emerged background
$draw = new ImagickDraw();
$draw->setFillColor($background);
$delta = abs($resize->pivot()->y());
if ($delta > 0) {
$draw->rectangle(
0,
0,
$resize->width(),
$delta - 1
);
}
$draw->rectangle(
0,
$size->height() + $delta,
$resize->width(),
$resize->height()
);
$frame->native()->drawImage($draw);
}
}
return $image;
}
}

View File

@ -64,6 +64,7 @@ use Intervention\Image\Modifiers\PlaceModifier;
use Intervention\Image\Modifiers\ProfileModifier;
use Intervention\Image\Modifiers\ProfileRemovalModifier;
use Intervention\Image\Modifiers\RemoveAnimationModifier;
use Intervention\Image\Modifiers\ResizeCanvasModifier;
use Intervention\Image\Modifiers\ResizeDownModifier;
use Intervention\Image\Modifiers\ResizeModifier;
use Intervention\Image\Modifiers\ResolutionModifier;
@ -521,6 +522,20 @@ final class Image implements ImageInterface, Countable
return $this->modify(new CoverDownModifier($width, $height, $position));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::resizeCanvas()
*/
public function resizeCanvas(
int $width,
int $height,
mixed $background = 'ffffff',
string $position = 'center'
): ImageInterface {
return $this->modify(new ResizeCanvasModifier($width, $height, $background, $position));
}
/**
* {@inheritdoc}
*

View File

@ -347,6 +347,25 @@ interface ImageInterface extends IteratorAggregate, Countable
*/
public function coverDown(int $width, int $height, string $position = 'center'): ImageInterface;
/**
* Resize the boundaries of the current image to given width and height.
* An anchor position can be defined to determine from what point of the
* image the resizing is going to happen. A background color can be passed
* to define the color of the new emerging areas.
*
* @param int $width
* @param int $height
* @param string $position
* @param mixed $background
* @return ImageInterface
*/
public function resizeCanvas(
int $width,
int $height,
mixed $background = 'ffffff',
string $position = 'center'
): ImageInterface;
/**
* Padded resizing means that the original image is scaled until it fits the
* defined target size with unchanged aspect ratio. The original image is

View File

@ -0,0 +1,24 @@
<?php
namespace Intervention\Image\Modifiers;
use Intervention\Image\Geometry\Rectangle;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SizeInterface;
class ResizeCanvasModifier extends AbstractModifier
{
public function __construct(
public int $width,
public int $height,
public mixed $background = 'ffffff',
public string $position = 'center'
) {
}
public function cropSize(ImageInterface $image): SizeInterface
{
return (new Rectangle($this->width, $this->height))
->alignPivotTo($image->size(), $this->position);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Intervention\Image\Tests\Drivers\Gd\Modifiers;
use Intervention\Image\Modifiers\ResizeCanvasModifier;
use Intervention\Image\Tests\TestCase;
use Intervention\Image\Tests\Traits\CanCreateGdTestImage;
/**
* @requires extension gd
* @covers \Intervention\Image\Modifiers\ResizeCanvasModifier
*/
class ResizeCanvasModifierTest extends TestCase
{
use CanCreateGdTestImage;
public function testModify(): void
{
$image = $this->createTestImage('tile.png');
$this->assertEquals(16, $image->width());
$this->assertEquals(16, $image->height());
$image->modify(new ResizeCanvasModifier(18, 18, 'ff0', 'center'));
$this->assertEquals(18, $image->width());
$this->assertEquals(18, $image->height());
$this->assertColor(255, 255, 0, 255, $image->pickColor(0, 0));
$this->assertColor(180, 224, 0, 255, $image->pickColor(1, 1));
$this->assertColor(180, 224, 0, 255, $image->pickColor(2, 2));
$this->assertColor(255, 255, 0, 255, $image->pickColor(17, 17));
$this->assertTransparency($image->pickColor(12, 1));
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Intervention\Image\Tests\Drivers\Imagick\Modifiers;
use Intervention\Image\Modifiers\ResizeCanvasModifier;
use Intervention\Image\Tests\TestCase;
use Intervention\Image\Tests\Traits\CanCreateImagickTestImage;
/**
* @requires extension gd
* @covers \Intervention\Image\Modifiers\ResizeCanvasModifier
*/
class ResizeCanvasModifierTest extends TestCase
{
use CanCreateImagickTestImage;
public function testModify(): void
{
$image = $this->createTestImage('tile.png');
$this->assertEquals(16, $image->width());
$this->assertEquals(16, $image->height());
$image->modify(new ResizeCanvasModifier(18, 18, 'ff0', 'center'));
$this->assertEquals(18, $image->width());
$this->assertEquals(18, $image->height());
$this->assertColor(255, 255, 0, 255, $image->pickColor(0, 0));
$this->assertColor(180, 224, 0, 255, $image->pickColor(1, 1));
$this->assertColor(180, 224, 0, 255, $image->pickColor(2, 2));
$this->assertColor(255, 255, 0, 255, $image->pickColor(17, 17));
$this->assertTransparency($image->pickColor(12, 1));
}
}