├── src ├── Colors │ ├── Cmyk │ │ ├── Channels │ │ │ ├── Key.php │ │ │ ├── Yellow.php │ │ │ ├── Magenta.php │ │ │ └── Cyan.php │ │ └── Decoders │ │ │ └── StringColorDecoder.php │ ├── Rgb │ │ ├── Channels │ │ │ ├── Blue.php │ │ │ ├── Green.php │ │ │ ├── Alpha.php │ │ │ └── Red.php │ │ └── Decoders │ │ │ ├── TransparentColorDecoder.php │ │ │ └── HexColorDecoder.php │ ├── Profile.php │ ├── Hsl │ │ ├── Channels │ │ │ ├── Hue.php │ │ │ ├── Luminance.php │ │ │ └── Saturation.php │ │ └── Decoders │ │ │ └── StringColorDecoder.php │ └── Hsv │ │ ├── Channels │ │ ├── Hue.php │ │ ├── Value.php │ │ └── Saturation.php │ │ └── Decoders │ │ └── StringColorDecoder.php ├── Interfaces │ ├── SpecializedInterface.php │ ├── ProfileInterface.php │ ├── EncoderInterface.php │ ├── AnalyzerInterface.php │ ├── DecoderInterface.php │ ├── ModifierInterface.php │ ├── InputHandlerInterface.php │ ├── EncodedImageInterface.php │ ├── ColorspaceInterface.php │ ├── ColorProcessorInterface.php │ ├── SpecializableInterface.php │ ├── FileInterface.php │ ├── DrawableFactoryInterface.php │ ├── ResolutionInterface.php │ ├── PointInterface.php │ ├── ColorChannelInterface.php │ ├── FontProcessorInterface.php │ ├── DrawableInterface.php │ ├── CoreInterface.php │ └── CollectionInterface.php ├── Modifiers │ ├── ScaleModifier.php │ ├── CoverDownModifier.php │ ├── ScaleDownModifier.php │ ├── ResizeDownModifier.php │ ├── ResizeCanvasRelativeModifier.php │ ├── FlipModifier.php │ ├── FlopModifier.php │ ├── InvertModifier.php │ ├── GreyscaleModifier.php │ ├── AlignRotationModifier.php │ ├── ProfileRemovalModifier.php │ ├── BlurModifier.php │ ├── GammaModifier.php │ ├── PixelateModifier.php │ ├── SharpenModifier.php │ ├── BrightnessModifier.php │ ├── SliceAnimationModifier.php │ ├── ProfileModifier.php │ ├── ContrastModifier.php │ ├── TrimModifier.php │ ├── ResolutionModifier.php │ ├── ResizeModifier.php │ ├── DrawEllipseModifier.php │ ├── DrawPolygonModifier.php │ ├── QuantizeColorsModifier.php │ ├── ColorizeModifier.php │ ├── DrawPixelModifier.php │ ├── FillModifier.php │ ├── RotateModifier.php │ ├── DrawLineModifier.php │ ├── PadModifier.php │ ├── DrawBezierModifier.php │ ├── DrawRectangleModifier.php │ ├── CropModifier.php │ ├── ColorspaceModifier.php │ ├── ContainModifier.php │ ├── PlaceModifier.php │ ├── CoverModifier.php │ ├── AbstractDrawModifier.php │ ├── ResizeCanvasModifier.php │ ├── RemoveAnimationModifier.php │ └── BlendTransparencyModifier.php ├── Exceptions │ ├── ColorException.php │ ├── DriverException.php │ ├── FontException.php │ ├── InputException.php │ ├── DecoderException.php │ ├── EncoderException.php │ ├── GeometryException.php │ ├── RuntimeException.php │ ├── AnimationException.php │ ├── NotWritableException.php │ └── NotSupportedException.php ├── Analyzers │ ├── WidthAnalyzer.php │ ├── HeightAnalyzer.php │ ├── ProfileAnalyzer.php │ ├── ColorspaceAnalyzer.php │ ├── ResolutionAnalyzer.php │ ├── PixelColorsAnalyzer.php │ └── PixelColorAnalyzer.php ├── Decoders │ ├── Base64ImageDecoder.php │ ├── BinaryImageDecoder.php │ ├── DataUriImageDecoder.php │ ├── FilePathImageDecoder.php │ ├── NativeObjectDecoder.php │ ├── FilePointerImageDecoder.php │ ├── SplFileInfoImageDecoder.php │ ├── EncodedImageObjectDecoder.php │ ├── ColorObjectDecoder.php │ └── ImageObjectDecoder.php ├── Encoders │ ├── BmpEncoder.php │ ├── GifEncoder.php │ ├── PngEncoder.php │ ├── AvifEncoder.php │ ├── HeicEncoder.php │ ├── TiffEncoder.php │ ├── WebpEncoder.php │ ├── Jpeg2000Encoder.php │ ├── JpegEncoder.php │ ├── AutoEncoder.php │ └── FilePathEncoder.php ├── Drivers │ ├── Specializable.php │ ├── SpecializableEncoder.php │ ├── Gd │ │ ├── Modifiers │ │ │ ├── ScaleModifier.php │ │ │ ├── ResizeDownModifier.php │ │ │ ├── ResizeCanvasRelativeModifier.php │ │ │ ├── CoverDownModifier.php │ │ │ ├── ScaleDownModifier.php │ │ │ ├── PadModifier.php │ │ │ ├── FlipModifier.php │ │ │ ├── FlopModifier.php │ │ │ ├── GammaModifier.php │ │ │ ├── InvertModifier.php │ │ │ ├── GreyscaleModifier.php │ │ │ ├── ContrastModifier.php │ │ │ ├── PixelateModifier.php │ │ │ ├── RemoveAnimationModifier.php │ │ │ ├── ProfileModifier.php │ │ │ ├── ProfileRemovalModifier.php │ │ │ ├── BrightnessModifier.php │ │ │ ├── BlurModifier.php │ │ │ ├── ResolutionModifier.php │ │ │ ├── SliceAnimationModifier.php │ │ │ ├── ResizeCanvasModifier.php │ │ │ ├── ColorizeModifier.php │ │ │ ├── ColorspaceModifier.php │ │ │ ├── DrawPixelModifier.php │ │ │ ├── BlendTransparencyModifier.php │ │ │ ├── SharpenModifier.php │ │ │ ├── DrawLineModifier.php │ │ │ ├── AlignRotationModifier.php │ │ │ └── CoverModifier.php │ │ ├── Analyzers │ │ │ ├── WidthAnalyzer.php │ │ │ ├── HeightAnalyzer.php │ │ │ ├── ColorspaceAnalyzer.php │ │ │ ├── ResolutionAnalyzer.php │ │ │ ├── PixelColorsAnalyzer.php │ │ │ └── PixelColorAnalyzer.php │ │ ├── Encoders │ │ │ ├── BmpEncoder.php │ │ │ ├── AvifEncoder.php │ │ │ ├── WebpEncoder.php │ │ │ ├── JpegEncoder.php │ │ │ └── PngEncoder.php │ │ └── Decoders │ │ │ ├── EncodedImageObjectDecoder.php │ │ │ ├── Base64ImageDecoder.php │ │ │ ├── SplFileInfoImageDecoder.php │ │ │ ├── FilePointerImageDecoder.php │ │ │ └── DataUriImageDecoder.php │ ├── Imagick │ │ ├── Modifiers │ │ │ ├── ScaleModifier.php │ │ │ ├── ScaleDownModifier.php │ │ │ ├── ResizeDownModifier.php │ │ │ ├── ResizeCanvasRelativeModifier.php │ │ │ ├── CoverDownModifier.php │ │ │ ├── FlipModifier.php │ │ │ ├── FlopModifier.php │ │ │ ├── GammaModifier.php │ │ │ ├── InvertModifier.php │ │ │ ├── BlurModifier.php │ │ │ ├── GreyscaleModifier.php │ │ │ ├── ResolutionModifier.php │ │ │ ├── PadModifier.php │ │ │ ├── SharpenModifier.php │ │ │ ├── BrightnessModifier.php │ │ │ ├── ContrastModifier.php │ │ │ ├── ProfileModifier.php │ │ │ ├── SliceAnimationModifier.php │ │ │ ├── ResizeCanvasModifier.php │ │ │ ├── ProfileRemovalModifier.php │ │ │ ├── RemoveAnimationModifier.php │ │ │ ├── RotateModifier.php │ │ │ ├── TrimModifier.php │ │ │ ├── DrawPixelModifier.php │ │ │ ├── StripMetaModifier.php │ │ │ ├── PixelateModifier.php │ │ │ ├── CoverModifier.php │ │ │ ├── ResizeModifier.php │ │ │ ├── BlendTransparencyModifier.php │ │ │ ├── QuantizeColorsModifier.php │ │ │ ├── ColorizeModifier.php │ │ │ ├── DrawLineModifier.php │ │ │ ├── PlaceModifier.php │ │ │ ├── DrawEllipseModifier.php │ │ │ ├── DrawRectangleModifier.php │ │ │ └── ColorspaceModifier.php │ │ ├── Analyzers │ │ │ ├── WidthAnalyzer.php │ │ │ ├── HeightAnalyzer.php │ │ │ ├── PixelColorsAnalyzer.php │ │ │ ├── ResolutionAnalyzer.php │ │ │ ├── ColorspaceAnalyzer.php │ │ │ ├── ProfileAnalyzer.php │ │ │ └── PixelColorAnalyzer.php │ │ ├── Decoders │ │ │ ├── Base64ImageDecoder.php │ │ │ ├── SplFileInfoImageDecoder.php │ │ │ ├── EncodedImageObjectDecoder.php │ │ │ ├── FilePointerImageDecoder.php │ │ │ ├── DataUriImageDecoder.php │ │ │ ├── FilePathImageDecoder.php │ │ │ └── BinaryImageDecoder.php │ │ ├── Encoders │ │ │ ├── BmpEncoder.php │ │ │ ├── GifEncoder.php │ │ │ ├── HeicEncoder.php │ │ │ ├── TiffEncoder.php │ │ │ ├── AvifEncoder.php │ │ │ ├── PngEncoder.php │ │ │ ├── Jpeg2000Encoder.php │ │ │ └── WebpEncoder.php │ │ └── ColorProcessor.php │ ├── SpecializableAnalyzer.php │ ├── SpecializableModifier.php │ ├── AbstractFrame.php │ ├── SpecializableDecoder.php │ └── AbstractEncoder.php ├── Geometry │ ├── Traits │ │ ├── HasBackgroundColor.php │ │ └── HasBorder.php │ ├── Pixel.php │ ├── Circle.php │ └── Factories │ │ └── Drawable.php ├── ModifierStack.php ├── Traits │ └── CanBuildFilePointer.php ├── EncodedImage.php ├── Typography │ └── TextBlock.php └── Config.php ├── LICENSE └── composer.json /src/Colors/Cmyk/Channels/Key.php: -------------------------------------------------------------------------------- 1 | normalize(), 6)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Interfaces/EncoderInterface.php: -------------------------------------------------------------------------------- 1 | size()->scale($this->width, $this->height); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ScaleModifier.php: -------------------------------------------------------------------------------- 1 | size()->scale($this->width, $this->height); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/ResizeDownModifier.php: -------------------------------------------------------------------------------- 1 | size()->resizeDown($this->width, $this->height); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ScaleDownModifier.php: -------------------------------------------------------------------------------- 1 | size()->scaleDown($this->width, $this->height); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ResizeDownModifier.php: -------------------------------------------------------------------------------- 1 | size()->resizeDown($this->width, $this->height); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/ResizeCanvasRelativeModifier.php: -------------------------------------------------------------------------------- 1 | drawable; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Modifiers/DrawPolygonModifier.php: -------------------------------------------------------------------------------- 1 | drawable; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Modifiers/QuantizeColorsModifier.php: -------------------------------------------------------------------------------- 1 | resizeDown($this->width, $this->height); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/CoverDownModifier.php: -------------------------------------------------------------------------------- 1 | resizeDown($this->width, $this->height); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Modifiers/DrawPixelModifier.php: -------------------------------------------------------------------------------- 1 | analyze($this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Interfaces/EncodedImageInterface.php: -------------------------------------------------------------------------------- 1 | modify($this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Colors/Profile.php: -------------------------------------------------------------------------------- 1 | core()->native()->getImageWidth(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Interfaces/ColorspaceInterface.php: -------------------------------------------------------------------------------- 1 | $normalized 18 | */ 19 | public function colorFromNormalized(array $normalized): ColorInterface; 20 | } 21 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Analyzers/HeightAnalyzer.php: -------------------------------------------------------------------------------- 1 | core()->native()->getImageHeight(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Modifiers/FillModifier.php: -------------------------------------------------------------------------------- 1 | position instanceof PointInterface; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/ScaleDownModifier.php: -------------------------------------------------------------------------------- 1 | size()->scaleDown($this->width, $this->height); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Modifiers/RotateModifier.php: -------------------------------------------------------------------------------- 1 | angle, 360); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Encoders/JpegEncoder.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | public function __debugInfo(): array 17 | { 18 | return [ 19 | 'delay' => $this->delay(), 20 | 'left' => $this->offsetLeft(), 21 | 'top' => $this->offsetTop(), 22 | 'dispose' => $this->dispose(), 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/FlipModifier.php: -------------------------------------------------------------------------------- 1 | native()->flipImage(); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Encoders/AutoEncoder.php: -------------------------------------------------------------------------------- 1 | encode( 20 | $this->encoderByMediaType( 21 | $image->origin()->mediaType() 22 | ) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/FlopModifier.php: -------------------------------------------------------------------------------- 1 | native()->flopImage(); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Interfaces/ColorProcessorInterface.php: -------------------------------------------------------------------------------- 1 | drawable; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Modifiers/PadModifier.php: -------------------------------------------------------------------------------- 1 | size() 15 | ->containMax( 16 | $this->width, 17 | $this->height 18 | ) 19 | ->alignPivotTo( 20 | $this->getResizeSize($image), 21 | $this->position 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Analyzers/WidthAnalyzer.php: -------------------------------------------------------------------------------- 1 | core()->native()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Analyzers/HeightAnalyzer.php: -------------------------------------------------------------------------------- 1 | core()->native()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/GammaModifier.php: -------------------------------------------------------------------------------- 1 | native()->gammaImage($this->gamma); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/InvertModifier.php: -------------------------------------------------------------------------------- 1 | native()->negateImage(false); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Modifiers/DrawBezierModifier.php: -------------------------------------------------------------------------------- 1 | drawable; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Modifiers/DrawRectangleModifier.php: -------------------------------------------------------------------------------- 1 | drawable; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/PadModifier.php: -------------------------------------------------------------------------------- 1 | size() 15 | ->containMax( 16 | $this->width, 17 | $this->height 18 | ) 19 | ->alignPivotTo( 20 | $this->getResizeSize($image), 21 | $this->position 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/BlurModifier.php: -------------------------------------------------------------------------------- 1 | native()->blurImage($this->amount, 0.5 * $this->amount); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/GreyscaleModifier.php: -------------------------------------------------------------------------------- 1 | native()->modulateImage(100, 0, 100); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ResolutionModifier.php: -------------------------------------------------------------------------------- 1 | core()->native(); 16 | $imagick->setImageResolution($this->x, $this->y); 17 | 18 | return $image; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/PadModifier.php: -------------------------------------------------------------------------------- 1 | size() 15 | ->containMax( 16 | $this->width, 17 | $this->height 18 | ) 19 | ->alignPivotTo( 20 | $this->getResizeSize($image), 21 | $this->position 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Analyzers/PixelColorsAnalyzer.php: -------------------------------------------------------------------------------- 1 | colorspace(); 16 | 17 | foreach ($image as $frame) { 18 | $colors->push( 19 | parent::colorAt($colorspace, $frame->native()) 20 | ); 21 | } 22 | 23 | return $colors; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/SharpenModifier.php: -------------------------------------------------------------------------------- 1 | native()->unsharpMaskImage(1, 1, $this->amount / 6.25, 0); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/BrightnessModifier.php: -------------------------------------------------------------------------------- 1 | native()->modulateImage(100 + $this->level, 100, 100); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Analyzers/ColorspaceAnalyzer.php: -------------------------------------------------------------------------------- 1 | native()->sigmoidalContrastImage($this->level > 0, abs($this->level / 4), 0); 17 | } 18 | 19 | return $image; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php: -------------------------------------------------------------------------------- 1 | core()->native())); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/FlipModifier.php: -------------------------------------------------------------------------------- 1 | native(), IMG_FLIP_VERTICAL); 22 | } 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/FlopModifier.php: -------------------------------------------------------------------------------- 1 | native(), IMG_FLIP_HORIZONTAL); 22 | } 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Decoders/ColorObjectDecoder.php: -------------------------------------------------------------------------------- 1 | native(), 1, $this->gamma); 22 | } 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/InvertModifier.php: -------------------------------------------------------------------------------- 1 | native(), IMG_FILTER_NEGATE); 22 | } 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Decoders/Base64ImageDecoder.php: -------------------------------------------------------------------------------- 1 | isValidBase64($input)) { 21 | throw new DecoderException('Unable to decode input'); 22 | } 23 | 24 | return parent::decode(base64_decode((string) $input)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/GreyscaleModifier.php: -------------------------------------------------------------------------------- 1 | native(), IMG_FILTER_GRAYSCALE); 22 | } 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Analyzers/PixelColorsAnalyzer.php: -------------------------------------------------------------------------------- 1 | colorspace(); 21 | 22 | foreach ($image as $frame) { 23 | $colors->push( 24 | parent::colorAt($colorspace, $frame->native()) 25 | ); 26 | } 27 | 28 | return $colors; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/ContrastModifier.php: -------------------------------------------------------------------------------- 1 | native(), IMG_FILTER_CONTRAST, ($this->level * -1)); 22 | } 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/PixelateModifier.php: -------------------------------------------------------------------------------- 1 | native(), IMG_FILTER_PIXELATE, $this->size, true); 22 | } 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php: -------------------------------------------------------------------------------- 1 | core()->setNative( 21 | $this->selectedFrame($image)->native() 22 | ); 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/ProfileModifier.php: -------------------------------------------------------------------------------- 1 | getRealPath()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Encoders/BmpEncoder.php: -------------------------------------------------------------------------------- 1 | createEncodedImage(function ($pointer) use ($image): void { 22 | imagebmp($image->core()->native(), $pointer, false); 23 | }, 'image/bmp'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Decoders/EncodedImageObjectDecoder.php: -------------------------------------------------------------------------------- 1 | toString()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/BrightnessModifier.php: -------------------------------------------------------------------------------- 1 | native(), IMG_FILTER_BRIGHTNESS, intval($this->level * 2.55)); 22 | } 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Encoders/AvifEncoder.php: -------------------------------------------------------------------------------- 1 | createEncodedImage(function ($pointer) use ($image): void { 22 | imageavif($image->core()->native(), $pointer, $this->quality); 23 | }, 'image/avif'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Decoders/EncodedImageObjectDecoder.php: -------------------------------------------------------------------------------- 1 | toString()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Drivers/SpecializableDecoder.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function specializable(): array; 18 | 19 | /** 20 | * Set the driver for which the object is specialized 21 | * 22 | * @throws DriverException 23 | */ 24 | public function setDriver(DriverInterface $driver): self; 25 | 26 | /** 27 | * Return the driver for which the object was specialized 28 | */ 29 | public function driver(): DriverInterface; 30 | } 31 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/BlurModifier.php: -------------------------------------------------------------------------------- 1 | amount; $i++) { 22 | imagefilter($frame->native(), IMG_FILTER_GAUSSIAN_BLUR); 23 | } 24 | } 25 | 26 | return $image; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Decoders/Base64ImageDecoder.php: -------------------------------------------------------------------------------- 1 | isValidBase64($input)) { 22 | throw new DecoderException('Unable to decode input'); 23 | } 24 | 25 | return parent::decode(base64_decode((string) $input)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Analyzers/ResolutionAnalyzer.php: -------------------------------------------------------------------------------- 1 | core()->native(); 17 | $imageResolution = $imagick->getImageResolution(); 18 | 19 | return new Resolution( 20 | $imageResolution['x'], 21 | $imageResolution['y'], 22 | $imagick->getImageUnits(), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/ResolutionModifier.php: -------------------------------------------------------------------------------- 1 | x)); 21 | $y = intval(round($this->y)); 22 | 23 | foreach ($image as $frame) { 24 | imageresolution($frame->native(), $x, $y); 25 | } 26 | 27 | return $image; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ProfileModifier.php: -------------------------------------------------------------------------------- 1 | core()->native(); 17 | $result = $imagick->profileImage('icc', (string) $this->profile); 18 | 19 | if ($result === false) { 20 | throw new ColorException('ICC color profile could not be set.'); 21 | } 22 | 23 | return $image; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/SliceAnimationModifier.php: -------------------------------------------------------------------------------- 1 | offset >= $image->count()) { 17 | throw new AnimationException('Offset is not in the range of frames.'); 18 | } 19 | 20 | $image->core()->slice($this->offset, $this->length); 21 | 22 | return $image; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Interfaces/FileInterface.php: -------------------------------------------------------------------------------- 1 | getRealPath()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Analyzers/ColorspaceAnalyzer.php: -------------------------------------------------------------------------------- 1 | core()->native()->getImageColorspace()) { 19 | Imagick::COLORSPACE_CMYK => new CmykColorspace(), 20 | default => new RgbColorspace(), 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ResizeCanvasModifier.php: -------------------------------------------------------------------------------- 1 | cropSize($image); 16 | 17 | $image->modify(new CropModifier( 18 | $cropSize->width(), 19 | $cropSize->height(), 20 | $cropSize->pivot()->x(), 21 | $cropSize->pivot()->y(), 22 | $this->background, 23 | )); 24 | 25 | return $image; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Colors/Rgb/Decoders/TransparentColorDecoder.php: -------------------------------------------------------------------------------- 1 | core()->native()->getImageProfiles('icc'); 18 | 19 | if (!array_key_exists('icc', $profiles)) { 20 | throw new ColorException('No ICC profile found in image.'); 21 | } 22 | 23 | return new Profile($profiles['icc']); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ProfileRemovalModifier.php: -------------------------------------------------------------------------------- 1 | core()->native(); 17 | $result = $imagick->profileImage('icc', null); 18 | 19 | if ($result === false) { 20 | throw new ColorException('ICC color profile could not be removed.'); 21 | } 22 | 23 | return $image; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php: -------------------------------------------------------------------------------- 1 | selectedFrame($image); 19 | $imagick->addImage($frame->native()->getImage()); 20 | 21 | // set new imagick to image 22 | $image->core()->setNative($imagick); 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/RotateModifier.php: -------------------------------------------------------------------------------- 1 | driver()->colorProcessor($image->colorspace())->colorToNative( 16 | $this->driver()->handleInput($this->background) 17 | ); 18 | 19 | foreach ($image as $frame) { 20 | $frame->native()->rotateImage( 21 | $background, 22 | $this->rotationAngle() * -1 23 | ); 24 | } 25 | 26 | return $image; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/TrimModifier.php: -------------------------------------------------------------------------------- 1 | isAnimated()) { 17 | throw new NotSupportedException('Trim modifier cannot be applied to animated images.'); 18 | } 19 | 20 | $imagick = $image->core()->native(); 21 | $imagick->trimImage(($this->tolerance / 100 * $imagick->getQuantum()) / 1.5); 22 | $imagick->setImagePage(0, 0, 0, 0); 23 | 24 | return $image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Interfaces/DrawableFactoryInterface.php: -------------------------------------------------------------------------------- 1 | quality === 100 && defined('IMG_WEBP_LOSSLESS') ? IMG_WEBP_LOSSLESS : $this->quality; 22 | 23 | return $this->createEncodedImage(function ($pointer) use ($image, $quality): void { 24 | imagewebp($image->core()->native(), $pointer, $quality); 25 | }, 'image/webp'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/SliceAnimationModifier.php: -------------------------------------------------------------------------------- 1 | offset >= $image->count()) { 22 | throw new AnimationException('Offset is not in the range of frames.'); 23 | } 24 | 25 | $image->core()->slice($this->offset, $this->length); 26 | 27 | return $image; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Decoders/FilePointerImageDecoder.php: -------------------------------------------------------------------------------- 1 | backgroundColor = $color; 19 | 20 | return $this; 21 | } 22 | 23 | /** 24 | * {@inheritdoc} 25 | * 26 | * @see DrawableInterface::backgroundColor() 27 | */ 28 | public function backgroundColor(): mixed 29 | { 30 | return $this->backgroundColor; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | * 36 | * @see DrawableInterface::hasBackgroundColor() 37 | */ 38 | public function hasBackgroundColor(): bool 39 | { 40 | return !empty($this->backgroundColor); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/ResizeCanvasModifier.php: -------------------------------------------------------------------------------- 1 | cropSize($image); 21 | 22 | $image->modify(new CropModifier( 23 | $cropSize->width(), 24 | $cropSize->height(), 25 | $cropSize->pivot()->x(), 26 | $cropSize->pivot()->y(), 27 | $this->background, 28 | )); 29 | 30 | return $image; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Decoders/FilePointerImageDecoder.php: -------------------------------------------------------------------------------- 1 | red * 2.55); 22 | $green = (int) round($this->green * 2.55); 23 | $blue = (int) round($this->blue * 2.55); 24 | 25 | foreach ($image as $frame) { 26 | imagefilter($frame->native(), IMG_FILTER_COLORIZE, $red, $green, $blue); 27 | } 28 | 29 | return $image; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Geometry/Pixel.php: -------------------------------------------------------------------------------- 1 | background = $background; 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | * 39 | * @see DrawableInterface::backgroundColor() 40 | */ 41 | public function backgroundColor(): ColorInterface 42 | { 43 | return $this->background; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/ColorspaceModifier.php: -------------------------------------------------------------------------------- 1 | targetColorspace() instanceof RgbColorspace)) { 23 | throw new NotSupportedException( 24 | 'Only RGB colorspace is supported by GD driver.' 25 | ); 26 | } 27 | 28 | return $image; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/DrawPixelModifier.php: -------------------------------------------------------------------------------- 1 | driver()->colorProcessor($image->colorspace())->colorToNative( 17 | $this->driver()->handleInput($this->color) 18 | ); 19 | 20 | $pixel = new ImagickDraw(); 21 | $pixel->setFillColor($color); 22 | $pixel->point($this->position->x(), $this->position->y()); 23 | 24 | foreach ($image as $frame) { 25 | $frame->native()->drawImage($pixel); 26 | } 27 | 28 | return $image; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Encoders/BmpEncoder.php: -------------------------------------------------------------------------------- 1 | core()->native(); 22 | $imagick->setFormat($format); 23 | $imagick->setImageFormat($format); 24 | $imagick->setCompression($compression); 25 | $imagick->setImageCompression($compression); 26 | 27 | return new EncodedImage($imagick->getImagesBlob(), 'image/bmp'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Decoders/DataUriImageDecoder.php: -------------------------------------------------------------------------------- 1 | parseDataUri($input); 25 | 26 | if (!$uri->isValid()) { 27 | throw new DecoderException('Unable to decode input'); 28 | } 29 | 30 | if ($uri->isBase64Encoded()) { 31 | return parent::decode(base64_decode($uri->data())); 32 | } 33 | 34 | return parent::decode(urldecode($uri->data())); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ModifierStack.php: -------------------------------------------------------------------------------- 1 | $modifiers 16 | * @return void 17 | */ 18 | public function __construct(protected array $modifiers) 19 | { 20 | // 21 | } 22 | 23 | /** 24 | * Apply all modifiers in stack to the given image 25 | */ 26 | public function apply(ImageInterface $image): ImageInterface 27 | { 28 | foreach ($this->modifiers as $modifier) { 29 | $modifier->apply($image); 30 | } 31 | 32 | return $image; 33 | } 34 | 35 | /** 36 | * Append new modifier to the stack 37 | */ 38 | public function push(ModifierInterface $modifier): self 39 | { 40 | $this->modifiers[] = $modifier; 41 | 42 | return $this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/StripMetaModifier.php: -------------------------------------------------------------------------------- 1 | core()->native()->getImageProfiles('icc'); 23 | 24 | // remove meta data 25 | $image->core()->native()->stripImage(); 26 | $image->setExif(new Collection()); 27 | 28 | if ($profiles !== []) { 29 | // re-apply icc profiles 30 | $image->core()->native()->profileImage("icc", $profiles['icc']); 31 | } 32 | return $image; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/PixelateModifier.php: -------------------------------------------------------------------------------- 1 | pixelateFrame($frame); 18 | } 19 | 20 | return $image; 21 | } 22 | 23 | protected function pixelateFrame(FrameInterface $frame): void 24 | { 25 | $size = $frame->size(); 26 | 27 | $frame->native()->scaleImage( 28 | (int) round(max(1, $size->width() / $this->size)), 29 | (int) round(max(1, $size->height() / $this->size)) 30 | ); 31 | 32 | $frame->native()->scaleImage($size->width(), $size->height()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-present Oliver Vogel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/CoverModifier.php: -------------------------------------------------------------------------------- 1 | getCropSize($image); 16 | $resize = $this->getResizeSize($crop); 17 | 18 | foreach ($image as $frame) { 19 | $frame->native()->cropImage( 20 | $crop->width(), 21 | $crop->height(), 22 | $crop->pivot()->x(), 23 | $crop->pivot()->y() 24 | ); 25 | 26 | $frame->native()->scaleImage( 27 | $resize->width(), 28 | $resize->height() 29 | ); 30 | 31 | $frame->native()->setImagePage(0, 0, 0, 0); 32 | } 33 | 34 | return $image; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/DrawPixelModifier.php: -------------------------------------------------------------------------------- 1 | driver()->colorProcessor($image->colorspace())->colorToNative( 21 | $this->driver()->handleInput($this->color) 22 | ); 23 | 24 | foreach ($image as $frame) { 25 | imagealphablending($frame->native(), true); 26 | imagesetpixel( 27 | $frame->native(), 28 | $this->position->x(), 29 | $this->position->y(), 30 | $color 31 | ); 32 | } 33 | 34 | return $image; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Encoders/FilePathEncoder.php: -------------------------------------------------------------------------------- 1 | encode( 33 | $this->encoderByFileExtension( 34 | is_null($this->path) ? $image->origin()->fileExtension() : pathinfo($this->path, PATHINFO_EXTENSION) 35 | ) 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Decoders/DataUriImageDecoder.php: -------------------------------------------------------------------------------- 1 | parseDataUri($input); 26 | 27 | if (!$uri->isValid()) { 28 | throw new DecoderException('Unable to decode input'); 29 | } 30 | 31 | if ($uri->isBase64Encoded()) { 32 | return parent::decode(base64_decode($uri->data())); 33 | } 34 | 35 | return parent::decode(urldecode($uri->data())); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Interfaces/ResolutionInterface.php: -------------------------------------------------------------------------------- 1 | getAdjustedSize($image); 18 | 19 | foreach ($image as $frame) { 20 | $frame->native()->scaleImage( 21 | $resizeTo->width(), 22 | $resizeTo->height() 23 | ); 24 | } 25 | 26 | return $image; 27 | } 28 | 29 | /** 30 | * @throws RuntimeException 31 | */ 32 | protected function getAdjustedSize(ImageInterface $image): SizeInterface 33 | { 34 | return $image->size()->resize($this->width, $this->height); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Encoders/GifEncoder.php: -------------------------------------------------------------------------------- 1 | core()->native(); 22 | 23 | $imagick->setFormat($format); 24 | $imagick->setImageFormat($format); 25 | $imagick->setCompression($compression); 26 | $imagick->setImageCompression($compression); 27 | 28 | if ($this->interlaced) { 29 | $imagick->setInterlaceScheme(Imagick::INTERLACE_LINE); 30 | } 31 | 32 | return new EncodedImage($imagick->getImagesBlob(), 'image/gif'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Interfaces/PointInterface.php: -------------------------------------------------------------------------------- 1 | blendingColor($this->driver()); 22 | 23 | foreach ($image as $frame) { 24 | // create new canvas with blending color as background 25 | $modified = Cloner::cloneBlended( 26 | $frame->native(), 27 | background: $blendingColor 28 | ); 29 | 30 | // set new gd image 31 | $frame->setNative($modified); 32 | } 33 | 34 | return $image; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Traits/CanBuildFilePointer.php: -------------------------------------------------------------------------------- 1 | driver()->handleInput( 23 | $this->driver()->config()->blendingColor 24 | ); 25 | 26 | $output = Cloner::cloneBlended( 27 | $image->core()->native(), 28 | background: $blendingColor 29 | ); 30 | 31 | return $this->createEncodedImage(function ($pointer) use ($output): void { 32 | imageinterlace($output, $this->progressive); 33 | imagejpeg($output, $pointer, $this->quality); 34 | }, 'image/jpeg'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Modifiers/CropModifier.php: -------------------------------------------------------------------------------- 1 | width, $this->height); 37 | $crop->align($this->position); 38 | 39 | return $crop->alignPivotTo( 40 | $image->size(), 41 | $this->position 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Modifiers/ColorspaceModifier.php: -------------------------------------------------------------------------------- 1 | target)) { 26 | return $this->target; 27 | } 28 | 29 | if (in_array($this->target, ['rgb', 'RGB', RgbColorspace::class])) { 30 | return new RgbColorspace(); 31 | } 32 | 33 | if (in_array($this->target, ['cmyk', 'CMYK', CmykColorspace::class])) { 34 | return new CmykColorspace(); 35 | } 36 | 37 | throw new NotSupportedException('Given colorspace is not supported.'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/BlendTransparencyModifier.php: -------------------------------------------------------------------------------- 1 | blendingColor($this->driver()); 17 | 18 | // get imagickpixel from blending color 19 | $pixel = $this->driver() 20 | ->colorProcessor($image->colorspace()) 21 | ->colorToNative($blendingColor); 22 | 23 | // merge transparent areas with the background color 24 | foreach ($image as $frame) { 25 | $frame->native()->setImageBackgroundColor($pixel); 26 | $frame->native()->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE); 27 | $frame->native()->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN); 28 | } 29 | 30 | return $image; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Analyzers/PixelColorAnalyzer.php: -------------------------------------------------------------------------------- 1 | colorAt( 20 | $image->colorspace(), 21 | $image->core()->frame($this->frame_key)->native() 22 | ); 23 | } 24 | 25 | /** 26 | * @throws ColorException 27 | */ 28 | protected function colorAt(ColorspaceInterface $colorspace, Imagick $imagick): ColorInterface 29 | { 30 | return $this->driver() 31 | ->colorProcessor($colorspace) 32 | ->nativeToColor( 33 | $imagick->getImagePixelColor($this->x, $this->y) 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Encoders/HeicEncoder.php: -------------------------------------------------------------------------------- 1 | strip || (is_null($this->strip) && $this->driver()->config()->strip)) { 22 | $image->modify(new StripMetaModifier()); 23 | } 24 | 25 | $imagick = $image->core()->native(); 26 | $imagick->setFormat($format); 27 | $imagick->setImageFormat($format); 28 | $imagick->setCompressionQuality($this->quality); 29 | $imagick->setImageCompressionQuality($this->quality); 30 | 31 | return new EncodedImage($imagick->getImagesBlob(), 'image/heic'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Geometry/Circle.php: -------------------------------------------------------------------------------- 1 | setWidth($diameter); 29 | $this->setHeight($diameter); 30 | 31 | return $this; 32 | } 33 | 34 | /** 35 | * Get diameter of circle 36 | */ 37 | public function diameter(): int 38 | { 39 | return $this->width(); 40 | } 41 | 42 | /** 43 | * Set radius of circle 44 | */ 45 | public function setRadius(int $radius): self 46 | { 47 | return $this->setDiameter(intval($radius * 2)); 48 | } 49 | 50 | /** 51 | * Get radius of circle 52 | */ 53 | public function radius(): int 54 | { 55 | return intval(round($this->diameter() / 2)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Modifiers/ContainModifier.php: -------------------------------------------------------------------------------- 1 | size() 30 | ->contain( 31 | $this->width, 32 | $this->height 33 | ) 34 | ->alignPivotTo( 35 | $this->getResizeSize($image), 36 | $this->position 37 | ); 38 | } 39 | 40 | public function getResizeSize(ImageInterface $image): SizeInterface 41 | { 42 | return new Rectangle($this->width, $this->height); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/QuantizeColorsModifier.php: -------------------------------------------------------------------------------- 1 | limit <= 0) { 17 | throw new InputException('Quantization limit must be greater than 0.'); 18 | } 19 | 20 | // no color reduction if the limit is higher than the colors in the img 21 | if ($this->limit > $image->core()->native()->getImageColors()) { 22 | return $image; 23 | } 24 | 25 | foreach ($image as $frame) { 26 | $frame->native()->quantizeImage( 27 | $this->limit, 28 | $frame->native()->getImageColorspace(), 29 | 0, 30 | false, 31 | false 32 | ); 33 | } 34 | 35 | return $image; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Geometry/Factories/Drawable.php: -------------------------------------------------------------------------------- 1 | size()->movePivot( 35 | $this->position, 36 | $this->offset_x, 37 | $this->offset_y 38 | ); 39 | 40 | $watermark_size = $watermark->size()->movePivot( 41 | $this->position 42 | ); 43 | 44 | return $image_size->relativePositionTo($watermark_size); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Drivers/AbstractEncoder.php: -------------------------------------------------------------------------------- 1 | encode($this); 28 | } 29 | 30 | /** 31 | * Build new file pointer, run callback with it and return result as encoded image 32 | * 33 | * @throws RuntimeException 34 | */ 35 | protected function createEncodedImage(callable $callback, ?string $mediaType = null): EncodedImage 36 | { 37 | $pointer = $this->buildFilePointer(); 38 | $callback($pointer); 39 | 40 | return is_string($mediaType) ? new EncodedImage($pointer, $mediaType) : new EncodedImage($pointer); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/SharpenModifier.php: -------------------------------------------------------------------------------- 1 | matrix(); 21 | foreach ($image as $frame) { 22 | imageconvolution($frame->native(), $matrix, 1, 0); 23 | } 24 | 25 | return $image; 26 | } 27 | 28 | /** 29 | * Create matrix to be used by imageconvolution() 30 | * 31 | * @return array> 32 | */ 33 | private function matrix(): array 34 | { 35 | $min = $this->amount >= 10 ? $this->amount * -0.01 : 0; 36 | $max = $this->amount * -0.025; 37 | $abs = ((4 * $min + 4 * $max) * -1) + 1; 38 | 39 | return [ 40 | [$min, $max, $min], 41 | [$max, $abs, $max], 42 | [$min, $max, $min] 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/DrawLineModifier.php: -------------------------------------------------------------------------------- 1 | driver()->colorProcessor($image->colorspace())->colorToNative( 20 | $this->backgroundColor() 21 | ); 22 | 23 | foreach ($image as $frame) { 24 | imagealphablending($frame->native(), true); 25 | imageantialias($frame->native(), true); 26 | imagesetthickness($frame->native(), $this->drawable->width()); 27 | imageline( 28 | $frame->native(), 29 | $this->drawable->start()->x(), 30 | $this->drawable->start()->y(), 31 | $this->drawable->end()->x(), 32 | $this->drawable->end()->y(), 33 | $color 34 | ); 35 | } 36 | 37 | return $image; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Colors/Cmyk/Decoders/StringColorDecoder.php: -------------------------------------------------------------------------------- 1 | [0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; 26 | if (preg_match($pattern, $input, $matches) != 1) { 27 | throw new DecoderException('Unable to decode input'); 28 | } 29 | 30 | $values = array_map(function (string $value): int { 31 | return intval(round(floatval(trim(str_replace('%', '', $value))))); 32 | }, [$matches['c'], $matches['m'], $matches['y'], $matches['k']]); 33 | 34 | return new Color(...$values); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ColorizeModifier.php: -------------------------------------------------------------------------------- 1 | normalizeLevel($this->red); 17 | $green = $this->normalizeLevel($this->green); 18 | $blue = $this->normalizeLevel($this->blue); 19 | 20 | foreach ($image as $frame) { 21 | $qrange = $frame->native()->getQuantumRange(); 22 | $frame->native()->levelImage(0, $red, $qrange['quantumRangeLong'], Imagick::CHANNEL_RED); 23 | $frame->native()->levelImage(0, $green, $qrange['quantumRangeLong'], Imagick::CHANNEL_GREEN); 24 | $frame->native()->levelImage(0, $blue, $qrange['quantumRangeLong'], Imagick::CHANNEL_BLUE); 25 | } 26 | 27 | return $image; 28 | } 29 | 30 | private function normalizeLevel(int $level): int 31 | { 32 | return $level > 0 ? intval(round($level / 5)) : intval(round(($level + 100) / 100)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/DrawLineModifier.php: -------------------------------------------------------------------------------- 1 | setStrokeWidth($this->drawable->width()); 22 | $drawing->setFillOpacity(0); 23 | $drawing->setStrokeColor( 24 | $this->driver()->colorProcessor($image->colorspace())->colorToNative( 25 | $this->backgroundColor() 26 | ) 27 | ); 28 | 29 | $drawing->line( 30 | $this->drawable->start()->x(), 31 | $this->drawable->start()->y(), 32 | $this->drawable->end()->x(), 33 | $this->drawable->end()->y(), 34 | ); 35 | 36 | foreach ($image as $frame) { 37 | $frame->native()->drawImage($drawing); 38 | } 39 | 40 | return $image; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Modifiers/CoverModifier.php: -------------------------------------------------------------------------------- 1 | size(); 34 | $crop = new Rectangle($this->width, $this->height); 35 | 36 | return $crop->contain( 37 | $imagesize->width(), 38 | $imagesize->height() 39 | )->alignPivotTo($imagesize, $this->position); 40 | } 41 | 42 | /** 43 | * @throws RuntimeException 44 | */ 45 | public function getResizeSize(SizeInterface $size): SizeInterface 46 | { 47 | return $size->resize($this->width, $this->height); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Encoders/TiffEncoder.php: -------------------------------------------------------------------------------- 1 | strip || (is_null($this->strip) && $this->driver()->config()->strip)) { 22 | $image->modify(new StripMetaModifier()); 23 | } 24 | 25 | $imagick = $image->core()->native(); 26 | $imagick->setFormat($format); 27 | $imagick->setImageFormat($format); 28 | $imagick->setCompression($imagick->getImageCompression()); 29 | $imagick->setImageCompression($imagick->getImageCompression()); 30 | $imagick->setCompressionQuality($this->quality); 31 | $imagick->setImageCompressionQuality($this->quality); 32 | 33 | return new EncodedImage($imagick->getImagesBlob(), 'image/tiff'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Decoders/FilePathImageDecoder.php: -------------------------------------------------------------------------------- 1 | isFile($input)) { 23 | throw new DecoderException('Unable to decode input'); 24 | } 25 | 26 | try { 27 | $imagick = new Imagick(); 28 | $imagick->readImage($input); 29 | } catch (ImagickException) { 30 | throw new DecoderException('Unable to decode input'); 31 | } 32 | 33 | // decode image 34 | $image = parent::decode($imagick); 35 | 36 | // set file path on origin 37 | $image->origin()->setFilePath($input); 38 | 39 | // extract exif data for the appropriate formats 40 | if (in_array($imagick->getImageFormat(), ['JPEG', 'TIFF', 'TIF'])) { 41 | $image->setExif($this->extractExifData($input)); 42 | } 43 | 44 | return $image; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Modifiers/AbstractDrawModifier.php: -------------------------------------------------------------------------------- 1 | driver()->handleInput($this->drawable()->backgroundColor()); 27 | } catch (DecoderException) { 28 | return $this->driver()->handleInput('transparent'); 29 | } 30 | 31 | return $color; 32 | } 33 | 34 | /** 35 | * @throws RuntimeException 36 | */ 37 | public function borderColor(): ColorInterface 38 | { 39 | try { 40 | $color = $this->driver()->handleInput($this->drawable()->borderColor()); 41 | } catch (DecoderException) { 42 | return $this->driver()->handleInput('transparent'); 43 | } 44 | 45 | return $color; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/PlaceModifier.php: -------------------------------------------------------------------------------- 1 | driver()->handleInput($this->element); 17 | $position = $this->getPosition($image, $watermark); 18 | 19 | // set opacity of watermark 20 | if ($this->opacity < 100) { 21 | $watermark->core()->native()->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET); 22 | $watermark->core()->native()->evaluateImage( 23 | Imagick::EVALUATE_DIVIDE, 24 | $this->opacity > 0 ? 100 / $this->opacity : 1000, 25 | Imagick::CHANNEL_ALPHA, 26 | ); 27 | } 28 | 29 | foreach ($image as $frame) { 30 | $frame->native()->compositeImage( 31 | $watermark->core()->native(), 32 | Imagick::COMPOSITE_DEFAULT, 33 | $position->x(), 34 | $position->y() 35 | ); 36 | } 37 | 38 | return $image; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intervention/image", 3 | "description": "PHP Image Processing", 4 | "homepage": "https://image.intervention.io", 5 | "keywords": [ 6 | "image", 7 | "gd", 8 | "imagick", 9 | "watermark", 10 | "thumbnail", 11 | "resize" 12 | ], 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "Oliver Vogel", 17 | "email": "oliver@intervention.io", 18 | "homepage": "https://intervention.io" 19 | } 20 | ], 21 | "require": { 22 | "php": "^8.1", 23 | "ext-mbstring": "*", 24 | "intervention/gif": "^4.2" 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", 28 | "mockery/mockery": "^1.6", 29 | "phpstan/phpstan": "^2.1", 30 | "squizlabs/php_codesniffer": "^3.8", 31 | "slevomat/coding-standard": "~8.0" 32 | }, 33 | "suggest": { 34 | "ext-exif": "Recommended to be able to read EXIF data properly." 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "Intervention\\Image\\": "src" 39 | } 40 | }, 41 | "autoload-dev": { 42 | "psr-4": { 43 | "Intervention\\Image\\Tests\\": "tests" 44 | } 45 | }, 46 | "config": { 47 | "allow-plugins": { 48 | "dealerdirect/phpcodesniffer-composer-installer": true 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Colors/Hsl/Decoders/StringColorDecoder.php: -------------------------------------------------------------------------------- 1 | [0-9\.]+), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; 26 | if (preg_match($pattern, $input, $matches) != 1) { 27 | throw new DecoderException('Unable to decode input'); 28 | } 29 | 30 | $values = array_map(function (string $value): int { 31 | return match (strpos($value, '%')) { 32 | false => intval(trim($value)), 33 | default => intval(trim(str_replace('%', '', $value))), 34 | }; 35 | }, [$matches['h'], $matches['s'], $matches['l']]); 36 | 37 | return new Color(...$values); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Encoders/AvifEncoder.php: -------------------------------------------------------------------------------- 1 | strip || (is_null($this->strip) && $this->driver()->config()->strip)) { 24 | $image->modify(new StripMetaModifier()); 25 | } 26 | 27 | $imagick = $image->core()->native(); 28 | $imagick->setFormat($format); 29 | $imagick->setImageFormat($format); 30 | $imagick->setCompression($compression); 31 | $imagick->setImageCompression($compression); 32 | $imagick->setCompressionQuality($this->quality); 33 | $imagick->setImageCompressionQuality($this->quality); 34 | 35 | return new EncodedImage($imagick->getImagesBlob(), 'image/avif'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Colors/Hsv/Decoders/StringColorDecoder.php: -------------------------------------------------------------------------------- 1 | [0-9\.]+), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i'; 26 | if (preg_match($pattern, $input, $matches) != 1) { 27 | throw new DecoderException('Unable to decode input'); 28 | } 29 | 30 | $values = array_map(function (string $value): int { 31 | return match (strpos($value, '%')) { 32 | false => intval(trim($value)), 33 | default => intval(trim(str_replace('%', '', $value))), 34 | }; 35 | }, [$matches['h'], $matches['s'], $matches['v']]); 36 | 37 | return new Color(...$values); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Decoders/BinaryImageDecoder.php: -------------------------------------------------------------------------------- 1 | readImageBlob($input); 30 | } catch (ImagickException) { 31 | throw new DecoderException('Unable to decode input'); 32 | } 33 | 34 | // decode image 35 | $image = parent::decode($imagick); 36 | 37 | // get media type enum from string media type 38 | $format = Format::tryCreate($image->origin()->mediaType()); 39 | 40 | // extract exif data for appropriate formats 41 | if (in_array($format, [Format::JPEG, Format::TIFF])) { 42 | $image->setExif($this->extractExifData($input)); 43 | } 44 | 45 | return $image; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Interfaces/ColorChannelInterface.php: -------------------------------------------------------------------------------- 1 | indexed) { 24 | // reduce colors 25 | $output = clone $image; 26 | $output->reduceColors(256); 27 | 28 | $output = $output->core()->native(); 29 | $output->setFormat('PNG'); 30 | $output->setImageFormat('PNG'); 31 | } else { 32 | $output = clone $image->core()->native(); 33 | $output->setFormat('PNG32'); 34 | $output->setImageFormat('PNG32'); 35 | } 36 | 37 | $output->setCompression(Imagick::COMPRESSION_ZIP); 38 | $output->setImageCompression(Imagick::COMPRESSION_ZIP); 39 | 40 | if ($this->interlaced) { 41 | $output->setInterlaceScheme(Imagick::INTERLACE_LINE); 42 | } 43 | 44 | return new EncodedImage($output->getImagesBlob(), 'image/png'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Encoders/Jpeg2000Encoder.php: -------------------------------------------------------------------------------- 1 | strip || (is_null($this->strip) && $this->driver()->config()->strip)) { 24 | $image->modify(new StripMetaModifier()); 25 | } 26 | 27 | $imagick = $image->core()->native(); 28 | $imagick->setImageBackgroundColor('white'); 29 | $imagick->setBackgroundColor('white'); 30 | $imagick->setFormat($format); 31 | $imagick->setImageFormat($format); 32 | $imagick->setCompression($compression); 33 | $imagick->setImageCompression($compression); 34 | $imagick->setCompressionQuality($this->quality); 35 | $imagick->setImageCompressionQuality($this->quality); 36 | 37 | return new EncodedImage($imagick->getImagesBlob(), 'image/jp2'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Interfaces/FontProcessorInterface.php: -------------------------------------------------------------------------------- 1 | mediaType; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | * 36 | * @see EncodedImageInterface::mimetype() 37 | */ 38 | public function mimetype(): string 39 | { 40 | return $this->mediaType(); 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | * 46 | * @see EncodedImageInterface::toDataUri() 47 | */ 48 | public function toDataUri(): string 49 | { 50 | return sprintf('data:%s;base64,%s', $this->mediaType(), base64_encode((string) $this)); 51 | } 52 | 53 | /** 54 | * Show debug info for the current image 55 | * 56 | * @return array 57 | */ 58 | public function __debugInfo(): array 59 | { 60 | return [ 61 | 'mediaType' => $this->mediaType(), 62 | 'size' => $this->size(), 63 | ]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Interfaces/DrawableInterface.php: -------------------------------------------------------------------------------- 1 | new Rectangle( 38 | is_null($this->width) ? $image->width() : $image->width() + $this->width, 39 | is_null($this->height) ? $image->height() : $image->height() + $this->height, 40 | ), 41 | default => new Rectangle( 42 | is_null($this->width) ? $image->width() : $this->width, 43 | is_null($this->height) ? $image->height() : $this->height, 44 | ), 45 | }; 46 | 47 | return $size->alignPivotTo($image->size(), $this->position); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/ColorProcessor.php: -------------------------------------------------------------------------------- 1 | convertTo($this->colorspace) 25 | ); 26 | } 27 | 28 | public function nativeToColor(mixed $native): ColorInterface 29 | { 30 | return match ($this->colorspace::class) { 31 | CmykColorspace::class => $this->colorspace->colorFromNormalized([ 32 | $native->getColorValue(Imagick::COLOR_CYAN), 33 | $native->getColorValue(Imagick::COLOR_MAGENTA), 34 | $native->getColorValue(Imagick::COLOR_YELLOW), 35 | $native->getColorValue(Imagick::COLOR_BLACK), 36 | ]), 37 | default => $this->colorspace->colorFromNormalized([ 38 | $native->getColorValue(Imagick::COLOR_RED), 39 | $native->getColorValue(Imagick::COLOR_GREEN), 40 | $native->getColorValue(Imagick::COLOR_BLUE), 41 | $native->getColorValue(Imagick::COLOR_ALPHA), 42 | ]), 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Typography/TextBlock.php: -------------------------------------------------------------------------------- 1 | push(new Line($line)); 20 | } 21 | } 22 | 23 | /** 24 | * Return array of lines in text block 25 | * 26 | * @return array 27 | */ 28 | public function lines(): array 29 | { 30 | return $this->items; 31 | } 32 | 33 | /** 34 | * Set lines of the text block 35 | * 36 | * @param array $lines 37 | */ 38 | public function setLines(array $lines): self 39 | { 40 | $this->items = $lines; 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * Get line by given key 47 | */ 48 | public function line(mixed $key): ?Line 49 | { 50 | if (!array_key_exists($key, $this->lines())) { 51 | return null; 52 | } 53 | 54 | return $this->lines()[$key]; 55 | } 56 | 57 | /** 58 | * Return line with most characters of text block 59 | */ 60 | public function longestLine(): Line 61 | { 62 | $lines = $this->lines(); 63 | usort($lines, function (Line $a, Line $b): int { 64 | if ($a->length() === $b->length()) { 65 | return 0; 66 | } 67 | return $a->length() > $b->length() ? -1 : 1; 68 | }); 69 | 70 | return $lines[0]; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Analyzers/PixelColorAnalyzer.php: -------------------------------------------------------------------------------- 1 | colorAt( 26 | $image->colorspace(), 27 | $image->core()->frame($this->frame_key)->native() 28 | ); 29 | } 30 | 31 | /** 32 | * @throws GeometryException 33 | * @throws ColorException 34 | */ 35 | protected function colorAt(ColorspaceInterface $colorspace, GdImage $gd): ColorInterface 36 | { 37 | $index = @imagecolorat($gd, $this->x, $this->y); 38 | 39 | if (!imageistruecolor($gd)) { 40 | $index = imagecolorsforindex($gd, $index); 41 | } 42 | 43 | if ($index === false) { 44 | throw new GeometryException( 45 | 'The specified position is not in the valid image area.' 46 | ); 47 | } 48 | 49 | return $this->driver()->colorProcessor($colorspace)->nativeToColor($index); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Encoders/PngEncoder.php: -------------------------------------------------------------------------------- 1 | prepareOutput($image); 27 | 28 | return $this->createEncodedImage(function ($pointer) use ($output): void { 29 | imageinterlace($output, $this->interlaced); 30 | imagepng($output, $pointer, -1); 31 | }, 'image/png'); 32 | } 33 | 34 | /** 35 | * Prepare given image instance for PNG format output according to encoder settings 36 | * 37 | * @throws RuntimeException 38 | * @throws ColorException 39 | * @throws AnimationException 40 | */ 41 | private function prepareOutput(ImageInterface $image): GdImage 42 | { 43 | if ($this->indexed) { 44 | $output = clone $image; 45 | $output->reduceColors(255); 46 | 47 | return $output->core()->native(); 48 | } 49 | 50 | return Cloner::clone($image->core()->native()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Geometry/Traits/HasBorder.php: -------------------------------------------------------------------------------- 1 | setBorderSize($size)->setBorderColor($color); 20 | } 21 | 22 | /** 23 | * {@inheritdoc} 24 | * 25 | * @see DrawableInterface::setBorderSize() 26 | */ 27 | public function setBorderSize(int $size): self 28 | { 29 | $this->borderSize = $size; 30 | 31 | return $this; 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | * 37 | * @see DrawableInterface::borderSize() 38 | */ 39 | public function borderSize(): int 40 | { 41 | return $this->borderSize; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | * 47 | * @see DrawableInterface::setBorderColor() 48 | */ 49 | public function setBorderColor(mixed $color): self 50 | { 51 | $this->borderColor = $color; 52 | 53 | return $this; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | * 59 | * @see DrawableInterface::borderColor() 60 | */ 61 | public function borderColor(): mixed 62 | { 63 | return $this->borderColor; 64 | } 65 | 66 | /** 67 | * {@inheritdoc} 68 | * 69 | * @see DrawableInterface::hasBorder() 70 | */ 71 | public function hasBorder(): bool 72 | { 73 | return $this->borderSize > 0 && !is_null($this->borderColor); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Colors/Rgb/Decoders/HexColorDecoder.php: -------------------------------------------------------------------------------- 1 | [a-f\d]{3}(?:[a-f\d]?|(?:[a-f\d]{3}(?:[a-f\d]{2})?)?)\b)$/i'; 26 | if (preg_match($pattern, $input, $matches) != 1) { 27 | throw new DecoderException('Unable to decode input'); 28 | } 29 | 30 | $values = match (strlen($matches['hex'])) { 31 | 3, 4 => str_split($matches['hex']), 32 | 6, 8 => str_split($matches['hex'], 2), 33 | default => throw new DecoderException('Unable to decode input'), 34 | }; 35 | 36 | $values = array_map(function (string $value): float|int { 37 | return match (strlen($value)) { 38 | 1 => hexdec($value . $value), 39 | 2 => hexdec($value), 40 | default => throw new DecoderException('Unable to decode input'), 41 | }; 42 | }, $values); 43 | 44 | return new Color(...$values); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php: -------------------------------------------------------------------------------- 1 | driver()->colorProcessor($image->colorspace())->colorToNative( 21 | $this->backgroundColor() 22 | ); 23 | 24 | $border_color = $this->driver()->colorProcessor($image->colorspace())->colorToNative( 25 | $this->borderColor() 26 | ); 27 | 28 | foreach ($image as $frame) { 29 | $drawing = new ImagickDraw(); 30 | $drawing->setFillColor($background_color); 31 | 32 | if ($this->drawable->hasBorder()) { 33 | $drawing->setStrokeWidth($this->drawable->borderSize()); 34 | $drawing->setStrokeColor($border_color); 35 | } 36 | 37 | $drawing->ellipse( 38 | $this->drawable->position()->x(), 39 | $this->drawable->position()->y(), 40 | $this->drawable->width() / 2, 41 | $this->drawable->height() / 2, 42 | 0, 43 | 360 44 | ); 45 | 46 | $frame->native()->drawImage($drawing); 47 | } 48 | 49 | return $image; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Encoders/WebpEncoder.php: -------------------------------------------------------------------------------- 1 | strip || (is_null($this->strip) && $this->driver()->config()->strip)) { 25 | $image->modify(new StripMetaModifier()); 26 | } 27 | 28 | $imagick = $image->core()->native(); 29 | $imagick->setImageBackgroundColor(new ImagickPixel('transparent')); 30 | 31 | if (!$image->isAnimated()) { 32 | $imagick = $imagick->mergeImageLayers(Imagick::LAYERMETHOD_MERGE); 33 | } 34 | 35 | $imagick->setFormat($format); 36 | $imagick->setImageFormat($format); 37 | $imagick->setCompression($compression); 38 | $imagick->setImageCompression($compression); 39 | $imagick->setImageCompressionQuality($this->quality); 40 | 41 | if ($this->quality === 100) { 42 | $imagick->setOption('webp:lossless', 'true'); 43 | } 44 | 45 | return new EncodedImage($imagick->getImagesBlob(), 'image/webp'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php: -------------------------------------------------------------------------------- 1 | driver()->colorProcessor($image->colorspace())->colorToNative( 23 | $this->backgroundColor() 24 | ); 25 | 26 | $border_color = $this->driver()->colorProcessor($image->colorspace())->colorToNative( 27 | $this->borderColor() 28 | ); 29 | 30 | $drawing->setFillColor($background_color); 31 | if ($this->drawable->hasBorder()) { 32 | $drawing->setStrokeColor($border_color); 33 | $drawing->setStrokeWidth($this->drawable->borderSize()); 34 | } 35 | 36 | // build rectangle 37 | $drawing->rectangle( 38 | $this->drawable->position()->x(), 39 | $this->drawable->position()->y(), 40 | $this->drawable->position()->x() + $this->drawable->width(), 41 | $this->drawable->position()->y() + $this->drawable->height() 42 | ); 43 | 44 | foreach ($image as $frame) { 45 | $frame->native()->drawImage($drawing); 46 | } 47 | 48 | return $image; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/AlignRotationModifier.php: -------------------------------------------------------------------------------- 1 | exif('IFD0.Orientation')) { 21 | 2 => $image->flop(), 22 | 3 => $image->rotate(180), 23 | 4 => $image->rotate(180)->flop(), 24 | 5 => $image->rotate(270)->flop(), 25 | 6 => $image->rotate(270), 26 | 7 => $image->rotate(90)->flop(), 27 | 8 => $image->rotate(90), 28 | default => $image 29 | }; 30 | 31 | return $this->markAligned($image); 32 | } 33 | 34 | /** 35 | * Set exif data of image to top-left orientation, marking the image as 36 | * aligned and making sure the rotation correction process is not 37 | * performed again. 38 | */ 39 | private function markAligned(ImageInterface $image): ImageInterface 40 | { 41 | $exif = $image->exif()->map(function ($item) { 42 | if (is_array($item) && array_key_exists('Orientation', $item)) { 43 | $item['Orientation'] = 1; 44 | return $item; 45 | } 46 | 47 | return $item; 48 | }); 49 | 50 | return $image->setExif($exif); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Modifiers/RemoveAnimationModifier.php: -------------------------------------------------------------------------------- 1 | core()->frame($this->normalizePosition($image)); 26 | } 27 | 28 | /** 29 | * Return the position of the selected frame as integer 30 | * 31 | * @throws InputException 32 | */ 33 | protected function normalizePosition(ImageInterface $image): int 34 | { 35 | if (is_int($this->position)) { 36 | return $this->position; 37 | } 38 | 39 | if (is_numeric($this->position)) { 40 | return (int) $this->position; 41 | } 42 | 43 | // calculate position from percentage value 44 | if (preg_match("/^(?P[0-9]{1,3})%$/", $this->position, $matches) != 1) { 45 | throw new InputException( 46 | 'Position must be either integer or a percent value as string.' 47 | ); 48 | } 49 | 50 | $total = count($image); 51 | $position = intval(round($total / 100 * intval($matches['percent']))); 52 | 53 | return $position == $total ? $position - 1 : $position; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Modifiers/BlendTransparencyModifier.php: -------------------------------------------------------------------------------- 1 | handleInput( 40 | $this->color ?: $driver->config()->blendingColor 41 | ); 42 | 43 | // replace alpha channel value with opaque value 44 | if ($color->isTransparent()) { 45 | return new Color( 46 | $color->channel(Red::class)->value(), 47 | $color->channel(Green::class)->value(), 48 | $color->channel(Blue::class)->value(), 49 | ); 50 | } 51 | 52 | return $color; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Drivers/Imagick/Modifiers/ColorspaceModifier.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | protected static array $mapping = [ 24 | RgbColorspace::class => Imagick::COLORSPACE_SRGB, 25 | CmykColorspace::class => Imagick::COLORSPACE_CMYK, 26 | ]; 27 | 28 | public function apply(ImageInterface $image): ImageInterface 29 | { 30 | $colorspace = $this->targetColorspace(); 31 | 32 | $imagick = $image->core()->native(); 33 | $imagick->transformImageColorspace( 34 | $this->getImagickColorspace($colorspace) 35 | ); 36 | 37 | return $image; 38 | } 39 | 40 | /** 41 | * @throws NotSupportedException 42 | */ 43 | private function getImagickColorspace(ColorspaceInterface $colorspace): int 44 | { 45 | if (!array_key_exists($colorspace::class, self::$mapping)) { 46 | throw new NotSupportedException('Given colorspace is not supported.'); 47 | } 48 | 49 | return self::$mapping[$colorspace::class]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | prepareOptions($options) as $name => $value) { 33 | if (!property_exists($this, $name)) { 34 | throw new InputException('Property ' . $name . ' does not exists for ' . $this::class . '.'); 35 | } 36 | 37 | $this->{$name} = $value; 38 | } 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * This method makes it possible to call self::setOptions() with a single 45 | * array instead of named parameters 46 | * 47 | * @param array $options 48 | * @return array 49 | */ 50 | private function prepareOptions(array $options): array 51 | { 52 | if ($options === []) { 53 | return $options; 54 | } 55 | 56 | if (count($options) > 1) { 57 | return $options; 58 | } 59 | 60 | if (!array_key_exists(0, $options)) { 61 | return $options; 62 | } 63 | 64 | if (!is_array($options[0])) { 65 | return $options; 66 | } 67 | 68 | return $options[0]; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Interfaces/CoreInterface.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function setNative(mixed $native): self; 24 | 25 | /** 26 | * Count number of frames of animated image core 27 | */ 28 | public function count(): int; 29 | 30 | /** 31 | * Return frame of given position in an animated image 32 | * 33 | * @throws AnimationException 34 | */ 35 | public function frame(int $position): FrameInterface; 36 | 37 | /** 38 | * Add new frame to core 39 | * 40 | * @return CoreInterface 41 | */ 42 | public function add(FrameInterface $frame): self; 43 | 44 | /** 45 | * Return number of repetitions of an animated image 46 | */ 47 | public function loops(): int; 48 | 49 | /** 50 | * Set the number of repetitions for an animation. Where a 51 | * value of 0 means infinite repetition. 52 | * 53 | * @return CoreInterface 54 | */ 55 | public function setLoops(int $loops): self; 56 | 57 | /** 58 | * Get first frame in core 59 | * 60 | * @throws AnimationException 61 | */ 62 | public function first(): FrameInterface; 63 | 64 | /** 65 | * Get last frame in core 66 | * 67 | * @throws AnimationException 68 | */ 69 | public function last(): FrameInterface; 70 | } 71 | -------------------------------------------------------------------------------- /src/Interfaces/CollectionInterface.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | interface CollectionInterface extends Traversable 13 | { 14 | /** 15 | * Determine if the collection has item at given key 16 | */ 17 | public function has(int|string $key): bool; 18 | 19 | /** 20 | * Add item to collection 21 | * 22 | * @return CollectionInterface 23 | */ 24 | public function push(mixed $item): self; 25 | 26 | /** 27 | * Return item for given key or return default is key does not exist 28 | */ 29 | public function get(int|string $key, mixed $default = null): mixed; 30 | 31 | /** 32 | * Return item at given numeric position starting at 0 33 | */ 34 | public function getAtPosition(int $key = 0, mixed $default = null): mixed; 35 | 36 | /** 37 | * Return first item in collection 38 | */ 39 | public function first(): mixed; 40 | 41 | /** 42 | * Return last item in collection 43 | */ 44 | public function last(): mixed; 45 | 46 | /** 47 | * Return item count of collection 48 | */ 49 | public function count(): int; 50 | 51 | /** 52 | * Empty collection 53 | * 54 | * @return CollectionInterface 55 | */ 56 | public function empty(): self; 57 | 58 | /** 59 | * Transform collection as array 60 | * 61 | * @return array 62 | */ 63 | public function toArray(): array; 64 | 65 | /** 66 | * Extract items based on given values and discard the rest. 67 | * 68 | * @return CollectionInterface 69 | */ 70 | public function slice(int $offset, ?int $length = 0): self; 71 | } 72 | -------------------------------------------------------------------------------- /src/Drivers/Gd/Modifiers/CoverModifier.php: -------------------------------------------------------------------------------- 1 | getCropSize($image); 25 | $resize = $this->getResizeSize($crop); 26 | 27 | foreach ($image as $frame) { 28 | $this->modifyFrame($frame, $crop, $resize); 29 | } 30 | 31 | return $image; 32 | } 33 | 34 | /** 35 | * @throws ColorException 36 | */ 37 | protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void 38 | { 39 | // create new image 40 | $modified = Cloner::cloneEmpty($frame->native(), $resize); 41 | 42 | // copy content from resource 43 | imagecopyresampled( 44 | $modified, 45 | $frame->native(), 46 | 0, 47 | 0, 48 | $crop->pivot()->x(), 49 | $crop->pivot()->y(), 50 | $resize->width(), 51 | $resize->height(), 52 | $crop->width(), 53 | $crop->height() 54 | ); 55 | 56 | // set new content as resource 57 | $frame->setNative($modified); 58 | } 59 | } 60 | --------------------------------------------------------------------------------