1
0
mirror of https://github.com/Intervention/image.git synced 2025-08-14 01:44:03 +02:00

Merge pull request #1277 from Intervention/feature/1253-add-transparency-param-for-place

Add opacity parameter to PlaceModifier
This commit is contained in:
Oliver Vogel
2024-01-20 09:50:30 +01:00
committed by GitHub
7 changed files with 122 additions and 15 deletions

View File

@@ -5,8 +5,10 @@ declare(strict_types=1);
namespace Intervention\Image\Drivers\Gd\Modifiers; namespace Intervention\Image\Drivers\Gd\Modifiers;
use Intervention\Image\Drivers\DriverSpecialized; use Intervention\Image\Drivers\DriverSpecialized;
use Intervention\Image\Interfaces\FrameInterface;
use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\ModifierInterface;
use Intervention\Image\Interfaces\PointInterface;
/** /**
* @method mixed getPosition(ImageInterface $image, ImageInterface $watermark) * @method mixed getPosition(ImageInterface $image, ImageInterface $watermark)
@@ -14,6 +16,7 @@ use Intervention\Image\Interfaces\ModifierInterface;
* @property string $position * @property string $position
* @property int $offset_x * @property int $offset_x
* @property int $offset_y * @property int $offset_y
* @property int $opacity
*/ */
class PlaceModifier extends DriverSpecialized implements ModifierInterface class PlaceModifier extends DriverSpecialized implements ModifierInterface
{ {
@@ -24,18 +27,93 @@ class PlaceModifier extends DriverSpecialized implements ModifierInterface
foreach ($image as $frame) { foreach ($image as $frame) {
imagealphablending($frame->native(), true); imagealphablending($frame->native(), true);
imagecopy(
$frame->native(), if ($this->opacity === 100) {
$watermark->core()->native(), $this->placeOpaque($frame, $watermark, $position);
$position->x(), } else {
$position->y(), $this->placeTransparent($frame, $watermark, $position);
0, }
0,
$watermark->width(),
$watermark->height()
);
} }
return $image; return $image;
} }
/**
* Insert watermark with 100% opacity
*
* @param FrameInterface $frame
* @param ImageInterface $watermark
* @param PointInterface $position
* @return void
*/
private function placeOpaque(FrameInterface $frame, ImageInterface $watermark, PointInterface $position): void
{
imagecopy(
$frame->native(),
$watermark->core()->native(),
$position->x(),
$position->y(),
0,
0,
$watermark->width(),
$watermark->height()
);
}
/**
* Insert watermark transparent with current opacity
*
* Unfortunately, the original PHP function imagecopymerge does not work reliably.
* For example, any transparency of the image to be inserted is not applied correctly.
* For this reason, a new GDImage is created into which the original image is inserted
* in the first step and the watermark is inserted with 100% opacity in the second
* step. This combination is then transferred to the original image again with the
* respective opacity.
*
* Please note: Unfortunately, there is still an edge case, when a transparent image
* is placed on a transparent background, the "double" transparent areas appear opaque!
*
* @param FrameInterface $frame
* @param ImageInterface $watermark
* @param PointInterface $position
* @return void
*/
private function placeTransparent(FrameInterface $frame, ImageInterface $watermark, PointInterface $position): void
{
$cut = imagecreatetruecolor($watermark->width(), $watermark->height());
imagecopy(
$cut,
$frame->native(),
0,
0,
$position->x(),
$position->y(),
imagesx($cut),
imagesy($cut)
);
imagecopy(
$cut,
$watermark->core()->native(),
0,
0,
0,
0,
imagesx($cut),
imagesy($cut)
);
imagecopymerge(
$frame->native(),
$cut,
$position->x(),
$position->y(),
0,
0,
$watermark->width(),
$watermark->height(),
$this->opacity
);
}
} }

View File

