1
0
mirror of https://github.com/Intervention/image.git synced 2025-08-26 23:35:12 +02:00

Add background color parameter to CropModifier

This commit is contained in:
Oliver Vogel
2024-01-08 12:13:18 +01:00
parent 4ea59c8eaa
commit a0337c2dc4
7 changed files with 122 additions and 7 deletions

View File

@@ -2,8 +2,12 @@
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\Gd\Cloner;
use Intervention\Image\Drivers\Gd\SpecializedModifier;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\FrameInterface;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SizeInterface;
@@ -12,6 +16,7 @@ use Intervention\Image\Interfaces\SizeInterface;
* @method SizeInterface crop(ImageInterface $image)
* @property int $offset_x
* @property int $offset_y
* @property mixed $background
*/
class CropModifier extends SpecializedModifier
{
@@ -19,18 +24,23 @@ class CropModifier extends SpecializedModifier
{
$originalSize = $image->size();
$crop = $this->crop($image);
$background = $this->driver()->handleInput($this->background);
foreach ($image as $frame) {
$this->cropFrame($frame, $originalSize, $crop);
$this->cropFrame($frame, $originalSize, $crop, $background);
}
return $image;
}
protected function cropFrame(FrameInterface $frame, SizeInterface $originalSize, SizeInterface $resizeTo): void
{
protected function cropFrame(
FrameInterface $frame,
SizeInterface $originalSize,
SizeInterface $resizeTo,
ColorInterface $background
): void {
// create new image
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo);
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo, $background);
// define offset
$offset_x = ($resizeTo->pivot()->x() + $this->offset_x);
@@ -42,6 +52,27 @@ class CropModifier extends SpecializedModifier
$targetWidth = $targetWidth < $originalSize->width() ? $targetWidth + $offset_x : $targetWidth;
$targetHeight = $targetHeight < $originalSize->height() ? $targetHeight + $offset_y : $targetHeight;
// 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,
$offset_x * -1,
$offset_y * -1,
$targetWidth,
$targetHeight,
$transparent
);
// copy content from resource
imagecopyresampled(
$modified,
@@ -56,6 +87,8 @@ class CropModifier extends SpecializedModifier
$targetHeight
);
imagealphablending($modified, true);
// set new content as recource
$frame->setNative($modified);
}

View File

@@ -2,6 +2,7 @@
namespace Intervention\Image\Drivers\Imagick\Modifiers;
use ImagickDraw;
use Intervention\Image\Drivers\DriverSpecializedModifier;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SizeInterface;
@@ -10,20 +11,73 @@ use Intervention\Image\Interfaces\SizeInterface;
* @method SizeInterface crop(ImageInterface $image)
* @property int $offset_x
* @property int $offset_y
* @property mixed $background
*/
class CropModifier extends DriverSpecializedModifier
{
public function apply(ImageInterface $image): ImageInterface
{
$originalSize = $image->size();
$crop = $this->crop($image);
$background = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->background)
);
$draw = new ImagickDraw();
$draw->setFillColor($background);
foreach ($image as $frame) {
// crop image
$frame->native()->extentImage(
$crop->width(),
$crop->height(),
$crop->pivot()->x() + $this->offset_x,
$crop->pivot()->y() + $this->offset_y
);
// cover the possible newly created areas with background color
if ($crop->width() > $originalSize->width()) {
$draw->rectangle(
$originalSize->width() + ($this->offset_x * -1),
0,
$crop->width(),
$crop->height()
);
$frame->native()->drawImage($draw);
}
// cover the possible newly created areas with background color
if ($crop->height() > $originalSize->height()) {
$draw->rectangle(
0,
$originalSize->height() + ($this->offset_y * -1),
$crop->width(),
$crop->height()
);
$frame->native()->drawImage($draw);
}
// cover the possible newly created areas with background color
if ($this->offset_x < 0) {
$draw->rectangle(
0,
0,
($this->offset_x * -1) - 1,
$originalSize->height() + ($this->offset_y * -1)
);
$frame->native()->drawImage($draw);
}
// cover the possible newly created areas with background color
if ($this->offset_y < 0) {
$draw->rectangle(
0,
0,
$crop->width(),
($this->offset_y * -1) - 1,
);
$frame->native()->drawImage($draw);
}
}
return $image;

