mirror of
https://github.com/Intervention/image.git
synced 2025-08-31 01:29:51 +02:00
Merge branch 'feature/blending-color' into next
This commit is contained in:
@@ -18,6 +18,6 @@ class TransparentColorDecoder extends HexColorDecoder
|
||||
throw new DecoderException('Unable to decode input');
|
||||
}
|
||||
|
||||
return parent::decode('#ff00ff00');
|
||||
return parent::decode('#ffffff00');
|
||||
}
|
||||
}
|
||||
|
89
src/Drivers/Gd/Cloner.php
Normal file
89
src/Drivers/Gd/Cloner.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd;
|
||||
|
||||
use GdImage;
|
||||
use Intervention\Image\Colors\Rgb\Color;
|
||||
use Intervention\Image\Geometry\Rectangle;
|
||||
use Intervention\Image\Interfaces\ColorInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
|
||||
class Cloner
|
||||
{
|
||||
/**
|
||||
* Create a clone of the given GdImage
|
||||
*
|
||||
* @param GdImage $gd
|
||||
* @return GdImage
|
||||
*/
|
||||
public static function clone(GdImage $gd): GdImage
|
||||
{
|
||||
// create empty canvas with same size
|
||||
$clone = static::cloneEmpty($gd);
|
||||
|
||||
// transfer actual image to clone
|
||||
imagecopy($clone, $gd, 0, 0, 0, 0, imagesx($gd), imagesy($gd));
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an "empty" clone of the given GdImage
|
||||
*
|
||||
* This only retains the basic data without transferring the actual image.
|
||||
* It is optionally possible to change the size of the result and set a
|
||||
* background color.
|
||||
*
|
||||
* @param GdImage $gd
|
||||
* @param null|SizeInterface $size
|
||||
* @param ColorInterface $background
|
||||
* @return GdImage
|
||||
*/
|
||||
public static function cloneEmpty(
|
||||
GdImage $gd,
|
||||
?SizeInterface $size = null,
|
||||
ColorInterface $background = new Color(255, 255, 255, 0)
|
||||
): GdImage {
|
||||
// define size
|
||||
$size = match (true) {
|
||||
is_null($size) => new Rectangle(imagesx($gd), imagesy($gd)),
|
||||
default => $size,
|
||||
};
|
||||
|
||||
// create new gd image with same size or new given size
|
||||
$clone = imagecreatetruecolor($size->width(), $size->height());
|
||||
|
||||
// copy resolution to clone
|
||||
$resolution = imageresolution($gd);
|
||||
if (is_array($resolution) && array_key_exists(0, $resolution) && array_key_exists(1, $resolution)) {
|
||||
imageresolution($clone, $resolution[0], $resolution[1]);
|
||||
}
|
||||
|
||||
// fill with background
|
||||
$processor = new ColorProcessor();
|
||||
imagefill($clone, 0, 0, $processor->colorToNative($background));
|
||||
imagealphablending($clone, true);
|
||||
imagesavealpha($clone, true);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of an GdImage that is positioned on the specified background color.
|
||||
* Possible transparent areas are mixed with this color.
|
||||
*
|
||||
* @param GdImage $gd
|
||||
* @param ColorInterface $background
|
||||
* @return GdImage
|
||||
*/
|
||||
public static function cloneBlended(GdImage $gd, ColorInterface $background): GdImage
|
||||
{
|
||||
// create empty canvas with same size
|
||||
$clone = static::cloneEmpty($gd, background: $background);
|
||||
|
||||
// transfer actual image to clone
|
||||
imagecopy($clone, $gd, 0, 0, 0, 0, imagesx($gd), imagesy($gd));
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
@@ -41,7 +41,6 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface
|
||||
if (!imageistruecolor($gd)) {
|
||||
imagepalettetotruecolor($gd);
|
||||
}
|
||||
|
||||
imagesavealpha($gd, true);
|
||||
|
||||
// build image instance
|
||||
|
@@ -47,7 +47,7 @@ class Driver extends AbstractDriver
|
||||
// build new transparent GDImage
|
||||
$data = imagecreatetruecolor($width, $height);
|
||||
imagesavealpha($data, true);
|
||||
$background = imagecolorallocatealpha($data, 255, 0, 255, 127);
|
||||
$background = imagecolorallocatealpha($data, 255, 255, 255, 127);
|
||||
imagealphablending($data, false);
|
||||
imagefill($data, 0, 0, $background);
|
||||
imagecolortransparent($data, $background);
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use Intervention\Image\Drivers\DriverSpecializedEncoder;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
@@ -13,9 +14,10 @@ class JpegEncoder extends DriverSpecializedEncoder
|
||||
{
|
||||
public function encode(ImageInterface $image): EncodedImage
|
||||
{
|
||||
$gd = $image->core()->native();
|
||||
$data = $this->getBuffered(function () use ($gd) {
|
||||
imagejpeg($gd, null, $this->quality);
|
||||
$output = Cloner::cloneBlended($image->core()->native(), background: $image->blendingColor());
|
||||
|
||||
$data = $this->getBuffered(function () use ($output) {
|
||||
imagejpeg($output, null, $this->quality);
|
||||
});
|
||||
|
||||
return new EncodedImage($data, 'image/jpeg');
|
||||
|
@@ -107,35 +107,6 @@ class Frame implements FrameInterface
|
||||
*/
|
||||
public function __clone(): void
|
||||
{
|
||||
// create new clone image
|
||||
$width = imagesx($this->native);
|
||||
$height = imagesy($this->native);
|
||||
$clone = match (imageistruecolor($this->native)) {
|
||||
true => imagecreatetruecolor($width, $height),
|
||||
default => imagecreate($width, $height),
|
||||
};
|
||||
|
||||
// transfer resolution to clone
|
||||
$resolution = imageresolution($this->native);
|
||||
if (is_array($resolution) && array_key_exists(0, $resolution) && array_key_exists(1, $resolution)) {
|
||||
imageresolution($clone, $resolution[0], $resolution[1]);
|
||||
}
|
||||
|
||||
// transfer transparency to clone
|
||||
$transIndex = imagecolortransparent($this->native);
|
||||
if ($transIndex != -1) {
|
||||
$rgba = imagecolorsforindex($clone, $transIndex);
|
||||
$transColor = imagecolorallocatealpha($clone, $rgba['red'], $rgba['green'], $rgba['blue'], 127);
|
||||
imagefill($clone, 0, 0, $transColor);
|
||||
imagecolortransparent($clone, $transColor);
|
||||
} else {
|
||||
imagealphablending($clone, false);
|
||||
imagesavealpha($clone, true);
|
||||
}
|
||||
|
||||
// transfer actual image to clone
|
||||
imagecopy($clone, $this->native, 0, 0, 0, 0, $width, $height);
|
||||
|
||||
$this->native = $clone;
|
||||
$this->native = Cloner::clone($this->native);
|
||||
}
|
||||
}
|
||||
|
34
src/Drivers/Gd/Modifiers/BlendTransparencyModifier.php
Normal file
34
src/Drivers/Gd/Modifiers/BlendTransparencyModifier.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
/**
|
||||
* @property mixed $color
|
||||
*/
|
||||
class BlendTransparencyModifier extends DriverSpecialized
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
// decode blending color
|
||||
$color = $this->driver()->handleInput(
|
||||
$this->color ? $this->color : $image->blendingColor()
|
||||
);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
// create new canvas with blending color as background
|
||||
$modified = Cloner::cloneBlended(
|
||||
$frame->native(),
|
||||
background: $color
|
||||
);
|
||||
|
||||
// set new gd image
|
||||
$frame->setNative($modified);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
@@ -5,12 +5,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\SpecializedModifier;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
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 getCropSize(ImageInterface $image)
|
||||
@@ -20,16 +20,17 @@ use Intervention\Image\Modifiers\FillModifier;
|
||||
* @property mixed $background
|
||||
* @property string $position
|
||||
*/
|
||||
class ContainModifier extends SpecializedModifier
|
||||
class ContainModifier extends DriverSpecialized
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$crop = $this->getCropSize($image);
|
||||
$resize = $this->getResizeSize($image);
|
||||
$background = $this->driver()->handleInput($this->background);
|
||||
$blendingColor = $image->blendingColor();
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$this->modify($frame, $crop, $resize, $background);
|
||||
$this->modify($frame, $crop, $resize, $background, $blendingColor);
|
||||
}
|
||||
|
||||
return $image;
|
||||
@@ -39,26 +40,19 @@ class ContainModifier extends SpecializedModifier
|
||||
FrameInterface $frame,
|
||||
SizeInterface $crop,
|
||||
SizeInterface $resize,
|
||||
ColorInterface $background
|
||||
ColorInterface $background,
|
||||
ColorInterface $blendingColor
|
||||
): void {
|
||||
// create new gd image
|
||||
$modified = $this->driver()->createImage(
|
||||
$resize->width(),
|
||||
$resize->height()
|
||||
)->modify(
|
||||
new FillModifier($background)
|
||||
)->core()->native();
|
||||
|
||||
// retain resolution
|
||||
$this->copyResolution($frame->native(), $modified);
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resize, $background);
|
||||
|
||||
// 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(),
|
||||
$blendingColor->channel(Red::class)->value(),
|
||||
$blendingColor->channel(Green::class)->value(),
|
||||
$blendingColor->channel(Blue::class)->value(),
|
||||
127,
|
||||
);
|
||||
imagealphablending($modified, false); // do not blend / just overwrite
|
||||
|
@@ -2,7 +2,8 @@
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Drivers\Gd\SpecializedModifier;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
@@ -11,7 +12,7 @@ use Intervention\Image\Interfaces\SizeInterface;
|
||||
* @method SizeInterface getResizeSize(ImageInterface $image)
|
||||
* @method SizeInterface getCropSize(ImageInterface $image)
|
||||
*/
|
||||
class CoverModifier extends SpecializedModifier
|
||||
class CoverModifier extends DriverSpecialized
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
@@ -28,31 +29,12 @@ class CoverModifier extends SpecializedModifier
|
||||
protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void
|
||||
{
|
||||
// create new image
|
||||
$modified = $this->driver()->createImage(
|
||||
$resize->width(),
|
||||
$resize->height()
|
||||
)->core()->native();
|
||||
|
||||
// get original image
|
||||
$original = $frame->native();
|
||||
|
||||
// retain resolution
|
||||
$this->copyResolution($original, $modified);
|
||||
|
||||
// preserve transparency
|
||||
$transIndex = imagecolortransparent($original);
|
||||
|
||||
if ($transIndex != -1) {
|
||||
$rgba = imagecolorsforindex($modified, $transIndex);
|
||||
$transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127);
|
||||
imagefill($modified, 0, 0, $transColor);
|
||||
imagecolortransparent($modified, $transColor);
|
||||
}
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resize);
|
||||
|
||||
// copy content from resource
|
||||
imagecopyresampled(
|
||||
$modified,
|
||||
$original,
|
||||
$frame->native(),
|
||||
0,
|
||||
0,
|
||||
$crop->pivot()->x(),
|
||||
|
@@ -2,7 +2,8 @@
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Drivers\Gd\SpecializedModifier;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
@@ -11,58 +12,109 @@ 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
|
||||
class CropModifier extends DriverSpecialized
|
||||
{
|
||||
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)
|
||||
);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$this->cropFrame($frame, $crop);
|
||||
$this->cropFrame($frame, $originalSize, $crop, $background);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
protected function cropFrame(FrameInterface $frame, SizeInterface $resizeTo): void
|
||||
{
|
||||
// create new image
|
||||
$modified = $this->driver()
|
||||
->createImage($resizeTo->width(), $resizeTo->height())
|
||||
->core()
|
||||
->native();
|
||||
protected function cropFrame(
|
||||
FrameInterface $frame,
|
||||
SizeInterface $originalSize,
|
||||
SizeInterface $resizeTo,
|
||||
int $background
|
||||
): void {
|
||||
// create new image with transparent background
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo);
|
||||
|
||||
// get original image
|
||||
$original = $frame->native();
|
||||
// define offset
|
||||
$offset_x = ($resizeTo->pivot()->x() + $this->offset_x);
|
||||
$offset_y = ($resizeTo->pivot()->y() + $this->offset_y);
|
||||
|
||||
// retain resolution
|
||||
$this->copyResolution($original, $modified);
|
||||
|
||||
// preserve transparency
|
||||
$transIndex = imagecolortransparent($original);
|
||||
|
||||
if ($transIndex != -1) {
|
||||
$rgba = imagecolorsforindex($modified, $transIndex);
|
||||
$transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127);
|
||||
imagefill($modified, 0, 0, $transColor);
|
||||
imagecolortransparent($modified, $transColor);
|
||||
}
|
||||
// define target width & height
|
||||
$targetWidth = min($resizeTo->width(), $originalSize->width());
|
||||
$targetHeight = min($resizeTo->height(), $originalSize->height());
|
||||
$targetWidth = $targetWidth < $originalSize->width() ? $targetWidth + $offset_x : $targetWidth;
|
||||
$targetHeight = $targetHeight < $originalSize->height() ? $targetHeight + $offset_y : $targetHeight;
|
||||
|
||||
// copy content from resource
|
||||
imagecopyresampled(
|
||||
$modified,
|
||||
$original,
|
||||
$frame->native(),
|
||||
$offset_x * -1,
|
||||
$offset_y * -1,
|
||||
0,
|
||||
0,
|
||||
$resizeTo->pivot()->x() + $this->offset_x,
|
||||
$resizeTo->pivot()->y() + $this->offset_y,
|
||||
$resizeTo->width(),
|
||||
$resizeTo->height(),
|
||||
$resizeTo->width(),
|
||||
$resizeTo->height(),
|
||||
$targetWidth,
|
||||
$targetHeight,
|
||||
$targetWidth,
|
||||
$targetHeight
|
||||
);
|
||||
|
||||
// don't alpha blend for covering areas
|
||||
imagealphablending($modified, false);
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ($resizeTo->width() > $originalSize->width() || $this->offset_x > 0) {
|
||||
imagefilledrectangle(
|
||||
$modified,
|
||||
$originalSize->width() + ($this->offset_x * -1) - $resizeTo->pivot()->x(),
|
||||
0,
|
||||
$resizeTo->width(),
|
||||
$resizeTo->height(),
|
||||
$background
|
||||
);
|
||||
}
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ($resizeTo->height() > $originalSize->height() || $this->offset_y > 0) {
|
||||
imagefilledrectangle(
|
||||
$modified,
|
||||
($this->offset_x * -1) - $resizeTo->pivot()->x(),
|
||||
$originalSize->height() + ($this->offset_y * -1) - $resizeTo->pivot()->y(),
|
||||
($this->offset_x * -1) + $originalSize->width() - 1 - $resizeTo->pivot()->x(),
|
||||
$resizeTo->height(),
|
||||
$background
|
||||
);
|
||||
}
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ((($this->offset_x * -1) - $resizeTo->pivot()->x() - 1) > 0) {
|
||||
imagefilledrectangle(
|
||||
$modified,
|
||||
0,
|
||||
0,
|
||||
($this->offset_x * -1) - $resizeTo->pivot()->x() - 1,
|
||||
$resizeTo->height(),
|
||||
$background
|
||||
);
|
||||
}
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ((($this->offset_y * -1) - $resizeTo->pivot()->y() - 1) > 0) {
|
||||
imagefilledrectangle(
|
||||
$modified,
|
||||
($this->offset_x * -1) - $resizeTo->pivot()->x(),
|
||||
0,
|
||||
($this->offset_x * -1) + $originalSize->width() - $resizeTo->pivot()->x() - 1,
|
||||
($this->offset_y * -1) - $resizeTo->pivot()->y() - 1,
|
||||
$background
|
||||
);
|
||||
}
|
||||
|
||||
// set new content as recource
|
||||
$frame->setNative($modified);
|
||||
}
|
||||
|
@@ -2,7 +2,8 @@
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Drivers\Gd\SpecializedModifier;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Exceptions\InputException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
@@ -10,7 +11,7 @@ use Intervention\Image\Interfaces\ImageInterface;
|
||||
* @property int $limit
|
||||
* @property mixed $background
|
||||
*/
|
||||
class QuantizeColorsModifier extends SpecializedModifier
|
||||
class QuantizeColorsModifier extends DriverSpecialized
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
@@ -33,10 +34,7 @@ class QuantizeColorsModifier extends SpecializedModifier
|
||||
|
||||
foreach ($image as $frame) {
|
||||
// create new image for color quantization
|
||||
$reduced = imagecreatetruecolor($width, $height);
|
||||
|
||||
// retain resolution
|
||||
$this->copyResolution($frame->native(), $reduced);
|
||||
$reduced = Cloner::cloneEmpty($frame->native(), background: $image->blendingColor());
|
||||
|
||||
// fill with background
|
||||
imagefill($reduced, 0, 0, $background);
|
||||
|
@@ -5,18 +5,18 @@ 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\SpecializedModifier;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
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 SpecializedModifier
|
||||
class ResizeCanvasModifier extends DriverSpecialized
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
@@ -35,16 +35,8 @@ class ResizeCanvasModifier extends SpecializedModifier
|
||||
SizeInterface $resize,
|
||||
ColorInterface $background,
|
||||
): void {
|
||||
// create new gd image
|
||||
$modified = $this->driver()->createImage(
|
||||
$resize->width(),
|
||||
$resize->height()
|
||||
)->modify(
|
||||
new FillModifier($background)
|
||||
)->core()->native();
|
||||
|
||||
// retain resolution
|
||||
$this->copyResolution($frame->native(), $modified);
|
||||
// create new canvas with target size & target background color
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resize, $background);
|
||||
|
||||
// make image area transparent to keep transparency
|
||||
// even if background-color is set
|
||||
@@ -57,7 +49,7 @@ class ResizeCanvasModifier extends SpecializedModifier
|
||||
);
|
||||
|
||||
imagealphablending($modified, false); // do not blend / just overwrite
|
||||
imagecolortransparent($modified, $transparent);
|
||||
// imagecolortransparent($modified, $transparent);
|
||||
imagefilledrectangle(
|
||||
$modified,
|
||||
$resize->pivot()->x() * -1,
|
||||
|
@@ -2,7 +2,8 @@
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Drivers\Gd\SpecializedModifier;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
@@ -11,7 +12,7 @@ use Intervention\Image\Interfaces\SizeInterface;
|
||||
* @property null|int $width
|
||||
* @property null|int $height
|
||||
*/
|
||||
class ResizeModifier extends SpecializedModifier
|
||||
class ResizeModifier extends DriverSpecialized
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
@@ -25,35 +26,13 @@ class ResizeModifier extends SpecializedModifier
|
||||
|
||||
private function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): void
|
||||
{
|
||||
// create new image
|
||||
$modified = imagecreatetruecolor(
|
||||
$resizeTo->width(),
|
||||
$resizeTo->height()
|
||||
);
|
||||
|
||||
// get current GDImage
|
||||
$current = $frame->native();
|
||||
|
||||
// retain resolution
|
||||
$this->copyResolution($current, $modified);
|
||||
|
||||
// preserve transparency
|
||||
$transIndex = imagecolortransparent($current);
|
||||
|
||||
if ($transIndex != -1) {
|
||||
$rgba = imagecolorsforindex($modified, $transIndex);
|
||||
$transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127);
|
||||
imagefill($modified, 0, 0, $transColor);
|
||||
imagecolortransparent($modified, $transColor);
|
||||
} else {
|
||||
imagealphablending($modified, false);
|
||||
imagesavealpha($modified, true);
|
||||
}
|
||||
// create empty canvas in target size
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo);
|
||||
|
||||
// copy content from resource
|
||||
imagecopyresampled(
|
||||
$modified,
|
||||
$current,
|
||||
$frame->native(),
|
||||
$resizeTo->pivot()->x(),
|
||||
$resizeTo->pivot()->y(),
|
||||
0,
|
||||
|
@@ -5,18 +5,18 @@ 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\SpecializedModifier;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Geometry\Rectangle;
|
||||
use Intervention\Image\Interfaces\ColorInterface;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Modifiers\FillModifier;
|
||||
|
||||
/**
|
||||
* @method mixed rotationAngle()
|
||||
* @property mixed $background
|
||||
*/
|
||||
class RotateModifier extends SpecializedModifier
|
||||
class RotateModifier extends DriverSpecialized
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
@@ -74,15 +74,7 @@ class RotateModifier extends SpecializedModifier
|
||||
->rotate($this->rotationAngle() * -1);
|
||||
|
||||
// create new gd image
|
||||
$modified = $this->driver()->createImage(
|
||||
imagesx($rotated),
|
||||
imagesy($rotated)
|
||||
)->modify(new FillModifier($background))
|
||||
->core()
|
||||
->native();
|
||||
|
||||
// retain resolution
|
||||
$this->copyResolution($frame->native(), $modified);
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $container, $background);
|
||||
|
||||
// draw the cutout on new gd image to have a transparent
|
||||
// background where the rotated image will be placed
|
||||
|
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd;
|
||||
|
||||
use GdImage;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Interfaces\ModifierInterface;
|
||||
|
||||
abstract class SpecializedModifier extends DriverSpecialized implements ModifierInterface
|
||||
{
|
||||
protected function copyResolution(GdImage $source, GdImage $target): void
|
||||
{
|
||||
$resolution = imageresolution($source);
|
||||
if (is_array($resolution) && array_key_exists(0, $resolution) && array_key_exists(1, $resolution)) {
|
||||
imageresolution($target, $resolution[0], $resolution[1]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -46,7 +46,7 @@ class Driver extends AbstractDriver
|
||||
*/
|
||||
public function createImage(int $width, int $height): ImageInterface
|
||||
{
|
||||
$background = new ImagickPixel('rgba(0, 0, 0, 0)');
|
||||
$background = new ImagickPixel('rgba(255, 255, 255, 0)');
|
||||
|
||||
$imagick = new Imagick();
|
||||
$imagick->newImage($width, $height, $background, 'png');
|
||||
@@ -54,6 +54,7 @@ class Driver extends AbstractDriver
|
||||
$imagick->setImageType(Imagick::IMGTYPE_UNDEFINED);
|
||||
$imagick->setColorspace(Imagick::COLORSPACE_SRGB);
|
||||
$imagick->setImageResolution(96, 96);
|
||||
$imagick->setImageBackgroundColor($background);
|
||||
|
||||
return new Image($this, new Core($imagick));
|
||||
}
|
||||
|
@@ -17,15 +17,25 @@ class JpegEncoder extends DriverSpecializedEncoder
|
||||
$format = 'jpeg';
|
||||
$compression = Imagick::COMPRESSION_JPEG;
|
||||
|
||||
// resolve blending color because jpeg has no transparency
|
||||
$background = $this->driver()
|
||||
->colorProcessor($image->colorspace())
|
||||
->colorToNative($image->blendingColor());
|
||||
|
||||
// set alpha value to 1 because Imagick renders
|
||||
// possible full transparent colors as black
|
||||
$background->setColorValue(Imagick::COLOR_ALPHA, 1);
|
||||
|
||||
$imagick = $image->core()->native();
|
||||
$imagick->setImageBackgroundColor('white');
|
||||
$imagick->setBackgroundColor('white');
|
||||
$imagick->setImageBackgroundColor($background);
|
||||
$imagick->setBackgroundColor($background);
|
||||
$imagick->setFormat($format);
|
||||
$imagick->setImageFormat($format);
|
||||
$imagick->setCompression($compression);
|
||||
$imagick->setImageCompression($compression);
|
||||
$imagick->setCompressionQuality($this->quality);
|
||||
$imagick->setImageCompressionQuality($this->quality);
|
||||
$imagick->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
|
||||
|
||||
return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg');
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace Intervention\Image\Drivers\Imagick;
|
||||
|
||||
use Imagick;
|
||||
use ImagickPixel;
|
||||
use Intervention\Image\Geometry\Rectangle;
|
||||
use Intervention\Image\Image;
|
||||
use Intervention\Image\Interfaces\DriverInterface;
|
||||
@@ -14,6 +15,9 @@ class Frame implements FrameInterface
|
||||
{
|
||||
public function __construct(protected Imagick $native)
|
||||
{
|
||||
$background = new ImagickPixel('rgba(255, 255, 255, 0)');
|
||||
$this->native->setImageBackgroundColor($background);
|
||||
$this->native->setBackgroundColor($background);
|
||||
}
|
||||
|
||||
/**
|
||||
|
35
src/Drivers/Imagick/Modifiers/BlendTransparencyModifier.php
Normal file
35
src/Drivers/Imagick/Modifiers/BlendTransparencyModifier.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||
|
||||
use Imagick;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
/**
|
||||
* @property mixed $color
|
||||
*/
|
||||
class BlendTransparencyModifier extends DriverSpecialized
|
||||
{
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
// decode blending color
|
||||
$color = $this->driver()->handleInput(
|
||||
$this->color ? $this->color : $image->blendingColor()
|
||||
);
|
||||
|
||||
// get imagickpixel from color
|
||||
$pixel = $this->driver()
|
||||
->colorProcessor($image->colorspace())
|
||||
->colorToNative($color);
|
||||
|
||||
// merge transparent areas with the background color
|
||||
foreach ($image as $frame) {
|
||||
$frame->native()->setImageBackgroundColor($pixel);
|
||||
$frame->native()->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
|
||||
$frame->native()->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||
|
||||
use ImagickDraw;
|
||||
use ImagickPixel;
|
||||
use Intervention\Image\Drivers\DriverSpecialized;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\ModifierInterface;
|
||||
@@ -11,20 +13,84 @@ use Intervention\Image\Interfaces\SizeInterface;
|
||||
* @method SizeInterface crop(ImageInterface $image)
|
||||
* @property int $offset_x
|
||||
* @property int $offset_y
|
||||
* @property mixed $background
|
||||
*/
|
||||
class CropModifier extends DriverSpecialized implements ModifierInterface
|
||||
{
|
||||
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)
|
||||
);
|
||||
|
||||
$transparent = new ImagickPixel('transparent');
|
||||
|
||||
$draw = new ImagickDraw();
|
||||
$draw->setFillColor($background);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$frame->native()->setBackgroundColor($transparent);
|
||||
$frame->native()->setImageBackgroundColor($transparent);
|
||||
|
||||
// crop image
|
||||
$frame->native()->extentImage(
|
||||
$crop->width(),
|
||||
$crop->height(),
|
||||
$crop->pivot()->x() + $this->offset_x,
|
||||
$crop->pivot()->y() + $this->offset_y
|
||||
);
|
||||
|
||||
// repage
|
||||
$frame->native()->setImagePage(
|
||||
$crop->width(),
|
||||
$crop->height(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ($crop->width() > $originalSize->width() || $this->offset_x > 0) {
|
||||
$draw->rectangle(
|
||||
$originalSize->width() + ($this->offset_x * -1) - $crop->pivot()->x(),
|
||||
0,
|
||||
$crop->width(),
|
||||
$crop->height()
|
||||
);
|
||||
}
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ($crop->height() > $originalSize->height() || $this->offset_y > 0) {
|
||||
$draw->rectangle(
|
||||
($this->offset_x * -1) - $crop->pivot()->x(),
|
||||
$originalSize->height() + ($this->offset_y * -1) - $crop->pivot()->y(),
|
||||
($this->offset_x * -1) + $originalSize->width() - 1 - $crop->pivot()->x(),
|
||||
$crop->height()
|
||||
);
|
||||
}
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ((($this->offset_x * -1) - $crop->pivot()->x() - 1) > 0) {
|
||||
$draw->rectangle(
|
||||
0,
|
||||
0,
|
||||
($this->offset_x * -1) - $crop->pivot()->x() - 1,
|
||||
$crop->height()
|
||||
);
|
||||
}
|
||||
|
||||
// cover the possible newly created areas with background color
|
||||
if ((($this->offset_y * -1) - $crop->pivot()->y() - 1) > 0) {
|
||||
$draw->rectangle(
|
||||
($this->offset_x * -1) - $crop->pivot()->x(),
|
||||
0,
|
||||
($this->offset_x * -1) + $originalSize->width() - $crop->pivot()->x() - 1,
|
||||
($this->offset_y * -1) - $crop->pivot()->y() - 1,
|
||||
);
|
||||
}
|
||||
|
||||
$frame->native()->drawImage($draw);
|
||||
}
|
||||
|
||||
return $image;
|
||||
|
@@ -10,6 +10,7 @@ use Intervention\Image\Analyzers\PixelColorsAnalyzer;
|
||||
use Intervention\Image\Analyzers\ProfileAnalyzer;
|
||||
use Intervention\Image\Analyzers\ResolutionAnalyzer;
|
||||
use Intervention\Image\Analyzers\WidthAnalyzer;
|
||||
use Intervention\Image\Colors\Rgb\Color;
|
||||
use Intervention\Image\Encoders\AutoEncoder;
|
||||
use Intervention\Image\Encoders\AvifEncoder;
|
||||
use Intervention\Image\Encoders\BmpEncoder;
|
||||
@@ -45,6 +46,7 @@ use Intervention\Image\Interfaces\ModifierInterface;
|
||||
use Intervention\Image\Interfaces\ProfileInterface;
|
||||
use Intervention\Image\Interfaces\ResolutionInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
use Intervention\Image\Modifiers\BlendTransparencyModifier;
|
||||
use Intervention\Image\Modifiers\BlurModifier;
|
||||
use Intervention\Image\Modifiers\BrightnessModifier;
|
||||
use Intervention\Image\Modifiers\ColorizeModifier;
|
||||
@@ -93,6 +95,14 @@ final class Image implements ImageInterface
|
||||
*/
|
||||
protected Origin $origin;
|
||||
|
||||
/**
|
||||
* Color is mixed with transparent areas when converting to a format which
|
||||
* does not support transparency.
|
||||
*
|
||||
* @var ColorInterface
|
||||
*/
|
||||
protected ColorInterface $blendingColor;
|
||||
|
||||
/**
|
||||
* Create new instance
|
||||
*
|
||||
@@ -107,6 +117,9 @@ final class Image implements ImageInterface
|
||||
protected CollectionInterface $exif = new Collection()
|
||||
) {
|
||||
$this->origin = new Origin();
|
||||
$this->blendingColor = $this->colorspace()->importColor(
|
||||
new Color(255, 255, 255, 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,6 +382,38 @@ final class Image implements ImageInterface
|
||||
return $this->analyze(new PixelColorsAnalyzer($x, $y));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ImageInterface::blendingColor()
|
||||
*/
|
||||
public function blendingColor(): ColorInterface
|
||||
{
|
||||
return $this->blendingColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ImageInterface::setBlendingColor()
|
||||
*/
|
||||
public function setBlendingColor(mixed $color): ImageInterface
|
||||
{
|
||||
$this->blendingColor = $this->driver()->handleInput($color);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ImageInterface::blendTransparency()
|
||||
*/
|
||||
public function blendTransparency(mixed $color = null): ImageInterface
|
||||
{
|
||||
return $this->modify(new BlendTransparencyModifier($color));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@@ -671,9 +716,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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -185,6 +185,31 @@ interface ImageInterface extends IteratorAggregate, Countable
|
||||
*/
|
||||
public function pickColors(int $x, int $y): CollectionInterface;
|
||||
|
||||
/**
|
||||
* Return color that is mixed with transparent areas when converting to a format which
|
||||
* does not support transparency.
|
||||
*
|
||||
* @return ColorInterface
|
||||
*/
|
||||
public function blendingColor(): ColorInterface;
|
||||
|
||||
/**
|
||||
* Set blending color will have no effect unless image is converted into a format
|
||||
* which does not support transparency.
|
||||
*
|
||||
* @param mixed $color
|
||||
* @return ImageInterface
|
||||
*/
|
||||
public function setBlendingColor(mixed $color): ImageInterface;
|
||||
|
||||
/**
|
||||
* Replace transparent areas of the image with given color
|
||||
*
|
||||
* @param mixed $color
|
||||
* @return ImageInterface
|
||||
*/
|
||||
public function blendTransparency(mixed $color = null): ImageInterface;
|
||||
|
||||
/**
|
||||
* Retrieve ICC color profile of image
|
||||
*
|
||||
@@ -466,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
|
||||
*/
|
||||
@@ -474,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;
|
||||
|
||||
|
10
src/Modifiers/BlendTransparencyModifier.php
Normal file
10
src/Modifiers/BlendTransparencyModifier.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Modifiers;
|
||||
|
||||
class BlendTransparencyModifier extends AbstractModifier
|
||||
{
|
||||
public function __construct(public mixed $color = null)
|
||||
{
|
||||
}
|
||||
}
|
@@ -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'
|
||||
) {
|
||||
}
|
||||
|
69
tests/Drivers/Gd/ClonerTest.php
Normal file
69
tests/Drivers/Gd/ClonerTest.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Tests\Drivers\Gd;
|
||||
|
||||
use Intervention\Image\Colors\Rgb\Color;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Geometry\Rectangle;
|
||||
use Intervention\Image\Tests\TestCase;
|
||||
|
||||
class ClonerTest extends TestCase
|
||||
{
|
||||
public function testClone(): void
|
||||
{
|
||||
$gd = imagecreatefromgif($this->getTestImagePath('gradient.gif'));
|
||||
$clone = Cloner::clone($gd);
|
||||
|
||||
$this->assertEquals(16, imagesx($gd));
|
||||
$this->assertEquals(16, imagesy($gd));
|
||||
$this->assertEquals(16, imagesx($clone));
|
||||
$this->assertEquals(16, imagesy($clone));
|
||||
|
||||
$this->assertEquals(
|
||||
imagecolorsforindex($gd, imagecolorat($gd, 10, 10)),
|
||||
imagecolorsforindex($clone, imagecolorat($clone, 10, 10))
|
||||
);
|
||||
}
|
||||
|
||||
public function testCloneEmpty(): void
|
||||
{
|
||||
$gd = imagecreatefromgif($this->getTestImagePath('gradient.gif'));
|
||||
$clone = Cloner::cloneEmpty($gd, new Rectangle(12, 12), new Color(255, 0, 0, 0));
|
||||
|
||||
$this->assertEquals(16, imagesx($gd));
|
||||
$this->assertEquals(16, imagesy($gd));
|
||||
$this->assertEquals(12, imagesx($clone));
|
||||
$this->assertEquals(12, imagesy($clone));
|
||||
|
||||
$this->assertEquals(
|
||||
['red' => 0, 'green' => 255, 'blue' => 2, 'alpha' => 0],
|
||||
imagecolorsforindex($gd, imagecolorat($gd, 10, 10)),
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
['red' => 255, 'green' => 0, 'blue' => 0, 'alpha' => 127],
|
||||
imagecolorsforindex($clone, imagecolorat($clone, 10, 10))
|
||||
);
|
||||
}
|
||||
|
||||
public function testCLoneBlended(): void
|
||||
{
|
||||
$gd = imagecreatefromgif($this->getTestImagePath('gradient.gif'));
|
||||
$clone = Cloner::cloneBlended($gd, new Color(255, 0, 255, 255));
|
||||
|
||||
$this->assertEquals(16, imagesx($gd));
|
||||
$this->assertEquals(16, imagesy($gd));
|
||||
$this->assertEquals(16, imagesx($clone));
|
||||
$this->assertEquals(16, imagesy($clone));
|
||||
|
||||
$this->assertEquals(
|
||||
['red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 127],
|
||||
imagecolorsforindex($gd, imagecolorat($gd, 1, 0)),
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
['red' => 255, 'green' => 0, 'blue' => 255, 'alpha' => 0],
|
||||
imagecolorsforindex($clone, imagecolorat($clone, 1, 0))
|
||||
);
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ namespace Intervention\Image\Tests\Drivers\Gd;
|
||||
|
||||
use Intervention\Image\Analyzers\WidthAnalyzer;
|
||||
use Intervention\Image\Collection;
|
||||
use Intervention\Image\Colors\Rgb\Color;
|
||||
use Intervention\Image\Drivers\Gd\Core;
|
||||
use Intervention\Image\Drivers\Gd\Driver;
|
||||
use Intervention\Image\Drivers\Gd\Frame;
|
||||
@@ -53,10 +54,10 @@ class ImageTest extends TestCase
|
||||
$this->assertEquals(4, $result->width());
|
||||
|
||||
$this->assertEquals('ff0000', $image->pickColor(0, 0)->toHex());
|
||||
$this->assertEquals('00000000', $image->pickColor(1, 0)->toHex());
|
||||
$this->assertTransparency($image->pickColor(1, 0));
|
||||
|
||||
$this->assertEquals('ff0000', $clone->pickColor(0, 0)->toHex());
|
||||
$this->assertEquals('00000000', $clone->pickColor(1, 0)->toHex());
|
||||
$this->assertTransparency($image->pickColor(1, 0));
|
||||
}
|
||||
|
||||
public function testDriver(): void
|
||||
@@ -210,4 +211,14 @@ class ImageTest extends TestCase
|
||||
{
|
||||
$this->assertInstanceOf(Image::class, $this->image->text('test', 0, 0, new Font()));
|
||||
}
|
||||
|
||||
public function testSetGetBlendingColor(): void
|
||||
{
|
||||
$image = $this->readTestImage('gradient.gif');
|
||||
$this->assertInstanceOf(ColorInterface::class, $image->blendingColor());
|
||||
$this->assertColor(255, 255, 255, 0, $image->blendingColor());
|
||||
$result = $image->setBlendingColor(new Color(1, 2, 3, 4));
|
||||
$this->assertColor(1, 2, 3, 4, $result->blendingColor());
|
||||
$this->assertColor(1, 2, 3, 4, $image->blendingColor());
|
||||
}
|
||||
}
|
||||
|
@@ -138,6 +138,6 @@ class GdInputHandlerTest extends TestCase
|
||||
$input = 'transparent';
|
||||
$result = $handler->handle($input);
|
||||
$this->assertInstanceOf(RgbColor::class, $result);
|
||||
$this->assertEquals([255, 0, 255, 0], $result->toArray());
|
||||
$this->assertEquals([255, 255, 255, 0], $result->toArray());
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ class ContainModifierTest extends TestCase
|
||||
$this->assertEquals(200, $image->width());
|
||||
$this->assertEquals(100, $image->height());
|
||||
$this->assertColor(255, 255, 0, 255, $image->pickColor(0, 0));
|
||||
$this->assertColor(255, 255, 0, 0, $image->pickColor(140, 10)); // transparent
|
||||
$this->assertTransparency($image->pickColor(140, 10));
|
||||
$this->assertColor(255, 255, 0, 255, $image->pickColor(175, 10));
|
||||
}
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ class FlipFlopModifierTest extends TestCase
|
||||
$image = $this->readTestImage('tile.png');
|
||||
$this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex());
|
||||
$image->modify(new FlipModifier());
|
||||
$this->assertEquals('00000000', $image->pickColor(0, 0)->toHex());
|
||||
$this->assertTransparency($image->pickColor(0, 0));
|
||||
}
|
||||
|
||||
public function testFlopImage(): void
|
||||
@@ -29,6 +29,6 @@ class FlipFlopModifierTest extends TestCase
|
||||
$image = $this->readTestImage('tile.png');
|
||||
$this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex());
|
||||
$image->modify(new FlopModifier());
|
||||
$this->assertEquals('00000000', $image->pickColor(0, 0)->toHex());
|
||||
$this->assertTransparency($image->pickColor(0, 0));
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick;
|
||||
use Imagick;
|
||||
use Intervention\Image\Analyzers\WidthAnalyzer;
|
||||
use Intervention\Image\Collection;
|
||||
use Intervention\Image\Colors\Rgb\Color;
|
||||
use Intervention\Image\Drivers\Imagick\Core;
|
||||
use Intervention\Image\Drivers\Imagick\Driver;
|
||||
use Intervention\Image\Drivers\Imagick\Frame;
|
||||
@@ -52,10 +53,10 @@ class ImageTest extends TestCase
|
||||
$this->assertEquals(4, $result->width());
|
||||
|
||||
$this->assertEquals('ff0000', $image->pickColor(0, 0)->toHex());
|
||||
$this->assertEquals('00000000', $image->pickColor(1, 0)->toHex());
|
||||
$this->assertTransparency($image->pickColor(1, 0));
|
||||
|
||||
$this->assertEquals('ff0000', $clone->pickColor(0, 0)->toHex());
|
||||
$this->assertEquals('00000000', $clone->pickColor(1, 0)->toHex());
|
||||
$this->assertTransparency($clone->pickColor(1, 0));
|
||||
}
|
||||
|
||||
public function testDriver(): void
|
||||
@@ -204,4 +205,14 @@ class ImageTest extends TestCase
|
||||
{
|
||||
$this->assertInstanceOf(Image::class, $this->image->sharpen(12));
|
||||
}
|
||||
|
||||
public function testSetGetBlendingColor(): void
|
||||
{
|
||||
$image = $this->readTestImage('gradient.gif');
|
||||
$this->assertInstanceOf(ColorInterface::class, $image->blendingColor());
|
||||
$this->assertColor(255, 255, 255, 0, $image->blendingColor());
|
||||
$result = $image->setBlendingColor(new Color(1, 2, 3, 4));
|
||||
$this->assertColor(1, 2, 3, 4, $result->blendingColor());
|
||||
$this->assertColor(1, 2, 3, 4, $image->blendingColor());
|
||||
}
|
||||
}
|
||||
|
@@ -138,6 +138,6 @@ class InputHandlerTest extends TestCase
|
||||
$input = 'transparent';
|
||||
$result = $handler->handle($input);
|
||||
$this->assertInstanceOf(RgbColor::class, $result);
|
||||
$this->assertEquals([255, 0, 255, 0], $result->toArray());
|
||||
$this->assertEquals([255, 255, 255, 0], $result->toArray());
|
||||
}
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user