@@ -15,6 +15,7 @@ use Intervention\Image\Interfaces\ModifierInterface;
* @property string $position * @property string $position
* @property int $offset_x * @property int $offset_x
* @property int $offset_y * @property int $offset_y
* @property int $opacity
*/ */
class PlaceModifier extends DriverSpecialized implements ModifierInterface class PlaceModifier extends DriverSpecialized implements ModifierInterface
{ {
@@ -23,6 +24,15 @@ class PlaceModifier extends DriverSpecialized implements ModifierInterface
$watermark = $this->driver()->handleInput($this->element); $watermark = $this->driver()->handleInput($this->element);
$position = $this->getPosition($image, $watermark); $position = $this->getPosition($image, $watermark);
// set opacity of watermark
if ($this->opacity < 100) {
$watermark->core()->native()->evaluateImage(
Imagick::EVALUATE_DIVIDE,
$this->opacity > 0 ? (100 / $this->opacity) : 1000,
Imagick::CHANNEL_ALPHA,
);
}
foreach ($image as $frame) { foreach ($image as $frame) {
$frame->native()->compositeImage( $frame->native()->compositeImage(
$watermark->core()->native(), $watermark->core()->native(),

View File

@@ -744,9 +744,10 @@ final class Image implements ImageInterface
mixed $element, mixed $element,
string $position = 'top-left', string $position = 'top-left',
int $offset_x = 0, int $offset_x = 0,
int $offset_y = 0 int $offset_y = 0,
int $opacity = 100
): ImageInterface { ): ImageInterface {
return $this->modify(new PlaceModifier($element, $position, $offset_x, $offset_y)); return $this->modify(new PlaceModifier($element, $position, $offset_x, $offset_y, $opacity));
} }
/** /**

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Intervention\Image\Interfaces; namespace Intervention\Image\Interfaces;
use Countable; use Countable;
use Intervention\Image\EncodedImage;
use Intervention\Image\Origin; use Intervention\Image\Origin;
use IteratorAggregate; use IteratorAggregate;
@@ -522,13 +521,15 @@ interface ImageInterface extends IteratorAggregate, Countable
* @param string $position * @param string $position
* @param int $offset_x * @param int $offset_x
* @param int $offset_y * @param int $offset_y
* @param int $opacity
* @return ImageInterface * @return ImageInterface
*/ */
public function place( public function place(
mixed $element, mixed $element,
string $position = 'top-left', string $position = 'top-left',
int $offset_x = 0, int $offset_x = 0,
int $offset_y = 0 int $offset_y = 0,
int $opacity = 100
): ImageInterface; ): ImageInterface;
/** /**

View File

@@ -13,7 +13,8 @@ class PlaceModifier extends SpecializableModifier
public mixed $element, public mixed $element,
public string $position, public string $position,
public int $offset_x, public int $offset_x,
public int $offset_y public int $offset_y,
public int $opacity = 100
) { ) {
} }

View File

@@ -24,4 +24,12 @@ class PlaceModifierTest extends TestCase
$image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0));
$this->assertEquals('32250d', $image->pickColor(300, 25)->toHex()); $this->assertEquals('32250d', $image->pickColor(300, 25)->toHex());
} }
public function testColorChangeOpacity(): void
{
$image = $this->readTestImage('test.jpg');
$this->assertEquals('febc44', $image->pickColor(300, 25)->toHex());
$image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0, 50));
$this->assertEquals('987028', $image->pickColor(300, 25)->toHex());
}
} }

View File

@@ -24,4 +24,12 @@ class PlaceModifierTest extends TestCase
$image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0)); $image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0));
$this->assertEquals('33260e', $image->pickColor(300, 25)->toHex()); $this->assertEquals('33260e', $image->pickColor(300, 25)->toHex());
} }
public function testColorChangeOpacity(): void
{
$image = $this->readTestImage('test.jpg');
$this->assertEquals('febc44', $image->pickColor(300, 25)->toHex());
$image->modify(new PlaceModifier(__DIR__ . '/../../../images/circle.png', 'top-right', 0, 0, 50));
$this->assertEquals('987129', $image->pickColor(300, 25)->toHex());
}
} }