View File

@@ -715,9 +715,10 @@ final class Image implements ImageInterface
int $height,
int $offset_x = 0,
int $offset_y = 0,
mixed $background = 'ffffff',
string $position = 'top-left'
): ImageInterface {
return $this->modify(new CropModifier($width, $height, $offset_x, $offset_y, $position));
return $this->modify(new CropModifier($width, $height, $offset_x, $offset_y, $background, $position));
}
/**

View File

@@ -491,6 +491,7 @@ interface ImageInterface extends IteratorAggregate, Countable
* @param int $height
* @param int $offset_x
* @param int $offset_y
* @param mixed $background
* @param string $position
* @return ImageInterface
*/
@@ -499,6 +500,7 @@ interface ImageInterface extends IteratorAggregate, Countable
int $height,
int $offset_x = 0,
int $offset_y = 0,
mixed $background = 'ffffff',
string $position = 'top-left'
): ImageInterface;

View File

@@ -13,6 +13,7 @@ class CropModifier extends AbstractModifier
public int $height,
public int $offset_x = 0,
public int $offset_y = 0,
public mixed $background = 'f00',
public string $position = 'top-left'
) {
}

View File

@@ -17,11 +17,23 @@ class CropModifierTest extends TestCase
public function testModify(): void
{
$image = $this->readTestImage('blocks.png');
$image = $image->modify(new CropModifier(200, 200, 0, 0, 'bottom-right'));
$image = $image->modify(new CropModifier(200, 200, 0, 0, 'ffffff', 'bottom-right'));
$this->assertEquals(200, $image->width());
$this->assertEquals(200, $image->height());
$this->assertColor(255, 0, 0, 255, $image->pickColor(5, 5));
$this->assertColor(255, 0, 0, 255, $image->pickColor(100, 100));
$this->assertColor(255, 0, 0, 255, $image->pickColor(190, 190));
}
public function testModifyExtend(): void
{
$image = $this->readTestImage('blocks.png');
$image = $image->modify(new CropModifier(800, 100, -10, -10, 'ff0000', 'top-left'));
$this->assertEquals(800, $image->width());
$this->assertEquals(100, $image->height());
$this->assertColor(255, 0, 0, 255, $image->pickColor(9, 9));
$this->assertColor(0, 0, 255, 255, $image->pickColor(16, 16));
$this->assertColor(0, 0, 255, 255, $image->pickColor(445, 16));
$this->assertTransparency($image->pickColor(460, 16));
}
}

View File

@@ -17,11 +17,23 @@ class CropModifierTest extends TestCase
public function testModify(): void
{
$image = $this->readTestImage('blocks.png');
$image = $image->modify(new CropModifier(200, 200, 0, 0, 'bottom-right'));
$image = $image->modify(new CropModifier(200, 200, 0, 0, 'ffffff', 'bottom-right'));
$this->assertEquals(200, $image->width());
$this->assertEquals(200, $image->height());
$this->assertColor(255, 0, 0, 255, $image->pickColor(5, 5));
$this->assertColor(255, 0, 0, 255, $image->pickColor(100, 100));
$this->assertColor(255, 0, 0, 255, $image->pickColor(190, 190));
}
public function testModifyExtend(): void
{
$image = $this->readTestImage('blocks.png');
$image = $image->modify(new CropModifier(800, 100, -10, -10, 'ff0000', 'top-left'));
$this->assertEquals(800, $image->width());
$this->assertEquals(100, $image->height());
$this->assertColor(255, 0, 0, 255, $image->pickColor(9, 9));
$this->assertColor(0, 0, 255, 255, $image->pickColor(16, 16));
$this->assertColor(0, 0, 255, 255, $image->pickColor(445, 16));
$this->assertTransparency($image->pickColor(460, 16));
}
}