Dockerfile000064400000001605150731646300006545 0ustar00FROM php:8-cli RUN apt update \ && apt install -y \ libpng-dev \ libicu-dev \ libpq-dev \ libzip-dev \ zip \ zlib1g-dev \ locales \ locales-all \ libmagickwand-dev \ libwebp-dev \ && pecl install imagick \ && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ && docker-php-ext-enable imagick \ && docker-php-ext-install \ intl \ opcache \ pdo \ pdo_pgsql \ pdo_mysql \ pgsql \ fileinfo \ mysqli \ gd \ bcmath \ exif \ zip \ && apt-get clean # install composer # RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer LICENSE000064400000002066150731646300005562 0ustar00The MIT License (MIT) Copyright (c) 2021 Oliver Vogel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.composer.json000064400000001616150731646300007277 0ustar00{ "name": "intervention/image", "description": "PHP image manipulation", "homepage": "https://image.intervention.io/", "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail"], "license": "MIT", "authors": [ { "name": "Oliver Vogel", "email": "oliver@intervention.io", "homepage": "https://intervention.io/" } ], "require": { "php": "^8.1", "intervention/gif": "^3.0", "intervention/mimesniffer": "^0.4.2" }, "require-dev": { "phpunit/phpunit": "^9", "mockery/mockery": "^1.4", "phpstan/phpstan": "^1" }, "autoload": { "psr-4": { "Intervention\\Image\\": "src" } }, "autoload-dev": { "psr-4": { "Intervention\\Image\\Tests\\": "tests" } }, "minimum-stability": "alpha" } docker-compose.yml000064400000000260150731646300010204 0ustar00version: '3' services: tests: build: ./ working_dir: /project command: bash -c "composer install && ./vendor/bin/phpunit -vvv" volumes: - ./:/project phpunit.xml.dist000064400000001264150731646300007727 0ustar00 ./tests/ src readme.md000064400000003333150731646300006332 0ustar00# Intervention Image ## PHP Image Manipulation [![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image) [![Build Status](https://github.com/Intervention/image/actions/workflows/run-tests.yml/badge.svg)](https://github.com/Intervention/image/actions) [![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats) Intervention Image is a **image handling and manipulation library written in PHP** providing an easier and expressive way to create, edit, and compose images. GD library or Imagick can be selected as the base layer for all operations. - Simple interface for common tasks - Interchangable driver architecture - Support for animated images - Framework-agnostic - PSR-12 compliant ## Code Examples ```php // create image manager with desired driver $manager = new ImageManager('gd') // open an image file $image = $manager->make('images/example.gif'); // resize image instance $image->resize(320, 240); // insert a watermark $image->place('images/watermark.png'); // encode edited image $encoded = $image->toJpg(); // save encoded image $encoded->save('images/example.jpg'); ``` ## Requirements - PHP >=8.0 ## Supported Image Libraries - GD Library - Imagick PHP extension ## Installation ```bash composer require intervention/image ``` ## Getting started Learn the [basics](https://image.intervention.io/v3/basics/instantiation/) on how to use Intervention Image and more with the [official documentation](https://image.intervention.io/v3/). ## License Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). Copyright 2022 Oliver Vogel src/Collection.php000064400000006624150731646300010154 0ustar00items); } public function toArray(): array { return $this->items; } /** * Count items in collection * * @return integer */ public function count(): int { return count($this->items); } /** * Append new item to collection * * @param mixed $item * @return CollectionInterface */ public function push($item): CollectionInterface { $this->items[] = $item; return $this; } /** * Return first item in collection * * @return mixed */ public function first() { if ($item = reset($this->items)) { return $item; } return null; } /** * Returns last item in collection * * @return mixed */ public function last() { if ($item = end($this->items)) { return $item; } return null; } /** * Return item with given key * * @param integer $key * @return mixed */ public function get(int $key = 0, $default = null) { if (! array_key_exists($key, $this->items)) { return $default; } return $this->items[$key]; } public function has(int $key): bool { return array_key_exists($key, $this->items); } public function query(string $query, $default = null) { $items = $this->getItemsFlat(); if (!array_key_exists($query, $items)) { return $default; } return $items[$query]; } public function map(callable $callback): self { $items = array_map(function ($item) use ($callback) { return $callback($item); }, $this->items); return new self($items); } public function pushEach(array $data, ?callable $callback = null): CollectionInterface { if (! is_iterable($data)) { throw new RuntimeException('Unable to iterate given data.'); } foreach ($data as $item) { $this->push(is_callable($callback) ? $callback($item) : $item); } return $this; } private function getItemsFlat(): array { $iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator($this->items) ); $items = []; foreach ($iterator as $value) { $keys = []; foreach (range(0, $iterator->getDepth()) as $depth) { $keys[] = $iterator->getSubIterator($depth)->key(); } $items[join('.', $keys)] = $value; } return $items; } } src/Drivers/Abstract/AbstractColor.php000064400000001351150731646300013774 0ustar00red(), $this->green(), $this->blue() ); } /** * Determine if color is greyscale * * @return boolean */ public function isGreyscale(): bool { return ($this->red() === $this->green()) && ($this->green() === $this->blue()); } } src/Drivers/Abstract/AbstractFont.php000064400000004565150731646300013636 0ustar00size = $size; return $this; } public function getSize(): float { return $this->size; } public function angle(float $angle): FontInterface { $this->angle = $angle; return $this; } public function getAngle(): float { return $this->angle; } public function filename(string $filename): FontInterface { $this->filename = $filename; return $this; } public function getFilename(): ?string { return $this->filename; } public function hasFilename(): bool { return is_file($this->filename); } public function color($color): FontInterface { $this->color = $color; return $this; } public function getColor(): ?ColorInterface { return $this->handleInput($this->color); } public function align(string $align): FontInterface { $this->align = $align; return $this; } public function getValign(): string { return $this->valign; } public function valign(string $valign): FontInterface { $this->valign = $valign; return $this; } public function getAlign(): string { return $this->align; } public function lineHeight(float $height): FontInterface { $this->lineHeight = $height; return $this; } public function getLineHeight(): float { return $this->lineHeight; } public function leadingInPixels(): int { return intval(round($this->fontSizeInPixels() * $this->getLineHeight())); } public function capHeight(): int { return $this->getBoxSize('T')->getHeight(); } public function fontSizeInPixels(): int { return $this->getBoxSize('Hy')->getHeight(); } } src/Drivers/Abstract/AbstractImage.php000064400000023303150731646300013741 0ustar00getWidth(), $this->getHeight()); } public function size(): SizeInterface { return $this->getSize(); } public function modify(ModifierInterface $modifier): ImageInterface { return $modifier->apply($this); } public function encode(EncoderInterface $encoder): EncodedImage { return $encoder->encode($this); } public function toJpeg(int $quality = 75): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\JpegEncoder', $quality) ); } public function toWebp(int $quality = 75): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\WebpEncoder', $quality) ); } public function toGif(): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\GifEncoder') ); } public function toPng(): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\PngEncoder') ); } public function toBitmap(): EncodedImage { return $this->encode( $this->resolveDriverClass('Encoders\BmpEncoder') ); } public function toBmp(): EncodedImage { return $this->toBitmap(); } public function greyscale(): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\GreyscaleModifier') ); } public function invert(): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\InvertModifier') ); } public function brightness(int $level): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\BrightnessModifier', $level) ); } public function contrast(int $level): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\ContrastModifier', $level) ); } public function gamma(float $gamma): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\GammaModifier', $gamma) ); } public function blur(int $amount = 5): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\BlurModifier', $amount) ); } public function rotate(float $angle, $background = 'ffffff'): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\RotateModifier', $angle, $background) ); } /** * Creates a vertical mirror image * * @return ImageInterface */ public function flip(): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\FlipModifier') ); } /** * Creates a horizontal mirror image * * @return ImageInterface */ public function flop(): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\FlopModifier') ); } public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface { return $this->modify( $this->resolveDriverClass( 'Modifiers\PlaceModifier', $element, $position, $offset_x, $offset_y ) ); } public function fill($color, ?int $x = null, ?int $y = null): ImageInterface { $color = $this->handleInput($color); $position = (is_null($x) && is_null($y)) ? null : new Point($x, $y); return $this->modify( $this->resolveDriverClass( 'Modifiers\FillModifier', $color, $position ) ); } public function pixelate(int $size): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\PixelateModifier', $size) ); } public function sharpen(int $amount = 10): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\SharpenModifier', $amount) ); } public function pickColors(int $x, int $y): CollectionInterface { $colors = new Collection(); foreach ($this as $key => $frame) { $colors->push($this->pickColor($x, $y, $key)); } return $colors; } public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface { $font = $this->runCallback($init, $this->resolveDriverClass('Font')); $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font, $text); return $this->modify($modifier); } public function drawPixel(int $x, int $y, $color = null): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\DrawPixelModifier', new Point($x, $y), $color) ); } public function drawRectangle(int $x, int $y, ?callable $init = null): ImageInterface { $rectangle = $this->runCallback($init, new Rectangle(0, 0)); $modifier = $this->resolveDriverClass('Modifiers\DrawRectangleModifier', new Point($x, $y), $rectangle); return $this->modify($modifier); } public function drawEllipse(int $x, int $y, ?callable $init = null): ImageInterface { $ellipse = $this->runCallback($init, new Ellipse(0, 0)); $modifier = $this->resolveDriverClass('Modifiers\DrawEllipseModifier', new Point($x, $y), $ellipse); return $this->modify($modifier); } public function drawCircle(int $x, int $y, ?callable $init = null): ImageInterface { $circle = $this->runCallback($init, new Circle(0)); $modifier = $this->resolveDriverClass('Modifiers\DrawEllipseModifier', new Point($x, $y), $circle); return $this->modify($modifier); } public function drawLine(callable $init = null): ImageInterface { $line = $this->runCallback($init, new Line(new Point(), new Point())); $modifier = $this->resolveDriverClass('Modifiers\DrawLineModifier', $line->getStart(), $line); return $this->modify($modifier); } public function drawPolygon(callable $init = null): ImageInterface { $polygon = $this->runCallback($init, new Polygon()); $modifier = $this->resolveDriverClass('Modifiers\DrawPolygonModifier', $polygon); return $this->modify($modifier); } public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\ResizeModifier', $width, $height) ); } public function resizeDown(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\ResizeDownModifier', $width, $height) ); } public function scale(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\ScaleModifier', $width, $height) ); } public function scaleDown(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\ScaleDownModifier', $width, $height) ); } public function fit(int $width, int $height, string $position = 'center'): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\FitModifier', $width, $height, $position) ); } public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\FitDownModifier', $width, $height, $position) ); } public function pad(int $width, int $height, $background = 'ffffff', string $position = 'center'): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\PadModifier', $width, $height, $background, $position) ); } public function padDown( int $width, int $height, $background = 'ffffff', string $position = 'center' ): ImageInterface { return $this->modify( $this->resolveDriverClass('Modifiers\PadDownModifier', $width, $height, $background, $position) ); } public function destroy(): void { $this->modify( $this->resolveDriverClass('Modifiers\DestroyModifier') ); } } src/Drivers/Abstract/AbstractInputHandler.php000064400000000676150731646300015324 0ustar00chain()->handle($input); } } src/Drivers/Abstract/AbstractTextWriter.php000064400000003640150731646300015042 0ustar00font; } protected function getPosition(): Point { return $this->position; } /** * Build TextBlock object from text string and align every line * according to text writers font object and position. * * @return TextBlock */ public function getAlignedTextBlock(): TextBlock { $lines = new TextBlock($this->text); $position = $this->getPosition(); $font = $this->getFont(); $boundingBox = $lines->getBoundingBox($font, $position); $pivot = $boundingBox->last(); $leading = $font->leadingInPixels(); $blockWidth = $lines->longestLine()->widthInFont($font); $x = $pivot->getX(); $y = $font->hasFilename() ? $pivot->getY() + $font->capHeight() : $pivot->getY(); $x_adjustment = 0; foreach ($lines as $line) { $x_adjustment = $font->getAlign() == 'left' ? 0 : $blockWidth - $line->widthInFont($font); $x_adjustment = $font->getAlign() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; $x_adjustment = $font->getAlign() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; $position = new Point($x + $x_adjustment, $y); $position->rotate($font->getAngle(), $pivot); $line->setPosition($position); $y += $leading; } return $lines; } } src/Drivers/Abstract/Decoders/AbstractDecoder.php000064400000002167150731646300016021 0ustar00decode($input); } catch (DecoderException $e) { if (!$this->hasSuccessor()) { throw new DecoderException($e->getMessage()); } return $this->successor->handle($input); } return $decoded; } protected function hasSuccessor(): bool { return $this->successor !== null; } protected function inputType($input): AbstractType { return MimeSniffer::createFromString($input)->getType(); } } src/Drivers/Abstract/Encoders/AbstractEncoder.php000064400000001370150731646300016040 0ustar00quality = $quality; return $this; } public function getQuality(): int { return $this->quality; } } src/Drivers/Abstract/Modifiers/AbstractDrawModifier.php000064400000005114150731646300017214 0ustar00drawable; } protected function getBackgroundColor(): ?ColorInterface { try { $color = $this->handleInput($this->drawable->getBackgroundColor()); } catch (DecoderException $e) { return $this->handleInput('transparent'); } return $color; } protected function getBorderColor(): ?ColorInterface { try { $color = $this->handleInput($this->drawable->getBorderColor()); } catch (DecoderException $e) { return $this->handleInput('transparent'); } return $color; } public function polygon(): Polygon { if (!is_a($this->drawable(), Polygon::class)) { throw new GeometryException( 'Shape mismatch. Excepted Polygon::class, found ' . get_class($this->drawable()) ); } return $this->drawable(); } public function ellipse(): Ellipse { if (!is_a($this->drawable(), Ellipse::class)) { throw new GeometryException( 'Shape mismatch. Excepted Ellipse::class, found ' . get_class($this->drawable()) ); } return $this->drawable(); } public function line(): Line { if (!is_a($this->drawable(), Line::class)) { throw new GeometryException( 'Shape mismatch. Excepted Line::class, found ' . get_class($this->drawable()) ); } return $this->drawable(); } public function rectangle(): Rectangle { if (!is_a($this->drawable(), Rectangle::class)) { throw new GeometryException( 'Shape mismatch. Excepted Rectangle::class, found ' . get_class($this->drawable()) ); } return $this->drawable(); } } src/Drivers/Abstract/Modifiers/AbstractFitModifier.php000064400000001634150731646300017044 0ustar00getSize(); $crop = new Rectangle($this->width, $this->height); $crop = $crop->contain( $imagesize->getWidth(), $imagesize->getHeight() )->alignPivotTo($imagesize, $this->position); return $crop; } protected function getResizeSize(SizeInterface $size): SizeInterface { return $size->scale($this->width, $this->height); } } src/Drivers/Abstract/Modifiers/AbstractPadModifier.php000064400000001526150731646300017026 0ustar00getSize() ->contain($this->width, $this->height) ->alignPivotTo($this->getResizeSize($image), $this->position); } protected function getResizeSize(ImageInterface $image): SizeInterface { return new Rectangle($this->width, $this->height); } } src/Drivers/Abstract/Modifiers/AbstractRotateModifier.php000064400000001653150731646300017561 0ustar00angle, 360); } protected function backgroundColor(): ColorInterface { try { return $this->handleInput($this->background); } catch (DecoderException $e) { throw new TypeException("rotate(): Argument #2 must be a color value."); } } } src/Drivers/Gd/Color.php000064400000001736150731646300011106 0ustar00toArray()[0]; } public function green(): int { return $this->toArray()[1]; } public function blue(): int { return $this->toArray()[2]; } public function alpha(): float { return $this->toArray()[3]; } public function toArray(): array { $a = ($this->value >> 24) & 0x7F; $r = ($this->value >> 16) & 0xFF; $g = ($this->value >> 8) & 0xFF; $b = $this->value & 0xFF; $a = (float) round(1 - $a / 127, 2); return [$r, $g, $b, $a]; } public function toInt(): int { return $this->value; } } src/Drivers/Gd/Decoders/ArrayColorDecoder.php000064400000002102150731646300015107 0ustar00isValidColorArray($input)) { throw new DecoderException('Unable to decode input'); } if (count($input) === 3) { $input[] = 1; } list($r, $g, $b, $a) = $input; return new Color( ($this->opacityToGdAlpha($a) << 24) + ($r << 16) + ($g << 8) + $b ); } protected function opacityToGdAlpha(float $opacity): int { return intval(round($opacity * 127 * -1 + 127)); } } src/Drivers/Gd/Decoders/Base64ImageDecoder.php000064400000001247150731646300015032 0ustar00isValidBase64($input)) { throw new DecoderException('Unable to decode input'); } return parent::decode(base64_decode($input)); } } src/Drivers/Gd/Decoders/BinaryImageDecoder.php000064400000004076150731646300015235 0ustar00inputType($input)->isBinary()) { throw new DecoderException('Unable to decode input'); } if (is_a($this->inputType($input), ImageGif::class)) { return $this->decodeGif($input); // decode (animated) gif } $gd = @imagecreatefromstring($input); if ($gd === false) { throw new DecoderException('Unable to decode input'); } if (! imageistruecolor($gd)) { imagepalettetotruecolor($gd); } imagesavealpha($gd, true); return new Image(new Collection([new Frame($gd)])); } protected function decodeGif($input): ImageInterface { $image = new Image(new Collection()); $gif = GifDecoder::decode($input); if (!$gif->isAnimated()) { return $image->addFrame(new Frame(@imagecreatefromstring($input))); } $image->setLoops($gif->getMainApplicationExtension()?->getLoops()); $splitter = GifSplitter::create($gif)->split(); $delays = $splitter->getDelays(); foreach ($splitter->coalesceToResources() as $key => $gd) { $image->addFrame((new Frame($gd))->setDelay($delays[$key] / 100)); } return $image; } } src/Drivers/Gd/Decoders/DataUriImageDecoder.php000064400000001652150731646300015337 0ustar00decodeDataUri($input); if (! $uri->isValid()) { throw new DecoderException('Unable to decode input'); } if ($uri->isBase64Encoded()) { return parent::decode(base64_decode($uri->data())); } return parent::decode(urldecode($uri->data())); } } src/Drivers/Gd/Decoders/FilePathImageDecoder.php000064400000001532150731646300015477 0ustar00hexColorFromColorName($input); if (empty($hexcolor)) { throw new DecoderException('Unable to decode input'); } return parent::decode($hexcolor); } } src/Drivers/Gd/Decoders/ImageObjectDecoder.php000064400000001201150731646300015202 0ustar00[0-9]{1,3}), ?(?P[0-9]{1,3}), ?(?P[0-9]{1,3})\)$/i"; if ((bool) preg_match($pattern, $input, $matches)) { return parent::decode([$matches['r'], $matches['g'], $matches['b']]); } // rgba string like "rgba(200, 10, 30, 0.5)" $pattern = "/^rgba ?\(((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?((?P[0-9]{1,3})), ?(?P[0-9.]{1,4})\)$/i"; if ((bool) preg_match($pattern, $input, $matches)) { return parent::decode([$matches['r'], $matches['g'], $matches['b'], $matches['a']]); } throw new DecoderException('Unable to decode input'); } } src/Drivers/Gd/Decoders/TransparentColorDecoder.php000064400000001162150731646300016337 0ustar00getBuffered(function () use ($image) { imagebmp($image->getFrame()->getCore(), null, false); }); return new EncodedImage($data, 'image/bmp'); } } src/Drivers/Gd/Encoders/GifEncoder.php000064400000002242150731646300013570 0ustar00isAnimated()) { return $this->encodeAnimated($image); } $data = $this->getBuffered(function () use ($image) { imagegif($image->getFrame()->getCore()); }); return new EncodedImage($data, 'image/gif'); } protected function encodeAnimated($image): EncodedImage { $builder = GifBuilder::canvas( $image->getWidth(), $image->getHeight(), $image->getLoops() ); foreach ($image as $frame) { $source = $this->encode($frame->toImage()); $builder->addFrame($source, $frame->getDelay()); } return new EncodedImage($builder->encode(), 'image/gif'); } } src/Drivers/Gd/Encoders/JpegEncoder.php000064400000001311150731646300013744 0ustar00quality = $quality; } public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { imagejpeg($image->getFrame()->getCore(), null, $this->quality); }); return new EncodedImage($data, 'image/jpeg'); } } src/Drivers/Gd/Encoders/PngEncoder.php000064400000001134150731646300013606 0ustar00getBuffered(function () use ($image) { imagepng($image->getFrame()->getCore(), null, -1); }); return new EncodedImage($data, 'image/png'); } } src/Drivers/Gd/Encoders/WebpEncoder.php000064400000001311150731646300013754 0ustar00quality = $quality; } public function encode(ImageInterface $image): EncodedImage { $data = $this->getBuffered(function () use ($image) { imagewebp($image->getFrame()->getCore(), null, $this->quality); }); return new EncodedImage($data, 'image/webp'); } } src/Drivers/Gd/Font.php000064400000003773150731646300010741 0ustar00hasFilename()) { // calculate box size from gd font $box = new Rectangle(0, 0); $chars = mb_strlen($text); if ($chars > 0) { $box->setWidth($chars * $this->getGdFontWidth()); $box->setHeight($this->getGdFontHeight()); } return $box; } // calculate box size from font file with angle 0 $box = imageftbbox( $this->getSize(), 0, $this->getFilename(), $text ); // build polygon from points $polygon = new Polygon(); $polygon->addPoint(new Point($box[6], $box[7])); $polygon->addPoint(new Point($box[4], $box[5])); $polygon->addPoint(new Point($box[2], $box[3])); $polygon->addPoint(new Point($box[0], $box[1])); return $polygon; } public function getGdFont(): int { if (is_numeric($this->filename)) { return $this->filename; } return 1; } protected function getGdFontWidth(): int { return $this->getGdFont() + 4; } protected function getGdFontHeight(): int { switch ($this->getGdFont()) { case 2: return 14; case 3: return 14; case 4: return 16; case 5: return 16; default: case 1: return 8; } } } src/Drivers/Gd/Frame.php000064400000004123150731646300011053 0ustar00core = $core; return $this; } public function getCore(): GdImage { return $this->core; } public function unsetCore(): void { unset($this->core); } public function getSize(): SizeInterface { return new Rectangle(imagesx($this->core), imagesy($this->core)); } public function getDelay(): float { return $this->delay; } public function setDelay(float $delay): FrameInterface { $this->delay = $delay; return $this; } public function getDispose(): int { return $this->dispose; } public function setDispose(int $dispose): FrameInterface { $this->dispose = $dispose; return $this; } public function setOffset(int $left, int $top): FrameInterface { $this->offset_left = $left; $this->offset_top = $top; return $this; } public function getOffsetLeft(): int { return $this->offset_left; } public function setOffsetLeft(int $offset): FrameInterface { $this->offset_left = $offset; return $this; } public function getOffsetTop(): int { return $this->offset_top; } public function setOffsetTop(int $offset): FrameInterface { $this->offset_top = $offset; return $this; } public function toImage(): ImageInterface { return new Image(new Collection([$this])); } } src/Drivers/Gd/Image.php000064400000003233150731646300011044 0ustar00frames; } public function count(): int { return $this->frames->count(); } public function isAnimated(): bool { return $this->count() > 1; } public function getLoops(): int { return $this->loops; } public function setLoops(int $count): self { $this->loops = $count; return $this; } public function getFrame(int $key = 0): ?FrameInterface { return $this->frames->get($key); } public function addFrame(FrameInterface $frame): ImageInterface { $this->frames->push($frame); return $this; } public function getWidth(): int { return imagesx($this->getFrame()->getCore()); } public function getHeight(): int { return imagesy($this->getFrame()->getCore()); } public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return new Color(imagecolorat($frame->getCore(), $x, $y)); } return null; } } src/Drivers/Gd/ImageFactory.php000064400000001350150731646300012372 0ustar00newCore($width, $height)) ]) ); } public function newCore(int $width, int $height) { $core = imagecreatetruecolor($width, $height); $color = imagecolorallocatealpha($core, 0, 0, 0, 127); imagefill($core, 0, 0, $color); imagesavealpha($core, true); return $core; } } src/Drivers/Gd/InputHandler.php000064400000002154150731646300012420 0ustar00blurFrame($frame); } return $image; } protected function blurFrame(FrameInterface $frame): void { for ($i = 0; $i < $this->amount; $i++) { imagefilter($frame->getCore(), IMG_FILTER_GAUSSIAN_BLUR); } } } src/Drivers/Gd/Modifiers/BrightnessModifier.php000064400000001037150731646300015532 0ustar00getCore(), IMG_FILTER_BRIGHTNESS, intval($this->level * 2.55)); } return $image; } } src/Drivers/Gd/Modifiers/ContrastModifier.php000064400000001023150731646300015212 0ustar00getCore(), IMG_FILTER_CONTRAST, ($this->level * -1)); } return $image; } } src/Drivers/Gd/Modifiers/DestroyModifier.php000064400000000617150731646300015056 0ustar00unsetCore(); } return $image; } } src/Drivers/Gd/Modifiers/DrawEllipseModifier.php000064400000003761150731646300015643 0ustar00eachFrame(function ($frame) { if ($this->ellipse()->hasBorder()) { // slightly smaller ellipse to keep 1px bordered edges clean if ($this->ellipse()->hasBackgroundColor()) { imagefilledellipse( $frame->getCore(), $this->position->getX(), $this->position->getY(), $this->ellipse()->getWidth() - 1, $this->ellipse()->getHeight() - 1, $this->getBackgroundColor()->toInt() ); } imagesetthickness($frame->getCore(), $this->ellipse()->getBorderSize()); // gd's imageellipse doesn't respect imagesetthickness so i use // imagearc with 359.9 degrees here. imagearc( $frame->getCore(), $this->position->getX(), $this->position->getY(), $this->ellipse()->getWidth(), $this->ellipse()->getHeight(), 0, 359.99, $this->getBorderColor()->toInt() ); } else { imagefilledellipse( $frame->getCore(), $this->position->getX(), $this->position->getY(), $this->ellipse()->getWidth(), $this->ellipse()->getHeight(), $this->getBackgroundColor()->toInt() ); } }); } } src/Drivers/Gd/Modifiers/DrawLineModifier.php000064400000001424150731646300015127 0ustar00eachFrame(function ($frame) { imageline( $frame->getCore(), $this->line()->getStart()->getX(), $this->line()->getStart()->getY(), $this->line()->getEnd()->getX(), $this->line()->getEnd()->getY(), $this->getBackgroundColor()->toInt() ); }); } } src/Drivers/Gd/Modifiers/DrawPixelModifier.php000064400000001516150731646300015323 0ustar00handleInput($this->color); return $image->eachFrame(function ($frame) use ($color) { imagesetpixel( $frame->getCore(), $this->position->getX(), $this->position->getY(), $color->toInt() ); }); } } src/Drivers/Gd/Modifiers/DrawPolygonModifier.php000064400000002464150731646300015674 0ustar00eachFrame(function ($frame) { if ($this->polygon()->hasBackgroundColor()) { imagefilledpolygon( $frame->getCore(), $this->polygon()->toArray(), $this->polygon()->count(), $this->getBackgroundColor()->toInt() ); } if ($this->polygon()->hasBorder()) { imagesetthickness($frame->getCore(), $this->polygon()->getBorderSize()); imagepolygon( $frame->getCore(), $this->polygon()->toArray(), $this->polygon()->count(), $this->getBorderColor()->toInt() ); } }); } } src/Drivers/Gd/Modifiers/DrawRectangleModifier.php000064400000003135150731646300016145 0ustar00eachFrame(function ($frame) { // draw background if ($this->rectangle()->hasBackgroundColor()) { imagefilledrectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), $this->getBackgroundColor()->toInt() ); } if ($this->rectangle()->hasBorder()) { // draw border imagesetthickness($frame->getCore(), $this->rectangle()->getBorderSize()); imagerectangle( $frame->getCore(), $this->position->getX(), $this->position->getY(), $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY(), $this->getBorderColor()->toInt() ); } }); return $image; } } src/Drivers/Gd/Modifiers/FillModifier.php000064400000002721150731646300014311 0ustar00hasPosition()) { $this->floodFillWithColor($frame); } else { $this->fillAllWithColor($frame); } } return $image; } protected function floodFillWithColor(Frame $frame): void { imagefill( $frame->getCore(), $this->position->getX(), $this->position->getY(), $this->color->toInt() ); } protected function fillAllWithColor(Frame $frame): void { imagealphablending($frame->getCore(), true); imagefilledrectangle( $frame->getCore(), 0, 0, $frame->getSize()->getWidth() - 1, $frame->getSize()->getHeight() - 1, $this->color->toInt() ); } protected function hasPosition(): bool { return !empty($this->position); } } src/Drivers/Gd/Modifiers/FitDownModifier.php000064400000000456150731646300015000 0ustar00scaleDown($this->width, $this->height); } } src/Drivers/Gd/Modifiers/FitModifier.php000064400000005045150731646300014147 0ustar00getCropSize($image); $resize = $this->getResizeSize($crop); foreach ($image as $frame) { $this->modifyFrame($frame, $crop, $resize); } return $image; } protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image $modified = imagecreatetruecolor( $resize->getWidth(), $resize->getHeight() ); // get current image $current = $frame->getCore(); // preserve transparency imagealphablending($modified, false); $transIndex = imagecolortransparent($current); if ($transIndex != -1) { $rgba = imagecolorsforindex($modified, $transIndex); $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); imagefill($modified, 0, 0, $transColor); } else { imagesavealpha($modified, true); } // copy content from resource imagecopyresampled( $modified, $current, 0, 0, $crop->getPivot()->getX(), $crop->getPivot()->getY(), $resize->getWidth(), $resize->getHeight(), $crop->getWidth(), $crop->getHeight() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); for ($y = 0; $y < $resize->getHeight(); ++$y) { for ($x = 0; $x < $resize->getWidth(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, $x, $y, $transIndex ); } } } } // set new content as resource $frame->setCore($modified); } } src/Drivers/Gd/Modifiers/FlipModifier.php000064400000000650150731646300014314 0ustar00getCore(), IMG_FLIP_VERTICAL); } return $image; } } src/Drivers/Gd/Modifiers/FlopModifier.php000064400000000652150731646300014324 0ustar00getCore(), IMG_FLIP_HORIZONTAL); } return $image; } } src/Drivers/Gd/Modifiers/GammaModifier.php000064400000000777150731646300014456 0ustar00getCore(), 1, $this->gamma); } return $image; } } src/Drivers/Gd/Modifiers/GreyscaleModifier.php000064400000000662150731646300015343 0ustar00getCore(), IMG_FILTER_GRAYSCALE); } return $image; } } src/Drivers/Gd/Modifiers/InvertModifier.php000064400000000654150731646300014675 0ustar00getCore(), IMG_FILTER_NEGATE); } return $image; } } src/Drivers/Gd/Modifiers/PadDownModifier.php000064400000001353150731646300014757 0ustar00getResizeSize($image); return $image->getSize() ->contain($resize->width(), $resize->height()) ->alignPivotTo($resize, $this->position); } protected function getResizeSize(ImageInterface $image): SizeInterface { return (new Rectangle($this->width, $this->height)) ->resizeDown($image->getWidth(), $image->getHeight()); } } src/Drivers/Gd/Modifiers/PadModifier.php000064400000003651150731646300014132 0ustar00getCropSize($image); $resize = $this->getResizeSize($image); $background = $this->handleInput($this->background); foreach ($image as $frame) { $this->modify($frame, $crop, $resize, $background); } return $image; } protected function modify( FrameInterface $frame, SizeInterface $crop, SizeInterface $resize, ColorInterface $background ): void { // create new image $modified = imagecreatetruecolor( $resize->getWidth(), $resize->getHeight() ); imagefill($modified, 0, 0, $background->toInt()); // get current image $current = $frame->getCore(); // preserve transparency imagealphablending($modified, false); imagesavealpha($modified, true); // copy content from resource imagecopyresampled( $modified, $current, $crop->getPivot()->getX(), $crop->getPivot()->getY(), 0, 0, $crop->getWidth(), $crop->getHeight(), $frame->getSize()->getWidth(), $frame->getSize()->getHeight() ); imagedestroy($current); // set new content as recource $frame->setCore($modified); } } src/Drivers/Gd/Modifiers/PixelateModifier.php000064400000001102150731646300015166 0ustar00getCore(), IMG_FILTER_PIXELATE, $this->size, true); } return $image; } } src/Drivers/Gd/Modifiers/PlaceModifier.php000064400000003341150731646300014446 0ustar00decodeWatermark(); $position = $this->getPosition($image, $watermark); foreach ($image as $frame) { imagealphablending($frame->getCore(), true); imagecopy( $frame->getCore(), $watermark->getFrame()->getCore(), $position->getX(), $position->getY(), 0, 0, $watermark->getWidth(), $watermark->getHeight() ); } return $image; } protected function decodeWatermark(): Image { return $this->resolveDriverClass('InputHandler')->handle($this->element); } protected function getPosition(ImageInterface $image, ImageInterface $watermark): PointInterface { $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->movePivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } } src/Drivers/Gd/Modifiers/ResizeDownModifier.php000064400000000567150731646300015522 0ustar00getSize()->resizeDown($this->width, $this->height); } } src/Drivers/Gd/Modifiers/ResizeModifier.php000064400000005310150731646300014661 0ustar00getAdjustedSize($image); foreach ($image as $frame) { $this->resizeFrame($frame, $resizeTo); } return $image; } protected function getAdjustedSize(ImageInterface $image): SizeInterface { return $image->getSize()->resize($this->width, $this->height); } protected function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): void { // create new image $modified = imagecreatetruecolor( $resizeTo->getWidth(), $resizeTo->getHeight() ); // get current image $current = $frame->getCore(); // preserve transparency imagealphablending($modified, false); $transIndex = imagecolortransparent($current); if ($transIndex != -1) { $rgba = imagecolorsforindex($modified, $transIndex); $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); imagefill($modified, 0, 0, $transColor); } else { imagesavealpha($modified, true); } // copy content from resource imagecopyresampled( $modified, $current, $resizeTo->getPivot()->getX(), $resizeTo->getPivot()->getY(), 0, 0, $resizeTo->getWidth(), $resizeTo->getHeight(), $frame->getSize()->getWidth(), $frame->getSize()->getHeight() ); imagedestroy($current); if ($transIndex != -1) { // @todo refactor because of duplication imagecolortransparent($modified, $transIndex); for ($y = 0; $y < $resizeTo->getHeight(); ++$y) { for ($x = 0; $x < $resizeTo->getWidth(); ++$x) { if (((imagecolorat($modified, $x, $y) >> 24) & 0x7F) >= 100) { imagesetpixel( $modified, $x, $y, $transIndex ); } } } } // set new content as recource $frame->setCore($modified); } } src/Drivers/Gd/Modifiers/RotateModifier.php000064400000001272150731646300014661 0ustar00setCore( imagerotate( $frame->getCore(), $this->rotationAngle(), $this->backgroundColor()->toInt() ) ); } return $image; } } src/Drivers/Gd/Modifiers/ScaleDownModifier.php000064400000000565150731646300015306 0ustar00getSize()->scaleDown($this->width, $this->height); } } src/Drivers/Gd/Modifiers/ScaleModifier.php000064400000000555150731646300014455 0ustar00getSize()->scale($this->width, $this->height); } } src/Drivers/Gd/Modifiers/SharpenModifier.php000064400000001550150731646300015022 0ustar00matrix(); foreach ($image as $frame) { imageconvolution($frame->getCore(), $matrix, 1, 0); } return $image; } protected function matrix(): array { $min = $this->amount >= 10 ? $this->amount * -0.01 : 0; $max = $this->amount * -0.025; $abs = ((4 * $min + 4 * $max) * -1) + 1; return [ [$min, $max, $min], [$max, $abs, $max], [$min, $max, $min] ]; } } src/Drivers/Gd/Modifiers/TextWriter.php000064400000003770150731646300014072 0ustar00getAlignedTextBlock(); foreach ($image as $frame) { if ($this->font->hasFilename()) { foreach ($lines as $line) { imagettftext( $frame->getCore(), $this->getFont()->getSize(), $this->getFont()->getAngle() * (-1), $line->getPosition()->getX(), $line->getPosition()->getY(), $this->getFont()->getColor()->toInt(), $this->getFont()->getFilename(), $line ); } // debug // $lines = new TextBlock($this->text); // $box = $lines->getBoundingBox($this->font, $this->position); // imagepolygon($frame->getCore(), $box->toArray(), 0); } else { foreach ($lines as $line) { imagestring( $frame->getCore(), $this->getFont()->getGdFont(), $line->getPosition()->getX(), $line->getPosition()->getY(), $line, $this->font->getColor()->toInt() ); } } } return $image; } protected function getFont(): FontInterface { if (!is_a($this->font, Font::class)) { throw new FontException('Font is not compatible to current driver.'); } return $this->font; } } src/Drivers/Imagick/Color.php000064400000002541150731646300012113 0ustar00pixel; } public function red(): int { return intval(round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255)); } public function green(): int { return intval(round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255)); } public function blue(): int { return intval(round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255)); } public function alpha(): float { return round($this->pixel->getColorValue(Imagick::COLOR_ALPHA), 2); } public function toArray(): array { return [ $this->red(), $this->green(), $this->blue(), $this->alpha() ]; } public function toInt(): int { $r = $this->red(); $g = $this->green(); $b = $this->blue(); $a = intval(round($this->alpha() * 255)); return intval(($a << 24) + ($r << 16) + ($g << 8) + $b); } } src/Drivers/Imagick/Decoders/ArrayColorDecoder.php000064400000001767150731646300016141 0ustar00isValidColorArray($input)) { throw new DecoderException('Unable to decode input'); } if (count($input) === 3) { $input[] = 1; } list($r, $g, $b, $a) = $input; $pixel = new ImagickPixel( sprintf('rgba(%d, %d, %d, %.2F)', $r, $g, $b, $a) ); return new Color($pixel); } } src/Drivers/Imagick/Decoders/Base64ImageDecoder.php000064400000001254150731646300016042 0ustar00isValidBase64($input)) { throw new DecoderException('Unable to decode input'); } return parent::decode(base64_decode($input)); } } src/Drivers/Imagick/Decoders/BinaryImageDecoder.php000064400000002014150731646300016235 0ustar00inputType($input)->isBinary()) { throw new DecoderException('Unable to decode input'); } $imagick = new Imagick(); $imagick->readImageBlob($input); $imagick = $imagick->coalesceImages(); $image = new Image($imagick); $image->setLoops($imagick->getImageIterations()); return $image; } } src/Drivers/Imagick/Decoders/DataUriImageDecoder.php000064400000001657150731646300016356 0ustar00decodeDataUri($input); if (! $uri->isValid()) { throw new DecoderException('Unable to decode input'); } if ($uri->isBase64Encoded()) { return parent::decode(base64_decode($uri->data())); } return parent::decode(urldecode($uri->data())); } } src/Drivers/Imagick/Decoders/FilePathImageDecoder.php000064400000001537150731646300016516 0ustar00hexColorFromColorName($input); if (empty($hexcolor)) { throw new DecoderException('Unable to decode input'); } return parent::decode($hexcolor); } } src/Drivers/Imagick/Decoders/ImageObjectDecoder.php000064400000001206150731646300016221 0ustar00getFrame()->getCore(); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); return new EncodedImage($imagick->getImagesBlob(), 'image/bmp'); } } src/Drivers/Imagick/Encoders/GifEncoder.php000064400000002101150731646300014574 0ustar00getImagick(); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); $imagick = $imagick->deconstructImages(); return new EncodedImage($imagick->getImagesBlob(), 'image/gif'); } } src/Drivers/Imagick/Encoders/JpegEncoder.php000064400000002150150731646300014760 0ustar00quality = $quality; } public function encode(ImageInterface $image): EncodedImage { $format = 'jpeg'; $compression = Imagick::COMPRESSION_JPEG; $imagick = $image->getFrame()->getCore(); $imagick->setImageBackgroundColor('white'); $imagick->setBackgroundColor('white'); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg'); } } src/Drivers/Imagick/Encoders/PngEncoder.php000064400000001454150731646300014625 0ustar00getFrame()->getCore(); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); return new EncodedImage($imagick->getImagesBlob(), 'image/png'); } } src/Drivers/Imagick/Encoders/WebpEncoder.php000064400000002165150731646300014776 0ustar00quality = $quality; } public function encode(ImageInterface $image): EncodedImage { $format = 'webp'; $compression = Imagick::COMPRESSION_ZIP; $imagick = $image->getFrame()->getCore(); $imagick->setImageBackgroundColor(new ImagickPixel('transparent')); $imagick = $imagick->mergeImageLayers(Imagick::LAYERMETHOD_MERGE); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); $imagick->setImageCompressionQuality($this->quality); return new EncodedImage($imagick->getImagesBlob(), 'image/webp'); } } src/Drivers/Imagick/Font.php000064400000003363150731646300011746 0ustar00hasFilename()) { throw new FontException('No font file specified.'); } $draw = new ImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $draw->setFont($this->getFilename()); $draw->setFontSize($this->getSize()); $draw->setFillColor($this->getColor()->getPixel()); $draw->setTextAlignment(Imagick::ALIGN_LEFT); return $draw; } public function getColor(): ?ColorInterface { $color = parent::getColor(); if (!is_a($color, Color::class)) { throw new FontException('Font is not compatible to current driver.'); } return $color; } /** * Calculate box size of current font * * @return Polygon */ public function getBoxSize(string $text): Polygon { // no text - no box size if (mb_strlen($text) === 0) { return (new Rectangle(0, 0)); } $draw = $this->toImagickDraw(); $draw->setStrokeAntialias(true); $draw->setTextAntialias(true); $dimensions = (new Imagick())->queryFontMetrics($draw, $text); return (new Rectangle( intval(round($dimensions['textWidth'])), intval(round($dimensions['ascender'] + $dimensions['descender'])), )); } } src/Drivers/Imagick/Frame.php000064400000004116150731646300012067 0ustar00core = $core; return $this; } public function getCore(): Imagick { return $this->core; } public function getSize(): SizeInterface { return new Rectangle( $this->core->getImageWidth(), $this->core->getImageHeight() ); } public function getDelay(): float { return $this->core->getImageDelay() / 100; } public function setDelay(float $delay): FrameInterface { $this->core->setImageDelay(intval(round($delay * 100))); return $this; } public function getDispose(): int { return $this->core->getImageDispose(); } public function setDispose(int $dispose): FrameInterface { $this->core->setImageDispose($dispose); return $this; } public function setOffset(int $left, int $top): FrameInterface { $this->core->setImagePage( $this->core->getImageWidth(), $this->core->getImageHeight(), $left, $top ); return $this; } public function getOffsetLeft(): int { return $this->core->getImagePage()['x']; } public function setOffsetLeft(int $offset): FrameInterface { return $this->setOffset($offset, $this->getOffsetTop()); } public function getOffsetTop(): int { return $this->core->getImagePage()['y']; } public function setOffsetTop(int $offset): FrameInterface { return $this->setOffset($this->getOffsetLeft(), $offset); } public function toImage(): ImageInterface { return new Image($this->getCore()); } } src/Drivers/Imagick/Image.php000064400000005574150731646300012070 0ustar00imagick; } public function getFrame(int $key = 0): ?FrameInterface { try { $this->imagick->setIteratorIndex($key); } catch (ImagickException $e) { return null; } return new Frame($this->imagick->current()); } public function addFrame(FrameInterface $frame): ImageInterface { $imagick = $frame->getCore(); $imagick->setImageDelay($frame->getDelay()); $imagick->setImageDispose($frame->getDispose()); $size = $frame->getSize(); $imagick->setImagePage( $size->getWidth(), $size->getHeight(), $frame->getOffsetLeft(), $frame->getOffsetTop() ); $this->imagick->addImage($imagick); return $this; } public function setLoops(int $count): ImageInterface { $this->imagick->setImageIterations($count); return $this; } public function getLoops(): int { return $this->imagick->getImageIterations(); } public function isAnimated(): bool { return $this->count() > 1; } public function count(): int { return $this->imagick->getNumberImages(); } public function current() { $this->imagick->setIteratorIndex($this->iteratorIndex); return new Frame($this->imagick->current()); } public function key() { return $this->iteratorIndex; } public function next(): void { $this->iteratorIndex = $this->iteratorIndex + 1; } public function rewind(): void { $this->iteratorIndex = 0; } public function valid(): bool { try { $result = $this->imagick->setIteratorIndex($this->iteratorIndex); } catch (ImagickException $e) { return false; } return $result; } public function getWidth(): int { return $this->getFrame()->getCore()->getImageWidth(); } public function getHeight(): int { return $this->getFrame()->getCore()->getImageHeight(); } public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface { if ($frame = $this->getFrame($frame_key)) { return new Color($frame->getCore()->getImagePixelColor($x, $y)); } return null; } } src/Drivers/Imagick/ImageFactory.php000064400000001407150731646300013407 0ustar00newCore($width, $height)); } public function newCore(int $width, int $height) { $imagick = new Imagick(); $imagick->newImage($width, $height, new ImagickPixel('rgba(0, 0, 0, 0)'), 'png'); $imagick->setType(Imagick::IMGTYPE_UNDEFINED); $imagick->setImageType(Imagick::IMGTYPE_UNDEFINED); $imagick->setColorspace(Imagick::COLORSPACE_UNDEFINED); return $imagick; } } src/Drivers/Imagick/InputHandler.php000064400000002161150731646300013430 0ustar00getCore()->blurImage(1 * $this->amount, 0.5 * $this->amount); } return $image; } } src/Drivers/Imagick/Modifiers/BrightnessModifier.php000064400000001020150731646300016534 0ustar00getCore()->modulateImage(100 + $this->level, 100, 100); } return $image; } } src/Drivers/Imagick/Modifiers/ContrastModifier.php000064400000001045150731646300016230 0ustar00getCore()->sigmoidalContrastImage($this->level > 0, abs($this->level / 4), 0); } return $image; } } src/Drivers/Imagick/Modifiers/DestroyModifier.php000064400000000633150731646300016066 0ustar00getCore()->clear(); } return $image; } } src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php000064400000003452150731646300016652 0ustar00eachFrame(function ($frame) { $drawing = new ImagickDraw(); $drawing->setFillColor($this->getBackgroundColor()->getPixel()); if ($this->ellipse()->hasBorder()) { $drawing->setStrokeWidth($this->ellipse()->getBorderSize()); $drawing->setStrokeColor($this->getBorderColor()->getPixel()); } $drawing->ellipse( $this->position->getX(), $this->position->getY(), $this->ellipse()->getWidth() / 2, $this->ellipse()->getHeight() / 2, 0, 360 ); $frame->getCore()->drawImage($drawing); }); } protected function getBackgroundColor(): ColorInterface { $color = parent::getBackgroundColor(); if (!is_a($color, Color::class)) { throw new DecoderException('Unable to decode background color.'); } return $color; } protected function getBorderColor(): ColorInterface { $color = parent::getBorderColor(); if (!is_a($color, Color::class)) { throw new DecoderException('Unable to decode border color.'); } return $color; } } src/Drivers/Imagick/Modifiers/DrawLineModifier.php000064400000001661150731646300016144 0ustar00setStrokeColor($this->getBackgroundColor()->getPixel()); $drawing->setStrokeWidth($this->line()->getWidth()); $drawing->line( $this->line()->getStart()->getX(), $this->line()->getStart()->getY(), $this->line()->getEnd()->getX(), $this->line()->getEnd()->getY(), ); return $image->eachFrame(function ($frame) use ($drawing) { $frame->getCore()->drawImage($drawing); }); } } src/Drivers/Imagick/Modifiers/DrawPixelModifier.php000064400000001567150731646300016343 0ustar00handleInput($this->color); $pixel = new ImagickDraw(); $pixel->setFillColor($color->getPixel()); $pixel->point($this->position->getX(), $this->position->getY()); return $image->eachFrame(function ($frame) use ($pixel) { $frame->getCore()->drawImage($pixel); }); } } src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php000064400000002527150731646300016706 0ustar00polygon()->hasBackgroundColor()) { $drawing->setFillColor($this->getBackgroundColor()->getPixel()); } if ($this->polygon()->hasBorder()) { $drawing->setStrokeColor($this->getBorderColor()->getPixel()); $drawing->setStrokeWidth($this->polygon()->getBorderSize()); } $drawing->polygon($this->points()); return $image->eachFrame(function ($frame) use ($drawing) { $frame->getCore()->drawImage($drawing); }); } private function points(): array { $points = []; foreach ($this->polygon() as $point) { $points[] = ['x' => $point->getX(), 'y' => $point->getY()]; } return $points; } } src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php000064400000003576150731646300017170 0ustar00setFillColor($this->getBackgroundColor()->getPixel()); if ($this->rectangle()->hasBorder()) { $drawing->setStrokeColor($this->getBorderColor()->getPixel()); $drawing->setStrokeWidth($this->rectangle()->getBorderSize()); } // build rectangle $drawing->rectangle( $this->position->getX(), $this->position->getY(), $this->position->getX() + $this->rectangle()->bottomRightPoint()->getX(), $this->position->getY() + $this->rectangle()->bottomRightPoint()->getY() ); $image->eachFrame(function ($frame) use ($drawing) { $frame->getCore()->drawImage($drawing); }); return $image; } protected function getBackgroundColor(): ColorInterface { $color = parent::getBackgroundColor(); if (!is_a($color, Color::class)) { throw new DecoderException('Unable to decode background color.'); } return $color; } protected function getBorderColor(): ColorInterface { $color = parent::getBorderColor(); if (!is_a($color, Color::class)) { throw new DecoderException('Unable to decode border color.'); } return $color; } } src/Drivers/Imagick/Modifiers/FillModifier.php000064400000003366150731646300015331 0ustar00hasPosition()) { $this->floodFillWithColor($frame); } else { $this->fillAllWithColor($frame); } } return $image; } protected function floodFillWithColor(Frame $frame): void { $target = $frame->getCore()->getImagePixelColor( $this->position->getX(), $this->position->getY() ); $frame->getCore()->floodfillPaintImage( $this->color->getPixel(), 100, $target, $this->position->getX(), $this->position->getY(), false, Imagick::CHANNEL_ALL ); } protected function fillAllWithColor(Frame $frame): void { $draw = new ImagickDraw(); $draw->setFillColor($this->color->getPixel()); $draw->rectangle( 0, 0, $frame->getCore()->getImageWidth(), $frame->getCore()->getImageHeight() ); $frame->getCore()->drawImage($draw); } protected function hasPosition(): bool { return !empty($this->position); } } src/Drivers/Imagick/Modifiers/FitDownModifier.php000064400000000463150731646300016010 0ustar00scaleDown($this->width, $this->height); } } src/Drivers/Imagick/Modifiers/FitModifier.php000064400000001612150731646300015155 0ustar00getCropSize($image); $resize = $this->getResizeSize($crop); foreach ($image as $frame) { $frame->getCore()->extentImage( $crop->getWidth(), $crop->getHeight(), $crop->getPivot()->getX(), $crop->getPivot()->getY() ); $frame->getCore()->scaleImage( $resize->getWidth(), $resize->getHeight() ); } return $image; } } src/Drivers/Imagick/Modifiers/FlipModifier.php000064400000000634150731646300015330 0ustar00getCore()->flipImage(); } return $image; } } src/Drivers/Imagick/Modifiers/FlopModifier.php000064400000000634150731646300015336 0ustar00getCore()->flopImage(); } return $image; } } src/Drivers/Imagick/Modifiers/GammaModifier.php000064400000000772150731646300015463 0ustar00getCore()->gammaImage($this->gamma); } return $image; } } src/Drivers/Imagick/Modifiers/GreyscaleModifier.php000064400000000660150731646300016353 0ustar00getCore()->modulateImage(100, 0, 100); } return $image; } } src/Drivers/Imagick/Modifiers/InvertModifier.php000064400000000645150731646300015707 0ustar00getCore()->negateImage(false); } return $image; } } src/Drivers/Imagick/Modifiers/PadDownModifier.php000064400000001360150731646300015767 0ustar00getResizeSize($image); return $image->getSize() ->contain($resize->width(), $resize->height()) ->alignPivotTo($resize, $this->position); } protected function getResizeSize(ImageInterface $image): SizeInterface { return (new Rectangle($this->width, $this->height)) ->resizeDown($image->getWidth(), $image->getHeight()); } } src/Drivers/Imagick/Modifiers/PadModifier.php000064400000005573150731646300015151 0ustar00getResizeSize($image); $crop = $this->getCropSize($image); $background = $this->handleInput($this->background); if (!is_a($background, Color::class)) { throw new DecoderException('Unable to decode backgroud color.'); } foreach ($image as $frame) { // resize current core $frame->getCore()->scaleImage( $crop->getWidth(), $crop->getHeight() ); // create new canvas, to get newly emerged background color $canvas = $this->buildBaseCanvas($crop, $resize, $background); // place current core onto canvas $canvas->compositeImage( $frame->getCore(), Imagick::COMPOSITE_DEFAULT, $crop->getPivot()->getX(), $crop->getPivot()->getY() ); // replace core $frame->getCore()->destroy(); $frame->setCore($canvas); } return $image; } protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, Color $background): Imagick { // build base canvas in target size $canvas = $this->imageFactory()->newCore( $resize->getWidth(), $resize->getHeight() ); // draw background color on canvas $draw = new ImagickDraw(); $draw->setFillColor($background->getPixel()); $draw->rectangle(0, 0, $canvas->getImageWidth(), $canvas->getImageHeight()); $canvas->drawImage($draw); // make area where image is placed transparent to keep // transparency even if background-color is set $draw = new ImagickDraw(); $fill = $background->toHex('#') == '#ff0000' ? '#00ff00' : '#ff0000'; $draw->setFillColor($fill); $draw->rectangle( $crop->getPivot()->getX(), $crop->getPivot()->getY(), $crop->getPivot()->getX() + $crop->getWidth() - 1, $crop->getPivot()->getY() + $crop->getHeight() - 1 ); $canvas->drawImage($draw); $canvas->transparentPaintImage($fill, 0, 0, false); return $canvas; } } src/Drivers/Imagick/Modifiers/PixelateModifier.php000064400000001562150731646300016212 0ustar00pixelateFrame($frame); } return $image; } protected function pixelateFrame(Frame $frame): void { $size = $frame->getSize(); $frame->getCore()->scaleImage( max(1, ($size->getWidth() / $this->size)), max(1, ($size->getHeight() / $this->size)) ); $frame->getCore()->scaleImage($size->getWidth(), $size->getHeight()); } } src/Drivers/Imagick/Modifiers/PlaceModifier.php000064400000003125150731646300015460 0ustar00decodeWatermark(); $position = $this->getPosition($image, $watermark); foreach ($image as $frame) { $frame->getCore()->compositeImage( $watermark->getFrame()->getCore(), Imagick::COMPOSITE_DEFAULT, $position->getX(), $position->getY() ); } return $image; } protected function decodeWatermark(): Image { return $this->resolveDriverClass('InputHandler')->handle($this->element); } protected function getPosition(ImageInterface $image, Image $watermark): PointInterface { $image_size = $image->getSize()->movePivot($this->position, $this->offset_x, $this->offset_y); $watermark_size = $watermark->getSize()->movePivot($this->position); return $image_size->getRelativePositionTo($watermark_size); } } src/Drivers/Imagick/Modifiers/ResizeDownModifier.php000064400000000574150731646300016532 0ustar00getSize()->resizeDown($this->width, $this->height); } } src/Drivers/Imagick/Modifiers/ResizeModifier.php000064400000001566150731646300015704 0ustar00getAdjustedSize($image); foreach ($image as $frame) { $frame->getCore()->scaleImage( $resizeTo->getWidth(), $resizeTo->getHeight() ); } return $image; } protected function getAdjustedSize(ImageInterface $image): SizeInterface { return $image->getSize()->resize($this->width, $this->height); } } src/Drivers/Imagick/Modifiers/RotateModifier.php000064400000001757150731646300015703 0ustar00backgroundColor(); if (!is_a($background, Color::class)) { throw new DecoderException('Unable to decode given background color.'); } foreach ($image as $frame) { $frame->getCore()->rotateImage( $background->getPixel(), $this->rotationAngle() ); } return $image; } protected function rotationAngle(): float { return parent::rotationAngle() * -1; } } src/Drivers/Imagick/Modifiers/ScaleDownModifier.php000064400000000572150731646300016316 0ustar00getSize()->scaleDown($this->width, $this->height); } } src/Drivers/Imagick/Modifiers/ScaleModifier.php000064400000000562150731646300015465 0ustar00getSize()->scale($this->width, $this->height); } } src/Drivers/Imagick/Modifiers/SharpenModifier.php000064400000001022150731646300016026 0ustar00getCore()->unsharpMaskImage(1, 1, $this->amount / 6.25, 0); } return $image; } } src/Drivers/Imagick/Modifiers/TextWriter.php000064400000003332150731646300015076 0ustar00getAlignedTextBlock(); foreach ($image as $frame) { foreach ($lines as $line) { $frame->getCore()->annotateImage( $this->getFont()->toImagickDraw(), $line->getPosition()->getX(), $line->getPosition()->getY(), $this->getFont()->getAngle(), $line ); } // debug // $lines = new TextBlock($this->text); // $box = $lines->getBoundingBox($this->font, $this->position); // $points = []; // foreach (array_chunk($box->toArray(), 2) as $p) { // $points[] = ['x' => $p[0], 'y' => $p[1]]; // } // $draw = new \ImagickDraw(); // $draw->setStrokeOpacity(1); // $draw->setStrokeColor('black'); // $draw->setFillColor('transparent'); // $draw->polygon($points); // $frame->getCore()->drawImage($draw); } return $image; } protected function getFont(): FontInterface { if (!is_a($this->font, Font::class)) { throw new FontException('Font is not compatible to current driver.'); } return $this->font; } } src/EncodedImage.php000064400000001676150731646300010367 0ustar00mimetype; } public function save(string $filepath): void { $saved = @file_put_contents($filepath, (string) $this); if ($saved === false) { throw new NotWritableException( "Can't write image data to path ({$filepath})." ); } } public function toDataUri(): string { return sprintf('data:%s;base64,%s', $this->mimetype, base64_encode($this->data)); } public function toString(): string { return $this->data; } public function __toString(): string { return $this->toString(); } } src/Exceptions/DecoderException.php000064400000000155150731646300013417 0ustar00setWidth($diameter); $this->setHeight($diameter); $this->pivot = $pivot ? $pivot : new Point(); } public function diameter(int $diameter): self { return $this->setDiameter($diameter); } public function setDiameter(int $diameter): self { $this->setWidth($diameter); $this->setHeight($diameter); return $this; } public function getDiameter(): int { return $this->diameter; } public function radius(int $radius): self { return $this->setRadius($radius); } public function setRadius(int $radius): self { return $this->diameter(intval($radius * 2)); } public function getRadius(): int { return intval($this->diameter / 2); } } src/Geometry/Ellipse.php000064400000002171150731646300011242 0ustar00pivot = $pivot ? $pivot : new Point(); } public function size(int $width, int $height): self { return $this->setSize($width, $height); } public function setSize(int $width, int $height): self { return $this->setWidth($width)->setHeight($height); } public function setWidth(int $width): self { $this->width = $width; return $this; } public function setHeight(int $height): self { $this->height = $height; return $this; } public function getWidth(): int { return $this->width; } public function getHeight(): int { return $this->height; } } src/Geometry/Line.php000064400000003017150731646300010534 0ustar00width; } public function setWidth(int $width): self { $this->width = $width; return $this; } public function width(int $width): self { return $this->setWidth($width); } public function color($color): self { $this->setBackgroundColor($color); return $this; } public function getStart(): Point { return $this->start; } public function getEnd(): Point { return $this->end; } public function setStart(Point $start): self { $this->start = $start; return $this; } public function from(int $x, int $y): self { $this->getStart()->setX($x); $this->getStart()->setY($y); return $this; } public function to(int $x, int $y): self { $this->getEnd()->setX($x); $this->getEnd()->setY($y); return $this; } public function setEnd(Point $end): self { $this->end = $end; return $this; } } src/Geometry/Pixel.php000064400000001015150731646300010722 0ustar00background = $background; return $this; } public function background(): ColorInterface { return $this->background; } } src/Geometry/Point.php000064400000004144150731646300010740 0ustar00x = $x; return $this; } /** * Get X coordinate * * @return integer */ public function getX(): int { return $this->x; } /** * Sets Y coordinate * * @param integer $y */ public function setY(int $y): self { $this->y = $y; return $this; } /** * Get Y coordinate * * @return integer */ public function getY(): int { return $this->y; } /** * Move X coordinate * * @param integer $value */ public function moveX(int $value): self { $this->x += $value; return $this; } /** * Move Y coordinate * * @param integer $value */ public function moveY(int $value): self { $this->y += $value; return $this; } public function move(int $x, int $y): self { return $this->moveX($x)->moveY($y); } /** * Sets both X and Y coordinate * * @param integer $x * @param integer $y * @return Point */ public function setPosition(int $x, int $y): self { $this->setX($x); $this->setY($y); return $this; } /** * Rotate point ccw around pivot * * @param float $angle * @param Point $pivot * @return Point */ public function rotate(float $angle, Point $pivot): self { $sin = round(sin(deg2rad($angle)), 6); $cos = round(cos(deg2rad($angle)), 6); return $this->setPosition( intval($cos * ($this->x - $pivot->x) - $sin * ($this->y - $pivot->y) + $pivot->x), intval($sin * ($this->x - $pivot->x) + $cos * ($this->y - $pivot->y) + $pivot->y) ); } } src/Geometry/Polygon.php000064400000022154150731646300011277 0ustar00pivot = $pivot ? $pivot : new Point(); } public function getIterator(): Traversable { return new ArrayIterator($this->points); } /** * Return current pivot point * * @return Point */ public function getPivot(): Point { return $this->pivot; } /** * Change pivot point to given point * * @param Point $pivot * @return Polygon */ public function setPivot(Point $pivot): self { $this->pivot = $pivot; return $this; } /** * Return first point of polygon * * @return ?Point */ public function first(): ?Point { if ($point = reset($this->points)) { return $point; } return null; } /** * Return last point of polygon * * @return ?Point */ public function last(): ?Point { if ($point = end($this->points)) { return $point; } return null; } /** * Return polygon's point count * * @return int */ public function count(): int { return count($this->points); } /** * Determine if point exists at given offset * * @param mixed $offset * @return bool */ public function offsetExists($offset): bool { return array_key_exists($offset, $this->points); } /** * Return point at given offset * * @param mixed $offset * @return Point */ public function offsetGet($offset) { return $this->points[$offset]; } /** * Set point at given offset * * @param mixed $offset * @param Point $value * @return void */ public function offsetSet($offset, $value): void { $this->points[$offset] = $value; } /** * Unset offset at given offset * * @param mixed $offset * @return void */ public function offsetUnset($offset): void { unset($this->points[$offset]); } /** * Add given point to polygon * * @param Point $point * @return Polygon */ public function addPoint(Point $point): self { $this->points[] = $point; return $this; } public function point(int $x, int $y): self { $this->addPoint(new Point($x, $y)); return $this; } /** * Calculate total horizontal span of polygon * * @return int */ public function getWidth(): int { return abs($this->getMostLeftPoint()->getX() - $this->getMostRightPoint()->getX()); } /** * Calculate total vertical span of polygon * * @return int */ public function getHeight(): int { return abs($this->getMostBottomPoint()->getY() - $this->getMostTopPoint()->getY()); } /** * Return most left point of all points in polygon * * @return Point */ public function getMostLeftPoint(): Point { $points = []; foreach ($this->points as $point) { $points[] = $point; } usort($points, function ($a, $b) { if ($a->getX() === $b->getX()) { return 0; } return ($a->getX() < $b->getX()) ? -1 : 1; }); return $points[0]; } /** * Return most right point in polygon * * @return Point */ public function getMostRightPoint(): Point { $points = []; foreach ($this->points as $point) { $points[] = $point; } usort($points, function ($a, $b) { if ($a->getX() === $b->getX()) { return 0; } return ($a->getX() > $b->getX()) ? -1 : 1; }); return $points[0]; } /** * Return most top point in polygon * * @return Point */ public function getMostTopPoint(): Point { $points = []; foreach ($this->points as $point) { $points[] = $point; } usort($points, function ($a, $b) { if ($a->getY() === $b->getY()) { return 0; } return ($a->getY() > $b->getY()) ? -1 : 1; }); return $points[0]; } /** * Return most bottom point in polygon * * @return Point */ public function getMostBottomPoint(): Point { $points = []; foreach ($this->points as $point) { $points[] = $point; } usort($points, function ($a, $b) { if ($a->getY() === $b->getY()) { return 0; } return ($a->getY() < $b->getY()) ? -1 : 1; }); return $points[0]; } /** * Create and return point in absolute center of the polygon * * @return Point */ public function getCenterPoint(): Point { return new Point( $this->getMostRightPoint()->getX() - (intval(round($this->getWidth() / 2))), $this->getMostTopPoint()->getY() - (intval(round($this->getHeight() / 2))) ); } /** * Align all points of polygon horizontally to given position around pivot point * * @param string $position * @return Polygon */ public function align(string $position): self { switch (strtolower($position)) { case 'center': case 'middle': $diff = ($this->getCenterPoint()->getX() - $this->getPivot()->getX()); break; case 'right': $diff = ($this->getMostRightPoint()->getX() - $this->getPivot()->getX()); break; default: case 'left': $diff = ($this->getMostLeftPoint()->getX() - $this->getPivot()->getX()); break; } foreach ($this->points as $point) { $point->setX($point->getX() - $diff); } return $this; } /** * Align all points of polygon vertically to given position around pivot point * * @param string $position * @return Polygon */ public function valign(string $position): self { switch (strtolower($position)) { case 'center': case 'middle': $diff = ($this->getCenterPoint()->getY() - $this->getPivot()->getY()); break; case 'top': $diff = ($this->getMostTopPoint()->getY() - $this->getPivot()->getY()) - $this->getHeight(); break; default: case 'bottom': $diff = ($this->getMostBottomPoint()->getY() - $this->getPivot()->getY()) + $this->getHeight(); break; } foreach ($this->points as $point) { $point->setY($point->getY() - $diff); } return $this; } /** * Rotate points of polygon around pivot point with given angle * * @param float $angle * @return Polygon */ public function rotate(float $angle): self { $sin = sin(deg2rad($angle)); $cos = cos(deg2rad($angle)); foreach ($this->points as $point) { // translate point to pivot $point->setX($point->getX() - $this->getPivot()->getX()); $point->setY($point->getY() - $this->getPivot()->getY()); // rotate point $x = $point->getX() * $cos - $point->getY() * $sin; $y = $point->getX() * $sin + $point->getY() * $cos; // translate point back $point->setX($x + $this->getPivot()->getX()); $point->setY($y + $this->getPivot()->getY()); } return $this; } /** * Move all points by given amount on the x-axis * * @param int $amount * @return Polygon */ public function movePointsX(int $amount): self { foreach ($this->points as $point) { $point->moveX($amount); } return $this; } /** * Move all points by given amount on the y-axis * * @param int $amount * @return Polygon */ public function movePointsY(int $amount): self { foreach ($this->points as $point) { $point->moveY($amount); } return $this; } /** * Return array of all x/y values of all points of polygon * * @return array */ public function toArray(): array { $coordinates = []; foreach ($this->points as $point) { $coordinates[] = $point->getX(); $coordinates[] = $point->getY(); } return $coordinates; } } src/Geometry/Rectangle.php000064400000016560150731646300011560 0ustar00pivot = $pivot ? $pivot : new Point(); $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY())); $this->addPoint(new Point($this->pivot->getX() + $width, $this->pivot->getY())); $this->addPoint(new Point($this->pivot->getX() + $width, $this->pivot->getY() - $height)); $this->addPoint(new Point($this->pivot->getX(), $this->pivot->getY() - $height)); } /** * Set the rectangle dimensions to given width & height * * @param int $width * @param int $height * @return Rectangle */ public function setSize(int $width, int $height): self { return $this->setWidth($width)->setHeight($height); } /** * Alias of self::setSize() */ public function size(int $width, int $height): self { return $this->setSize($width, $height); } public function setWidth(int $width): self { $this[1]->setX($this[0]->getX() + $width); $this[2]->setX($this[3]->getX() + $width); return $this; } public function setHeight(int $height): self { $this[2]->setY($this[1]->getY() + $height); $this[3]->setY($this[0]->getY() + $height); return $this; } public function getPivot(): Point { return $this->pivot; } public function setPivot(PointInterface $pivot): self { $this->pivot = $pivot; return $this; } /** * Move current pivot of current rectangle to given position * and moves point automatically by offset. * * @param string $position * @param int $offset_x * @param int $offset_y * @return Rectangle */ public function movePivot(string $position, int $offset_x = 0, int $offset_y = 0): self { switch (strtolower($position)) { case 'top': case 'top-center': case 'top-middle': case 'center-top': case 'middle-top': $x = intval($this->getWidth() / 2); $y = 0 + $offset_y; break; case 'top-right': case 'right-top': $x = $this->getWidth() - $offset_x; $y = 0 + $offset_y; break; case 'left': case 'left-center': case 'left-middle': case 'center-left': case 'middle-left': $x = 0 + $offset_x; $y = intval($this->getHeight() / 2); break; case 'right': case 'right-center': case 'right-middle': case 'center-right': case 'middle-right': $x = $this->getWidth() - $offset_x; $y = intval($this->getHeight() / 2); break; case 'bottom-left': case 'left-bottom': $x = 0 + $offset_x; $y = $this->getHeight() - $offset_y; break; case 'bottom': case 'bottom-center': case 'bottom-middle': case 'center-bottom': case 'middle-bottom': $x = intval($this->getWidth() / 2); $y = $this->getHeight() - $offset_y; break; case 'bottom-right': case 'right-bottom': $x = $this->getWidth() - $offset_x; $y = $this->getHeight() - $offset_y; break; case 'center': case 'middle': case 'center-center': case 'middle-middle': $x = intval($this->getWidth() / 2) + $offset_x; $y = intval($this->getHeight() / 2) + $offset_y; break; default: case 'top-left': case 'left-top': $x = 0 + $offset_x; $y = 0 + $offset_y; break; } $this->pivot->setPosition($x, $y); return $this; } public function alignPivotTo(SizeInterface $size, string $position): self { $reference = new self($size->getWidth(), $size->getHeight()); $reference->movePivot($position); $this->movePivot($position)->setPivot( $reference->getRelativePositionTo($this) ); return $this; } /** * Calculate the relative position to another Size * based on the pivot point settings of both sizes. * * @param Size $size * @return Point */ public function getRelativePositionTo(SizeInterface $rectangle): PointInterface { return new Point( $this->getPivot()->getX() - $rectangle->getPivot()->getX(), $this->getPivot()->getY() - $rectangle->getPivot()->getY() ); } public function getAspectRatio(): float { return $this->getWidth() / $this->getHeight(); } public function fitsInto(SizeInterface $size): bool { if ($this->getWidth() > $size->getWidth()) { return false; } if ($this->getHeight() > $size->getHeight()) { return false; } return true; } /** * Determine if size is landscape format * * @return boolean */ public function isLandscape(): bool { return $this->getWidth() > $this->getHeight(); } /** * Determine if size is portrait format * * @return boolean */ public function isPortrait(): bool { return $this->getWidth() < $this->getHeight(); } public function topLeftPoint(): PointInterface { return $this->points[0]; } public function bottomRightPoint(): PointInterface { return $this->points[2]; } protected function getResizer(?int $width = null, ?int $height = null): RectangleResizer { return new RectangleResizer($width, $height); } public function resize(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->resize($this); } public function resizeDown(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->resizeDown($this); } public function scale(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->scale($this); } public function scaleDown(?int $width = null, ?int $height = null): SizeInterface { return $this->getResizer($width, $height)->scaleDown($this); } public function cover(int $width, int $height): SizeInterface { return $this->getResizer($width, $height)->cover($this); } public function contain(int $width, int $height): SizeInterface { return $this->getResizer($width, $height)->contain($this); } } src/Geometry/Tools/RectangleResizer.php000064400000015407150731646300014223 0ustar00width); } protected function getTargetWidth(): ?int { return $this->hasTargetWidth() ? $this->width : null; } protected function hasTargetHeight(): bool { return is_integer($this->height); } protected function getTargetHeight(): ?int { return $this->hasTargetHeight() ? $this->height : null; } protected function getTargetSize(): SizeInterface { if (!$this->hasTargetWidth() || !$this->hasTargetHeight()) { throw new GeometryException('Target size needs width and height.'); } return new Rectangle($this->width, $this->height); } public function toWidth(int $width): self { $this->width = $width; return $this; } public function toHeight(int $height): self { $this->height = $height; return $this; } public function toSize(SizeInterface $size): self { $this->width = $size->getWidth(); $this->height = $size->getHeight(); return $this; } protected function getProportionalWidth(SizeInterface $size): int { if (! $this->hasTargetHeight()) { return $size->getWidth(); } return (int) round($this->height * $size->getAspectRatio()); } protected function getProportionalHeight(SizeInterface $size): int { if (! $this->hasTargetWidth()) { return $size->getHeight(); } return (int) round($this->width / $size->getAspectRatio()); } public function resize(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($width = $this->getTargetWidth()) { $resized->setWidth($width); } if ($height = $this->getTargetHeight()) { $resized->setHeight($height); } return $resized; } public function resizeDown(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($width = $this->getTargetWidth()) { $resized->setWidth( min($width, $size->getWidth()) ); } if ($height = $this->getTargetHeight()) { $resized->setHeight( min($height, $size->getHeight()) ); } return $resized; } public function scale(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth() )); $resized->setHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight() )); } elseif ($this->hasTargetWidth()) { $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); } elseif ($this->hasTargetHeight()) { $resized->setWidth($this->getProportionalWidth($size)); $resized->setHeight($this->getTargetHeight()); } return $resized; } public function scaleDown(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->getWidth(), $size->getHeight()); if ($this->hasTargetWidth() && $this->hasTargetHeight()) { $resized->setWidth(min( $this->getProportionalWidth($size), $this->getTargetWidth(), $size->getWidth() )); $resized->setHeight(min( $this->getProportionalHeight($size), $this->getTargetHeight(), $size->getHeight() )); } elseif ($this->hasTargetWidth()) { $resized->setWidth(min( $this->getTargetWidth(), $size->getWidth() )); $resized->setHeight(min( $this->getProportionalHeight($size), $size->getHeight() )); } elseif ($this->hasTargetHeight()) { $resized->setWidth(min( $this->getProportionalWidth($size), $size->getWidth() )); $resized->setHeight(min( $this->getTargetHeight(), $size->getHeight() )); } return $resized; } /** * Scale given size to cover target size * * @param SizeInterface $size Size to be resized * @return SizeInterface */ public function cover(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->getWidth(), $size->getHeight()); // auto height $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); if ($resized->fitsInto($this->getTargetSize())) { // auto width $resized->setWidth($this->getProportionalWidth($size)); $resized->setHeight($this->getTargetHeight()); } return $resized; } /** * Scale given size to contain target size * * @param SizeInterface $size Size to be resized * @return SizeInterface */ public function contain(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->getWidth(), $size->getHeight()); // auto height $resized->setWidth($this->getTargetWidth()); $resized->setHeight($this->getProportionalHeight($size)); if (!$resized->fitsInto($this->getTargetSize())) { // auto width $resized->setWidth($this->getProportionalWidth($size)); $resized->setHeight($this->getTargetHeight()); } return $resized; } /** * Crop target size out of given size at given position (i.e. move the pivot point) * * @param SizeInterface $size * @param string $position * @return SizeInterface */ public function crop(SizeInterface $size, string $position = 'top-left'): SizeInterface { return $this->resize($size)->alignPivotTo( $size->movePivot($position), $position ); } } src/Geometry/Traits/HasBackgroundColor.php000064400000001062150731646300014623 0ustar00setBackgroundColor($color); } public function setBackgroundColor($color): self { $this->backgroundColor = $color; return $this; } public function getBackgroundColor() { return $this->backgroundColor; } public function hasBackgroundColor(): bool { return !is_null($this->backgroundColor); } } src/Geometry/Traits/HasBorder.php000064400000001457150731646300012772 0ustar00setBorderSize($size)->setBorderColor($color); } public function setBorderSize(int $size): self { $this->borderSize = $size; return $this; } public function getBorderSize(): int { return $this->borderSize; } public function setBorderColor($color): self { $this->borderColor = $color; return $this; } public function getBorderColor() { return $this->borderColor; } public function hasBorder(): bool { return $this->borderSize > 0 && !is_null($this->borderColor); } } src/ImageManager.php000064400000002043150731646300010365 0ustar00resolveDriverClass('ImageFactory')->newImage($width, $height); } /** * Create new image instance from source * * @param mixed $source * @return ImageInterface */ public function make($source): ImageInterface { return $this->resolveDriverClass('InputHandler')->handle($source); } /** * Return id of current driver * * @return string */ protected function getCurrentDriver(): string { return strtolower($this->driver); } } src/Interfaces/CollectionInterface.php000064400000000541150731646300014050 0ustar00modifiers as $modifier) { $modifier->apply($image); } return $image; } public function push(ModifierInterface $modifier): self { $this->modifiers[] = $modifier; return $this; } } src/Traits/CanBuildNewImage.php000064400000000430150731646300012412 0ustar00resolveDriverClass('ImageFactory'); } } src/Traits/CanDecodeDataUri.php000064400000004033150731646300012376 0ustar00\w+\/[-+.\w]+)?" . "(?P(;[-\w]+=[-\w]+)*)(?P;base64)?,(?P.*)/"; $result = preg_match($pattern, $value, $matches); return new class ($matches, $result) { private $matches; private $result; public function __construct($matches, $result) { $this->matches = $matches; $this->result = $result; } public function isValid(): bool { return (bool) $this->result; } public function mediaType(): ?string { if (isset($this->matches['mediatype']) && !empty($this->matches['mediatype'])) { return $this->matches['mediatype']; } return null; } public function hasMediaType(): bool { return !empty($this->mediaType()); } public function parameters(): array { if (isset($this->matches['parameters']) && !empty($this->matches['parameters'])) { return explode(';', trim($this->matches['parameters'], ';')); } return []; } public function isBase64Encoded(): bool { if (isset($this->matches['base64']) && $this->matches['base64'] === ';base64') { return true; } return false; } public function data(): ?string { if (isset($this->matches['data']) && !empty($this->matches['data'])) { return $this->matches['data']; } return null; } }; } } src/Traits/CanHandleInput.php000064400000000550150731646300012154 0ustar00resolveDriverClass('InputHandler')->handle($input); } } src/Traits/CanReadHtmlColorNames.php000064400000012207150731646300013426 0ustar00 '#FFA07A', 'salmon' => '#FA8072', 'darksalmon' => '#E9967A', 'lightcoral' => '#F08080', 'indianred' => '#CD5C5C', 'crimson' => '#DC143C', 'firebrick' => '#B22222', 'red' => '#FF0000', 'darkred' => '#8B0000', 'coral' => '#FF7F50', 'tomato' => '#FF6347', 'orangered' => '#FF4500', 'gold' => '#FFD700', 'orange' => '#FFA500', 'darkorange' => '#FF8C00', 'lightyellow' => '#FFFFE0', 'lemonchiffon' => '#FFFACD', 'lightgoldenrodyellow' => '#FAFAD2', 'papayawhip' => '#FFEFD5', 'moccasin' => '#FFE4B5', 'peachpuff' => '#FFDAB9', 'palegoldenrod' => '#EEE8AA', 'khaki' => '#F0E68C', 'darkkhaki' => '#BDB76B', 'yellow' => '#FFFF00', 'lawngreen' => '#7CFC00', 'chartreuse' => '#7FFF00', 'limegreen' => '#32CD32', 'lime' => '#00FF00', 'forestgreen' => '#228B22', 'green' => '#008000', 'darkgreen' => '#006400', 'greenyellow' => '#ADFF2F', 'yellowgreen' => '#9ACD32', 'springgreen' => '#00FF7F', 'mediumspringgreen' => '#00FA9A', 'lightgreen' => '#90EE90', 'palegreen' => '#98FB98', 'darkseagreen' => '#8FBC8F', 'mediumseagre' => 'en #3CB371', 'seagreen' => '#2E8B57', 'olive' => '#808000', 'darkolivegreen' => '#556B2F', 'olivedrab' => '#6B8E23', 'lightcyan' => '#E0FFFF', 'cyan' => '#00FFFF', 'aqua' => '#00FFFF', 'aquamarine' => '#7FFFD4', 'mediumaquamarine' => '#66CDAA', 'paleturquoise' => '#AFEEEE', 'turquoise' => '#40E0D0', 'mediumturquoise' => '#48D1CC', 'darkturquoise' => '#00CED1', 'lightseagreen' => '#20B2AA', 'cadetblue' => '#5F9EA0', 'darkcyan' => '#008B8B', 'teal' => '#008080', 'powderblue' => '#B0E0E6', 'lightblue' => '#ADD8E6', 'lightskyblue' => '#87CEFA', 'skyblue' => '#87CEEB', 'deepskyblue' => '#00BFFF', 'lightsteelblue' => '#B0C4DE', 'dodgerblue' => '#1E90FF', 'cornflowerblue' => '#6495ED', 'steelblue' => '#4682B4', 'royalblue' => '#4169E1', 'blue' => '#0000FF', 'mediumblue' => '#0000CD', 'darkblue' => '#00008B', 'navy' => '#000080', 'midnightblue' => '#191970', 'mediumslateblue' => '#7B68EE', 'slateblue' => '#6A5ACD', 'darkslateblue' => '#483D8B', 'lavender' => '#E6E6FA', 'thistle' => '#D8BFD8', 'plum' => '#DDA0DD', 'violet' => '#EE82EE', 'orchid' => '#DA70D6', 'fuchsia' => '#FF00FF', 'magenta' => '#FF00FF', 'mediumorchid' => '#BA55D3', 'mediumpurple' => '#9370DB', 'blueviolet' => '#8A2BE2', 'darkviolet' => '#9400D3', 'darkorchid' => '#9932CC', 'darkmagenta' => '#8B008B', 'purple' => '#800080', 'indigo' => '#4B0082', 'pink' => '#FFC0CB', 'lightpink' => '#FFB6C1', 'hotpink' => '#FF69B4', 'deeppink' => '#FF1493', 'palevioletred' => '#DB7093', 'mediumvioletred' => '#C71585', 'white' => '#FFFFFF', 'snow' => '#FFFAFA', 'honeydew' => '#F0FFF0', 'mintcream' => '#F5FFFA', 'azure' => '#F0FFFF', 'aliceblue' => '#F0F8FF', 'ghostwhite' => '#F8F8FF', 'whitesmoke' => '#F5F5F5', 'seashell' => '#FFF5EE', 'beige' => '#F5F5DC', 'oldlace' => '#FDF5E6', 'floralwhite' => '#FFFAF0', 'ivory' => '#FFFFF0', 'antiquewhite' => '#FAEBD7', 'linen' => '#FAF0E6', 'lavenderblush' => '#FFF0F5', 'mistyrose' => '#FFE4E1', 'gainsboro' => '#DCDCDC', 'lightgray' => '#D3D3D3', 'silver' => '#C0C0C0', 'darkgray' => '#A9A9A9', 'gray' => '#808080', 'dimgray' => '#696969', 'lightslategray' => '#778899', 'slategray' => '#708090', 'darkslategray' => '#2F4F4F', 'black' => '#000000', 'cornsilk' => '#FFF8DC', 'blanchedalmond' => '#FFEBCD', 'bisque' => '#FFE4C4', 'navajowhite' => '#FFDEAD', 'wheat' => '#F5DEB3', 'burlywood' => '#DEB887', 'tan' => '#D2B48C', 'rosybrown' => '#BC8F8F', 'sandybrown' => '#F4A460', 'goldenrod' => '#DAA520', 'peru' => '#CD853F', 'chocolate' => '#D2691E', 'saddlebrown' => '#8B4513', 'sienna' => '#A0522D', 'brown' => '#A52A2A', 'maroon' => '#800000', ]; /** * Transform given html color name to hex color * or return null, if color name doesn't exist. * * @param string $name * @return null|string */ public function hexColorFromColorName(string $name): ?string { $name = strtolower($name); if (!array_key_exists($name, $this->color_names)) { return null; } return $this->color_names[$name]; } } src/Traits/CanResolveDriverClass.php000064400000002656150731646300013533 0ustar00getCurrentDriver(); $classname = sprintf( "Intervention\\Image\\Drivers\\%s\\%s", ucfirst($driver_id), $classname ); try { $reflection = new ReflectionClass($classname); } catch (ReflectionException $e) { throw new MissingDriverComponentException( 'Class (' . $classname . ') could not be resolved with driver ' . ucfirst($driver_id) . '.' ); } return $reflection->newInstanceArgs($arguments); } protected function getCurrentDriver(): string { $pattern = '/Intervention\\\Image\\\Drivers\\\(?P[A-Za-z]+)/'; preg_match($pattern, get_class($this), $matches); if (! array_key_exists('driver', $matches)) { throw new RuntimeException('Current driver could not be resolved.'); } return strtolower($matches['driver']); } } src/Traits/CanRunCallback.php000064400000000413150731646300012120 0ustar00 4) { return false; } // validate rgb values foreach ($input as $value) { if ($value < 0 || $value > 255) { return false; } } // validate alpha value if (isset($input[3]) && ($input[3] > 1 || $input[3] < 0)) { return false; } return true; } } src/Typography/Line.php000064400000001302150731646300011102 0ustar00position = new Point(); } public function getPosition(): Point { return $this->position; } public function setPosition(Point $point): self { $this->position = $point; return $this; } public function widthInFont(FontInterface $font): int { return $font->getBoxSize($this->text)->getWidth(); } public function __toString(): string { return $this->text; } } src/Typography/TextBlock.php000064400000003372150731646300012123 0ustar00push(new Line($line)); } } public function getBoundingBox(FontInterface $font, Point $pivot = null): Polygon { $pivot = $pivot ? $pivot : new Point(); // bounding box $box = (new Rectangle( $this->longestLine()->widthInFont($font), $font->leadingInPixels() * ($this->count() - 1) + $font->capHeight() )); // set pivot $box->setPivot($pivot); // align $box->align($font->getAlign()); $box->valign($font->getValign()); $box->rotate($font->getAngle()); return $box; } /** * Return array of lines in text block * * @return array */ public function lines(): array { return $this->items; } public function getLine($key): ?Line { if (!array_key_exists($key, $this->lines())) { return null; } return $this->lines()[$key]; } /** * Return line with most characters of text block * * @return Line */ public function longestLine(): Line { $lines = $this->lines(); usort($lines, function ($a, $b) { if (mb_strlen($a) === mb_strlen($b)) { return 0; } return (mb_strlen($a) > mb_strlen($b)) ? -1 : 1; }); return $lines[0]; } }