diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 5dd8eeeb..5db98ebc 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,3 @@ github: [Intervention] +ko_fi: interventionphp custom: https://paypal.me/interventionio diff --git a/.github/images/support.svg b/.github/images/support.svg new file mode 100644 index 00000000..af31794e --- /dev/null +++ b/.github/images/support.svg @@ -0,0 +1,24 @@ + + + + + + + + + Support me on Ko-fi + + + + + + Support me on Ko-fi + + + + + + + + + diff --git a/readme.md b/readme.md index bb082054..b922cefe 100644 --- a/readme.md +++ b/readme.md @@ -4,6 +4,7 @@ [![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) [![Build Status](https://github.com/Intervention/image/actions/workflows/run-tests.yml/badge.svg)](https://github.com/Intervention/image/actions) [![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) +[![Support me on Ko-fi](https://raw.githubusercontent.com/Intervention/image/develop/.github/images/support.svg)](https://ko-fi.com/interventionphp) Intervention Image is a **PHP image processing library** that provides a simple and expressive way to create, edit, and compose images. It features a unified diff --git a/src/Colors/Cmyk/Color.php b/src/Colors/Cmyk/Color.php index 5aad25a4..d3229e6c 100644 --- a/src/Colors/Cmyk/Color.php +++ b/src/Colors/Cmyk/Color.php @@ -152,4 +152,14 @@ class Color extends AbstractColor { return false; } + + /** + * {@inheritdoc} + * + * @see ColorInterface::isClear() + */ + public function isClear(): bool + { + return false; + } } diff --git a/src/Colors/Hsl/Color.php b/src/Colors/Hsl/Color.php index e682f9a2..20ca0e5d 100644 --- a/src/Colors/Hsl/Color.php +++ b/src/Colors/Hsl/Color.php @@ -120,4 +120,14 @@ class Color extends AbstractColor { return false; } + + /** + * {@inheritdoc} + * + * @see ColorInterface::isClear() + */ + public function isClear(): bool + { + return false; + } } diff --git a/src/Colors/Hsv/Color.php b/src/Colors/Hsv/Color.php index 9c7f285c..8a4f18b2 100644 --- a/src/Colors/Hsv/Color.php +++ b/src/Colors/Hsv/Color.php @@ -120,4 +120,14 @@ class Color extends AbstractColor { return false; } + + /** + * {@inheritdoc} + * + * @see ColorInterface::isClear() + */ + public function isClear(): bool + { + return false; + } } diff --git a/src/Colors/Rgb/Color.php b/src/Colors/Rgb/Color.php index f97a9241..4926a73c 100644 --- a/src/Colors/Rgb/Color.php +++ b/src/Colors/Rgb/Color.php @@ -178,4 +178,14 @@ class Color extends AbstractColor { return $this->alpha()->value() < $this->alpha()->max(); } + + /** + * {@inheritdoc} + * + * @see ColorInterface::isClear() + */ + public function isClear(): bool + { + return $this->alpha()->value() == 0; + } } diff --git a/src/Decoders/EncodedImageObjectDecoder.php b/src/Decoders/EncodedImageObjectDecoder.php new file mode 100644 index 00000000..daddff2d --- /dev/null +++ b/src/Decoders/EncodedImageObjectDecoder.php @@ -0,0 +1,11 @@ + new Rectangle(imagesx($gd), imagesy($gd)), - default => $size, - }; + $size = $size ? $size : new Rectangle(imagesx($gd), imagesy($gd)); // create new gd image with same size or new given size $clone = imagecreatetruecolor($size->width(), $size->height()); @@ -70,6 +68,12 @@ class Cloner imagealphablending($clone, true); imagesavealpha($clone, true); + // set background image as transparent if alpha channel value if color is below .5 + // comes into effect when the end format only supports binary transparency (like GIF) + if ($background->channel(Alpha::class)->value() < 128) { + imagecolortransparent($clone, $processor->colorToNative($background)); + } + return $clone; } diff --git a/src/Drivers/Gd/Decoders/EncodedImageObjectDecoder.php b/src/Drivers/Gd/Decoders/EncodedImageObjectDecoder.php new file mode 100644 index 00000000..f65d0a5b --- /dev/null +++ b/src/Drivers/Gd/Decoders/EncodedImageObjectDecoder.php @@ -0,0 +1,27 @@ +toString()); + } +} diff --git a/src/Drivers/Imagick/Decoders/EncodedImageObjectDecoder.php b/src/Drivers/Imagick/Decoders/EncodedImageObjectDecoder.php new file mode 100644 index 00000000..2348a6b7 --- /dev/null +++ b/src/Drivers/Imagick/Decoders/EncodedImageObjectDecoder.php @@ -0,0 +1,27 @@ +toString()); + } +} diff --git a/src/Drivers/Imagick/Decoders/NativeObjectDecoder.php b/src/Drivers/Imagick/Decoders/NativeObjectDecoder.php index 3f23902d..0799b92e 100644 --- a/src/Drivers/Imagick/Decoders/NativeObjectDecoder.php +++ b/src/Drivers/Imagick/Decoders/NativeObjectDecoder.php @@ -17,6 +17,13 @@ use Intervention\Image\Modifiers\RemoveAnimationModifier; class NativeObjectDecoder extends SpecializableDecoder implements SpecializedInterface { + protected const SUPPORTED_COLORSPACES = [ + Imagick::COLORSPACE_SRGB, + Imagick::COLORSPACE_CMYK, + Imagick::COLORSPACE_HSL, + Imagick::COLORSPACE_HSB, + ]; + public function decode(mixed $input): ImageInterface|ColorInterface { if (!is_object($input)) { @@ -34,6 +41,13 @@ class NativeObjectDecoder extends SpecializableDecoder implements SpecializedInt $input = $input->coalesceImages(); } + // turn image into rgb if colorspace if other than CMYK, RGB, HSL or HSV. + // this prevents working on greyscale colorspace images when loading + // from PNG color type greyscale format. + if (!in_array($input->getImageColorspace(), self::SUPPORTED_COLORSPACES)) { + $input->setImageColorspace(Imagick::COLORSPACE_SRGB); + } + // create image object $image = new Image( $this->driver(), diff --git a/src/InputHandler.php b/src/InputHandler.php index 2772ec45..a0720d13 100644 --- a/src/InputHandler.php +++ b/src/InputHandler.php @@ -15,6 +15,7 @@ use Intervention\Image\Decoders\Base64ImageDecoder; use Intervention\Image\Decoders\BinaryImageDecoder; use Intervention\Image\Decoders\ColorObjectDecoder; use Intervention\Image\Decoders\DataUriImageDecoder; +use Intervention\Image\Decoders\EncodedImageObjectDecoder; use Intervention\Image\Decoders\FilePathImageDecoder; use Intervention\Image\Decoders\FilePointerImageDecoder; use Intervention\Image\Decoders\ImageObjectDecoder; @@ -53,6 +54,7 @@ class InputHandler implements InputHandlerInterface BinaryImageDecoder::class, DataUriImageDecoder::class, Base64ImageDecoder::class, + EncodedImageObjectDecoder::class, ]; /** diff --git a/src/Interfaces/ColorInterface.php b/src/Interfaces/ColorInterface.php index 33c44bc7..b72f1fcc 100644 --- a/src/Interfaces/ColorInterface.php +++ b/src/Interfaces/ColorInterface.php @@ -97,4 +97,11 @@ interface ColorInterface * @return bool */ public function isTransparent(): bool; + + /** + * Determine whether the current color is completely transparent + * + * @return bool + */ + public function isClear(): bool; } diff --git a/tests/Unit/Colors/Cmyk/ColorTest.php b/tests/Unit/Colors/Cmyk/ColorTest.php index 3ae51d7d..7fae9ea4 100644 --- a/tests/Unit/Colors/Cmyk/ColorTest.php +++ b/tests/Unit/Colors/Cmyk/ColorTest.php @@ -111,4 +111,10 @@ final class ColorTest extends BaseTestCase $color = new Color(100, 50, 50, 0); $this->assertFalse($color->isTransparent()); } + + public function testIsClear(): void + { + $color = new Color(0, 0, 0, 0); + $this->assertFalse($color->isClear()); + } } diff --git a/tests/Unit/Colors/Hsl/ColorTest.php b/tests/Unit/Colors/Hsl/ColorTest.php index 9f503bf5..749d21b0 100644 --- a/tests/Unit/Colors/Hsl/ColorTest.php +++ b/tests/Unit/Colors/Hsl/ColorTest.php @@ -108,4 +108,10 @@ final class ColorTest extends BaseTestCase $color = new Color(0, 1, 0); $this->assertFalse($color->isTransparent()); } + + public function testIsClear(): void + { + $color = new Color(0, 1, 0); + $this->assertFalse($color->isClear()); + } } diff --git a/tests/Unit/Colors/Hsv/ColorTest.php b/tests/Unit/Colors/Hsv/ColorTest.php index 930c54ff..f4a270b2 100644 --- a/tests/Unit/Colors/Hsv/ColorTest.php +++ b/tests/Unit/Colors/Hsv/ColorTest.php @@ -108,4 +108,10 @@ final class ColorTest extends BaseTestCase $color = new Color(1, 0, 0); $this->assertFalse($color->isTransparent()); } + + public function testIsClear(): void + { + $color = new Color(0, 1, 0); + $this->assertFalse($color->isClear()); + } } diff --git a/tests/Unit/Colors/Rgb/ColorTest.php b/tests/Unit/Colors/Rgb/ColorTest.php index 6fb2051e..16bfd1ed 100644 --- a/tests/Unit/Colors/Rgb/ColorTest.php +++ b/tests/Unit/Colors/Rgb/ColorTest.php @@ -163,4 +163,19 @@ final class ColorTest extends BaseTestCase $color = new Color(255, 255, 255, 0); $this->assertTrue($color->isTransparent()); } + + public function testIsClear(): void + { + $color = new Color(255, 255, 255); + $this->assertFalse($color->isClear()); + + $color = new Color(255, 255, 255, 255); + $this->assertFalse($color->isClear()); + + $color = new Color(255, 255, 255, 85); + $this->assertFalse($color->isClear()); + + $color = new Color(255, 255, 255, 0); + $this->assertTrue($color->isClear()); + } } diff --git a/tests/Unit/Drivers/Gd/ClonerTest.php b/tests/Unit/Drivers/Gd/ClonerTest.php index fab41b25..7fd39699 100644 --- a/tests/Unit/Drivers/Gd/ClonerTest.php +++ b/tests/Unit/Drivers/Gd/ClonerTest.php @@ -48,7 +48,7 @@ final class ClonerTest extends BaseTestCase ); } - public function testCLoneBlended(): void + public function testCloneBlended(): void { $gd = imagecreatefromgif($this->getTestResourcePath('gradient.gif')); $clone = Cloner::cloneBlended($gd, new Color(255, 0, 255, 255)); diff --git a/tests/Unit/Drivers/Gd/Decoders/EncodedImageObjectDecoderTest.php b/tests/Unit/Drivers/Gd/Decoders/EncodedImageObjectDecoderTest.php new file mode 100644 index 00000000..f75762f1 --- /dev/null +++ b/tests/Unit/Drivers/Gd/Decoders/EncodedImageObjectDecoderTest.php @@ -0,0 +1,32 @@ +decoder = new EncodedImageObjectDecoder(); + $this->decoder->setDriver(new Driver()); + } + + public function testDecode(): void + { + $result = $this->decoder->decode(new EncodedImage($this->getTestResourceData())); + $this->assertInstanceOf(Image::class, $result); + } +} diff --git a/tests/Unit/Drivers/Imagick/Decoders/EncodedImageObjectDecoderTest.php b/tests/Unit/Drivers/Imagick/Decoders/EncodedImageObjectDecoderTest.php new file mode 100644 index 00000000..826498d3 --- /dev/null +++ b/tests/Unit/Drivers/Imagick/Decoders/EncodedImageObjectDecoderTest.php @@ -0,0 +1,32 @@ +decoder = new EncodedImageObjectDecoder(); + $this->decoder->setDriver(new Driver()); + } + + public function testDecode(): void + { + $result = $this->decoder->decode(new EncodedImage($this->getTestResourceData())); + $this->assertInstanceOf(Image::class, $result); + } +}