Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
tipuloidea
/
back
/
vendor
/
spatie
/
image
/
src
/
Drivers
/
Imagick
:
ImagickDriver.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php namespace Spatie\Image\Drivers\Imagick; use Imagick; use ImagickDraw; use ImagickPixel; use Spatie\Image\Drivers\Concerns\AddsWatermark; use Spatie\Image\Drivers\Concerns\CalculatesCropOffsets; use Spatie\Image\Drivers\Concerns\CalculatesFocalCropAndResizeCoordinates; use Spatie\Image\Drivers\Concerns\CalculatesFocalCropCoordinates; use Spatie\Image\Drivers\Concerns\GetsOrientationFromExif; use Spatie\Image\Drivers\Concerns\PerformsFitCrops; use Spatie\Image\Drivers\Concerns\PerformsOptimizations; use Spatie\Image\Drivers\Concerns\ValidatesArguments; use Spatie\Image\Drivers\ImageDriver; use Spatie\Image\Enums\AlignPosition; use Spatie\Image\Enums\BorderType; use Spatie\Image\Enums\ColorFormat; use Spatie\Image\Enums\Constraint; use Spatie\Image\Enums\CropPosition; use Spatie\Image\Enums\Fit; use Spatie\Image\Enums\FlipDirection; use Spatie\Image\Enums\Orientation; use Spatie\Image\Exceptions\InvalidFont; use Spatie\Image\Exceptions\MissingParameter; use Spatie\Image\Exceptions\UnsupportedImageFormat; use Spatie\Image\Point; use Spatie\Image\Size; class ImagickDriver implements ImageDriver { use AddsWatermark; use CalculatesCropOffsets; use CalculatesFocalCropAndResizeCoordinates; use CalculatesFocalCropCoordinates; use GetsOrientationFromExif; use PerformsFitCrops; use PerformsOptimizations; use ValidatesArguments; protected Imagick $image; protected ?string $format = null; protected array $exif = []; protected string $originalPath; public function new(int $width, int $height, ?string $backgroundColor = null): static { $color = new ImagickColor($backgroundColor); $image = new Imagick; $image->newImage($width, $height, $color->getPixel(), 'png'); $image->setType(Imagick::IMGTYPE_UNDEFINED); $image->setImageType(Imagick::IMGTYPE_UNDEFINED); $image->setColorspace(Imagick::COLORSPACE_UNDEFINED); return (new static)->setImage($image); } protected function setImage(Imagick $image): static { $this->image = $image; return $this; } public function image(): Imagick { return $this->image; } public function loadFile(string $path, bool $autoRotate = true): static { $this->originalPath = $path; $this->optimize = false; $this->image = new Imagick($path); $this->exif = $this->image->getImageProperties('exif:*'); if ($autoRotate) { $this->autoRotate(); } if ($this->isAnimated()) { $this->image = $this->image->coalesceImages(); } return $this; } protected function isAnimated(): bool { return count($this->image) > 1; } public function getWidth(): int { return $this->image->getImageWidth(); } public function getHeight(): int { return $this->image->getImageHeight(); } public function brightness(int $brightness): static { foreach ($this->image as $image) { $image->modulateImage(100 + $brightness, 100, 100); } return $this; } public function blur(int $blur): static { foreach ($this->image as $image) { $image->blurImage(0.5 * $blur, 0.1 * $blur); } return $this; } public function fit( Fit $fit, ?int $desiredWidth = null, ?int $desiredHeight = null, bool $relative = false, ?string $backgroundColor = null, ): static { if ($fit === Fit::Crop) { return $this->fitCrop($fit, $this->getWidth(), $this->getHeight(), $desiredWidth, $desiredHeight); } if ($fit === Fit::FillMax) { if (is_null($desiredWidth) || is_null($desiredHeight)) { throw new MissingParameter('Both desiredWidth and desiredHeight must be set when using Fit::FillMax'); } if (is_null($backgroundColor)) { throw new MissingParameter('backgroundColor must be set when using Fit::FillMax'); } return $this->fitFillMax($desiredWidth, $desiredHeight, $backgroundColor); } $calculatedSize = $fit->calculateSize( $this->getWidth(), $this->getHeight(), $desiredWidth, $desiredHeight ); foreach ($this->image as $image) { $image->scaleImage($calculatedSize->width, $calculatedSize->height); } if ($fit->shouldResizeCanvas()) { $this->resizeCanvas($desiredWidth, $desiredHeight, AlignPosition::Center, $relative, $backgroundColor); } return $this; } public function fitFillMax(int $desiredWidth, int $desiredHeight, string $backgroundColor, bool $relative = false): static { $this->resize($desiredWidth, $desiredHeight, [Constraint::PreserveAspectRatio]); $this->resizeCanvas($desiredWidth, $desiredHeight, AlignPosition::Center, $relative, $backgroundColor); return $this; } public function resizeCanvas( ?int $width = null, ?int $height = null, ?AlignPosition $position = null, bool $relative = false, ?string $backgroundColor = null ): static { $position ??= AlignPosition::Center; $originalWidth = $this->getWidth(); $originalHeight = $this->getHeight(); $width ??= $originalWidth; $height ??= $originalHeight; if ($relative) { $width = $originalWidth + $width; $height = $originalHeight + $height; } $width = $width <= 0 ? $width + $originalWidth : $width; $height = $height <= 0 ? $height + $originalHeight : $height; $canvas = $this->new($width, $height, $backgroundColor); $canvasSize = $canvas->getSize()->align($position); $imageSize = $this->getSize()->align($position); $canvasPosition = $imageSize->relativePosition($canvasSize); $imagePosition = $canvasSize->relativePosition($imageSize); if ($width <= $originalWidth) { $destinationX = 0; $sourceX = $canvasPosition->x; $sourceWidth = $canvasSize->width; } else { $destinationX = $imagePosition->x; $sourceX = 0; $sourceWidth = $originalWidth; } if ($height <= $originalHeight) { $destinationY = 0; $sourceY = $canvasPosition->y; $sourceHeight = $canvasSize->height; } else { $destinationY = $imagePosition->y; $sourceY = 0; $sourceHeight = $originalHeight; } // make image area transparent to keep transparency // even if background-color is set $rect = new ImagickDraw; $fill = $canvas->pickColor(0, 0, ColorFormat::Hex); $fill = $fill === '#ff0000' ? '#00ff00' : '#ff0000'; $rect->setFillColor($fill); $rect->rectangle($destinationX, $destinationY, $destinationX + $sourceWidth - 1, $destinationY + $sourceHeight - 1); $canvas->image->drawImage($rect); $canvas->image->transparentPaintImage($fill, 0, 0, false); foreach ($this->image->getImageProfiles() as $key => $value) { $canvas->image->setImageProfile($key, $value); } $canvas->image->setImageColorspace($this->image->getImageColorspace()); // copy image into new canvas $this->image->cropImage($sourceWidth, $sourceHeight, $sourceX, $sourceY); $canvas->image->compositeImage($this->image, Imagick::COMPOSITE_DEFAULT, $destinationX, $destinationY); $canvas->image->setImagePage(0, 0, 0, 0); // set new core to canvas $this->image = $canvas->image; return $this; } public function pickColor(int $x, int $y, ColorFormat $colorFormat): mixed { $color = new ImagickColor($this->image->getImagePixelColor($x, $y)); return $color->format($colorFormat); } public function save(?string $path = null): static { if (! $path) { $path = $this->originalPath; } if (is_null($this->format)) { $format = pathinfo($path, PATHINFO_EXTENSION); } else { $format = $this->format; } $formats = Imagick::queryFormats('*'); if (in_array('JPEG', $formats)) { $formats[] = 'JFIF'; } if (! in_array(strtoupper($format), $formats)) { throw UnsupportedImageFormat::make($format); } foreach ($this->image as $image) { if (strtoupper($format) === 'JFIF') { $image->setFormat('JPEG'); } else { $image->setFormat($format); } } if ($this->isAnimated()) { $image = $this->image->deconstructImages(); $image->writeImages($path, true); } else { $this->image->writeImage($path); } if ($this->optimize) { $this->optimizerChain->optimize($path); } $this->format = null; return $this; } public function base64(string $imageFormat = 'jpeg', bool $prefixWithFormat = true): string { $image = clone $this->image; $image->setFormat($imageFormat); if ($prefixWithFormat) { return 'data:image/'.$imageFormat.';base64,'.base64_encode($image->getImageBlob()); } return base64_encode($image->getImageBlob()); } public function driverName(): string { return 'imagick'; } public function getSize(): Size { return new Size($this->getWidth(), $this->getHeight()); } public function gamma(float $gamma): static { foreach ($this->image as $image) { $image->gammaImage($gamma); } return $this; } public function contrast(float $level): static { foreach ($this->image as $image) { $image->brightnessContrastImage(1, $level); } return $this; } public function colorize(int $red, int $green, int $blue): static { $quantumRange = $this->image->getQuantumRange(); $red = Helpers::normalizeColorizeLevel($red); $green = Helpers::normalizeColorizeLevel($green); $blue = Helpers::normalizeColorizeLevel($blue); foreach ($this->image as $image) { $image->levelImage(0, $red, $quantumRange['quantumRangeLong'], Imagick::CHANNEL_RED); $image->levelImage(0, $green, $quantumRange['quantumRangeLong'], Imagick::CHANNEL_GREEN); $image->levelImage(0, $blue, $quantumRange['quantumRangeLong'], Imagick::CHANNEL_BLUE); } return $this; } public function greyscale(): static { foreach ($this->image as $image) { $image->modulateImage(100, 0, 100); } return $this; } public function manualCrop(int $width, int $height, ?int $x = null, ?int $y = null): static { $cropped = new Size($width, $height); $position = new Point($x ?? 0, $y ?? 0); if (is_null($x) && is_null($y)) { $position = $this ->getSize() ->align(AlignPosition::Center) ->relativePosition($cropped->align(AlignPosition::Center)); } foreach ($this->image as $image) { $image->cropImage($cropped->width, $cropped->height, $position->x, $position->y); $image->setImagePage(0, 0, 0, 0); } return $this; } public function crop(int $width, int $height, CropPosition $position = CropPosition::Center): static { [$offsetX, $offsetY] = $this->calculateCropOffsets($width, $height, $position); return $this->manualCrop($width, $height, $offsetX, $offsetY); } public function focalCrop(int $width, int $height, ?int $cropCenterX = null, ?int $cropCenterY = null): static { [$width, $height, $cropCenterX, $cropCenterY] = $this->calculateFocalCropCoordinates( $width, $height, $cropCenterX, $cropCenterY ); $this->manualCrop($width, $height, $cropCenterX, $cropCenterY); return $this; } public function focalCropAndResize(int $width, int $height, ?int $cropCenterX = null, ?int $cropCenterY = null): static { [$cropWidth, $cropHeight, $cropX, $cropY] = $this->calculateFocalCropAndResizeCoordinates( $width, $height, $cropCenterX, $cropCenterY ); $this->manualCrop($cropWidth, $cropHeight, $cropX, $cropY) ->width($width) ->height($height); return $this; } public function sepia(): static { return $this ->greyscale() ->brightness(-40) ->contrast(20) ->colorize(50, 35, 20) ->brightness(-10) ->contrast(10); } public function sharpen(float $amount): static { foreach ($this->image as $image) { $image->unsharpMaskImage(1, 1, $amount / 6.25, 0); } return $this; } public function background(string $color): static { $background = $this->new($this->getWidth(), $this->getHeight(), $color); $this->overlay($background, $this, 0, 0); return $this; } public function overlay(ImageDriver $bottomImage, ImageDriver $topImage, int $x = 0, int $y = 0): static { $bottomImage->insert($topImage, AlignPosition::Center, $x, $y); $this->image = $bottomImage->image(); return $this; } public function orientation(?Orientation $orientation = null): static { if (is_null($orientation)) { $orientation = $this->getOrientationFromExif($this->exif); } foreach ($this->image as $image) { $image->rotateImage(new ImagickPixel('none'), $orientation->degrees()); } return $this; } /** * @return array<string, mixed> */ public function exif(): array { return $this->exif; } public function flip(FlipDirection $flip): static { foreach ($this->image as $image) { switch ($flip) { case FlipDirection::Vertical: $image->flipImage(); break; case FlipDirection::Horizontal: $image->flopImage(); break; case FlipDirection::Both: $image->flipImage(); $image->flopImage(); break; } } return $this; } public function pixelate(int $pixelate = 50): static { if ($pixelate === 0) { return $this; } $width = $this->getWidth(); $height = $this->getHeight(); foreach ($this->image as $image) { $image->scaleImage(max(1, (int) ($width / $pixelate)), max(1, (int) ($height / $pixelate))); $image->scaleImage($width, $height); } return $this; } public function insert( ImageDriver|string $otherImage, AlignPosition $position = AlignPosition::Center, int $x = 0, int $y = 0, int $alpha = 100 ): static { $this->ensureNumberBetween($alpha, 0, 100, 'alpha'); if (is_string($otherImage)) { $otherImage = (new static)->loadFile($otherImage); } $otherImage->image()->setImageOrientation(Imagick::ORIENTATION_UNDEFINED); $otherImage->image()->evaluateImage(Imagick::EVALUATE_DIVIDE, (1 / ($alpha / 100)), Imagick::CHANNEL_ALPHA); $imageSize = $this->getSize()->align($position, $x, $y); $watermarkSize = $otherImage->getSize()->align($position); $target = $imageSize->relativePosition($watermarkSize); foreach ($this->image as $image) { $image->compositeImage( $otherImage->image(), Imagick::COMPOSITE_OVER, $target->x, $target->y ); } return $this; } public function resize(int $width, int $height, array $constraints = []): static { $resized = $this->getSize()->resize($width, $height, $constraints); foreach ($this->image as $image) { $image->scaleImage($resized->width, $resized->height); } return $this; } public function width(int $width, array $constraints = [Constraint::PreserveAspectRatio]): static { $newHeight = (int) round($width / $this->getSize()->aspectRatio()); $this->resize($width, $newHeight, $constraints); return $this; } public function height(int $height, array $constraints = [Constraint::PreserveAspectRatio]): static { $newWidth = (int) round($height * $this->getSize()->aspectRatio()); $this->resize($newWidth, $height, $constraints); return $this; } public function border(int $width, BorderType $type, string $color = '000000'): static { if ($type === BorderType::Shrink) { $originalWidth = $this->getWidth(); $originalHeight = $this->getHeight(); $this ->resize( (int) round($this->getWidth() - ($width * 2)), (int) round($this->getHeight() - ($width * 2)), [Constraint::PreserveAspectRatio], ) ->resizeCanvas( $originalWidth, $originalHeight, AlignPosition::Center, false, $color, ); return $this; } if ($type === BorderType::Expand) { $this->resizeCanvas( (int) round($width * 2), (int) round($width * 2), AlignPosition::Center, true, $color, ); return $this; } if ($type === BorderType::Overlay) { $shape = new ImagickDraw; $backgroundColor = new ImagickColor; $shape->setFillColor($backgroundColor->getPixel()); $borderColor = new ImagickColor($color); $shape->setStrokeColor($borderColor->getPixel()); $shape->setStrokeWidth($width); $shape->rectangle( (int) round($width / 2), (int) round($width / 2), (int) round($this->getWidth() - ($width / 2)), (int) round($this->getHeight() - ($width / 2)), ); foreach ($this->image as $image) { $image->drawImage($shape); } return $this; } } public function quality(int $quality): static { foreach ($this->image as $image) { $this->image->setImageCompressionQuality($quality); $this->image->setCompressionQuality(100 - $quality); // For PNGs } return $this; } public function format(string $format): static { $this->format = $format; return $this; } public function autoRotate(): void { switch ($this->image->getImageOrientation()) { case Imagick::ORIENTATION_TOPLEFT: break; case Imagick::ORIENTATION_TOPRIGHT: $this->image->flopImage(); break; case Imagick::ORIENTATION_BOTTOMRIGHT: $this->image->rotateImage('#000', 180); break; case Imagick::ORIENTATION_BOTTOMLEFT: $this->image->flopImage(); $this->image->rotateImage('#000', 180); break; case Imagick::ORIENTATION_LEFTTOP: $this->image->flopImage(); $this->image->rotateImage('#000', -90); break; case Imagick::ORIENTATION_RIGHTTOP: $this->image->rotateImage('#000', 90); break; case Imagick::ORIENTATION_RIGHTBOTTOM: $this->image->flopImage(); $this->image->rotateImage('#000', 90); break; case Imagick::ORIENTATION_LEFTBOTTOM: $this->image->rotateImage('#000', -90); break; default: // Invalid orientation break; } $this->image->setImageOrientation(Imagick::ORIENTATION_TOPLEFT); } public function text( string $text, int $fontSize, string $color = '000000', int $x = 0, int $y = 0, int $angle = 0, string $fontPath = '', int $width = 0, ): static { if ($fontPath && ! file_exists($fontPath)) { throw InvalidFont::make($fontPath); } $textColor = new ImagickColor($color); $draw = new ImagickDraw; $draw->setFillColor($textColor->getPixel()); $draw->setFontSize($fontSize); if ($fontPath) { $draw->setFont($fontPath); } $this->image->annotateImage( $draw, $x, $y, $angle, $width > 0 ? $this->wrapText($text, $fontSize, $fontPath, $angle, $width) : $text, ); return $this; } public function wrapText(string $text, int $fontSize, string $fontPath = '', int $angle = 0, int $width = 0): string { if ($fontPath && ! file_exists($fontPath)) { throw InvalidFont::make($fontPath); } $wrapped = ''; $words = explode(' ', $text); foreach ($words as $word) { $teststring = "{$wrapped} {$word}"; $draw = new ImagickDraw; if ($fontPath) { $draw->setFont($fontPath); } $draw->setFontSize($fontSize); $metrics = (new Imagick)->queryFontMetrics($draw, $teststring); if ($metrics['textWidth'] > $width) { $wrapped .= "\n".$word; } else { $wrapped .= ' '.$word; } } return $wrapped; } }