Dockerfile 0000644 00000001653 15073164571 0006554 0 ustar 00 FROM php:8-cli
RUN apt update \
&& apt install -y \
libpng-dev \
libicu-dev \
libavif-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 --with-avif \
&& 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
LICENSE 0000644 00000002067 15073164571 0005567 0 ustar 00 The MIT License (MIT)
Copyright (c) 2023 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.json 0000644 00000001537 15073164571 0007305 0 ustar 00 {
"name": "intervention/image",
"description": "PHP image manipulation",
"homepage": "https://image.intervention.io/",
"keywords": ["image", "gd", "imagick", "watermark", "thumbnail", "resize"],
"license": "MIT",
"authors": [
{
"name": "Oliver Vogel",
"email": "oliver@intervention.io",
"homepage": "https://intervention.io/"
}
],
"require": {
"php": "^8.1",
"intervention/gif": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^9",
"mockery/mockery": "^1.6",
"phpstan/phpstan": "^1"
},
"autoload": {
"psr-4": {
"Intervention\\Image\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Intervention\\Image\\Tests\\": "tests"
}
},
"minimum-stability": "alpha"
}
docker-compose.yml 0000644 00000000535 15073164571 0010215 0 ustar 00 version: '3'
services:
tests:
build: ./
working_dir: /project
command: bash -c "composer install && ./vendor/bin/phpunit -vvv"
volumes:
- ./:/project
analysis:
build: ./
working_dir: /project
command: bash -c "composer install && ./vendor/bin/phpstan analyze --level=4 ./src"
volumes:
- ./:/project
phpunit.xml.dist 0000644 00000001264 15073164571 0007733 0 ustar 00
./tests/
src
readme.md 0000644 00000004224 15073164571 0006336 0 ustar 00 # Intervention Image
## PHP Image Manipulation
[](https://packagist.org/packages/intervention/image)
[](https://github.com/Intervention/image/actions)
[](https://packagist.org/packages/intervention/image/stats)
Intervention Image is a **image handling and manipulation library written in
PHP** providing an easy 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 image manipulation tasks
- Interchangable driver architecture
- Support for animated images
- Framework-agnostic
- PSR-12 compliant
## 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/).
## Code Examples
```php
// create image manager with desired driver
$manager = new ImageManager(
new Intervention\Image\Drivers\Gd\Driver()
);
// open an image file
$image = $manager->read('images/example.gif');
// resize image instance
$image->resize(height: 300);
// 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.1
## Supported Image Libraries
- GD Library
- Imagick PHP extension
## Development & Testing
With this package comes a Docker image to build a test suite and analysis
container. To build this container you have to have Docker installed on your
system. You can run all tests with this command.
```bash
docker-compose run --rm --build tests
```
Run the static analyzer on the code base.
```bash
docker-compose run --rm --build analysis
```
## License
Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT).
Copyright 2023 Oliver Vogel
src/Analyzers/AbstractAnalyzer.php 0000644 00000000633 15073164571 0013300 0 ustar 00 analyze($this);
}
}
src/Analyzers/ColorspaceAnalyzer.php 0000644 00000000146 15073164571 0013626 0 ustar 00 items);
}
/**
* Returns Iterator
*
* @return Traversable
*/
public function getIterator(): Traversable
{
return new ArrayIterator($this->items);
}
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(): mixed
{
if ($item = reset($this->items)) {
return $item;
}
return null;
}
/**
* Returns last item in collection
*
* @return mixed
*/
public function last(): mixed
{
if ($item = end($this->items)) {
return $item;
}
return null;
}
/**
* Return item at given position starting at 0
*
* @param integer $key
* @return mixed
*/
public function getAtPosition(int $key = 0, $default = null): mixed
{
if ($this->count() == 0) {
return $default;
}
$positions = array_values($this->items);
if (! array_key_exists($key, $positions)) {
return $default;
}
return $positions[$key];
}
public function get(int|string $query, $default = null): mixed
{
if ($this->count() == 0) {
return $default;
}
if (is_int($query) && array_key_exists($query, $this->items)) {
return $this->items[$query];
}
if (is_string($query) && strpos($query, '.') === false) {
return array_key_exists($query, $this->items) ? $this->items[$query] : $default;
}
$query = explode('.', $query);
$result = $default;
$items = $this->items;
foreach ($query as $key) {
if (!is_array($items) || !array_key_exists($key, $items)) {
$result = $default;
break;
}
$result = $items[$key];
$items = $result;
}
return $result;
}
public function map(callable $callback): self
{
$items = array_map(function ($item) use ($callback) {
return $callback($item);
}, $this->items);
return new self($items);
}
/**
* Run callback on each item of the collection an remove it if it does not return true
*
* @param callable $callback
* @return Collection
*/
public function filter(callable $callback): self
{
$items = array_filter($this->items, function ($item) use ($callback) {
return $callback($item);
});
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;
}
public function empty(): CollectionInterface
{
$this->items = [];
return $this;
}
}
src/Colors/Cmyk/Channels/Cyan.php 0000644 00000003033 15073164571 0012645 0 ustar 00 value = $this->validate(
match (true) {
is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())),
is_numeric($value) && is_null($normalized) => $value,
default => throw new ColorException('Color channels must either have a value or a normalized value')
}
);
}
public function value(): int
{
return $this->value;
}
public function normalize($precision = 32): float
{
return round($this->value() / $this->max(), $precision);
}
public function min(): int
{
return 0;
}
public function max(): int
{
return 100;
}
public function validate(mixed $value): mixed
{
if ($value < $this->min() || $value > $this->max()) {
throw new ColorException('CMYK color values must be in range 0-100.');
}
return $value;
}
public function toString(): string
{
return (string) $this->value();
}
public function __toString(): string
{
return $this->toString();
}
}
src/Colors/Cmyk/Channels/Key.php 0000644 00000000135 15073164571 0012503 0 ustar 00 channels = [
new Cyan($c),
new Magenta($m),
new Yellow($y),
new Key($k),
];
}
public static function create(mixed $input): ColorInterface
{
return (new class ([
Decoders\StringColorDecoder::class,
]) extends AbstractInputHandler
{
})->handle($input);
}
public function colorspace(): ColorspaceInterface
{
return new Colorspace();
}
public function toHex(string $prefix = ''): string
{
return $this->convertTo(RgbColorspace::class)->toHex($prefix);
}
public function channels(): array
{
return $this->channels;
}
public function cyan(): ColorChannelInterface
{
return $this->channel(Cyan::class);
}
public function magenta(): ColorChannelInterface
{
return $this->channel(Magenta::class);
}
public function yellow(): ColorChannelInterface
{
return $this->channel(Yellow::class);
}
public function key(): ColorChannelInterface
{
return $this->channel(Key::class);
}
public function toArray(): array
{
return [
$this->cyan()->value(),
$this->magenta()->value(),
$this->yellow()->value(),
$this->key()->value(),
];
}
public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface
{
$colorspace = match (true) {
is_object($colorspace) => $colorspace,
default => new $colorspace(),
};
return $colorspace->convertColor($this);
}
public function toString(): string
{
return sprintf(
'cmyk(%d%%, %d%%, %d%%, %d%%)',
$this->cyan()->value(),
$this->magenta()->value(),
$this->yellow()->value(),
$this->key()->value()
);
}
public function isGreyscale(): bool
{
return 0 === array_sum([
$this->cyan()->value(),
$this->magenta()->value(),
$this->yellow()->value(),
]);
}
public function __toString(): string
{
return $this->toString();
}
}
src/Colors/Cmyk/Colorspace.php 0000644 00000003215 15073164571 0012314 0 ustar 00 value();
}, self::$channels, $normalized);
return new Color(...$values);
}
/**
* {@inheritdoc}
*
* @see ColorspaceInterface::convertColor()
*/
public function convertColor(ColorInterface $color): ColorInterface
{
return match (get_class($color)) {
RgbColor::class => $this->convertRgbColor($color),
default => $color,
};
}
protected function convertRgbColor(RgbColor $color): CmykColor
{
$c = (255 - $color->red()->value()) / 255.0 * 100;
$m = (255 - $color->green()->value()) / 255.0 * 100;
$y = (255 - $color->blue()->value()) / 255.0 * 100;
$k = intval(round(min([$c, $m, $y])));
$c = intval(round($c - $k));
$m = intval(round($m - $k));
$y = intval(round($y - $k));
return new CmykColor($c, $m, $y, $k);
}
}
src/Colors/Cmyk/Decoders/StringColorDecoder.php 0000644 00000002334 15073164571 0015506 0 ustar 00 [0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)\)$/i';
if (preg_match($pattern, $input, $matches) != 1) {
throw new DecoderException('Unable to decode input');
}
$values = array_map(function ($value) {
return intval(round(floatval(trim(str_replace('%', '', $value)))));
}, [$matches['c'], $matches['m'], $matches['y'], $matches['k']]);
return new Color(...$values);
}
}
src/Colors/Profile.php 0000644 00000000272 15073164571 0010717 0 ustar 00 normalize(), 6));
}
public function __toString(): string
{
return $this->toString();
}
}
src/Colors/Rgb/Channels/Blue.php 0000644 00000000134 15073164571 0012450 0 ustar 00 value = $this->validate(
match (true) {
is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())),
is_numeric($value) && is_null($normalized) => $value,
default => throw new ColorException('Color channels must either have a value or a normalized value')
}
);
}
/**
* Alias of value()
*
* @return int
*/
public function toInt(): int
{
return $this->value;
}
/**
* {@inheritdoc}
*
* @see ColorChannelInterface::value()
*/
public function value(): int
{
return $this->value;
}
/**
* {@inheritdoc}
*
* @see ColorChannelInterface::normalize()
*/
public function normalize($precision = 32): float
{
return round($this->value() / $this->max(), $precision);
}
/**
* {@inheritdoc}
*
* @see ColorChannelInterface::min()
*/
public function min(): int
{
return 0;
}
/**
* {@inheritdoc}
*
* @see ColorChannelInterface::max()
*/
public function max(): int
{
return 255;
}
/**
* {@inheritdoc}
*
* @see ColorChannelInterface::validate()
*/
public function validate(mixed $value): mixed
{
if ($value < $this->min() || $value > $this->max()) {
throw new ColorException('RGB color values must be in range 0-255.');
}
return $value;
}
/**
* {@inheritdoc}
*
* @see ColorChannelInterface::toString()
*/
public function toString(): string
{
return (string) $this->value();
}
/**
* {@inheritdoc}
*
* @see ColorChannelInterface::__toString()
*/
public function __toString(): string
{
return $this->toString();
}
}
src/Colors/Rgb/Color.php 0000644 00000011461 15073164571 0011111 0 ustar 00 channels = [
new Red($r),
new Green($g),
new Blue($b),
new Alpha($a),
];
}
public function colorspace(): ColorspaceInterface
{
return new Colorspace();
}
/**
* {@inheritdoc}
*
* @see ColorInterface::create()
*/
public static function create(mixed $input): ColorInterface
{
return (new class ([
Decoders\HexColorDecoder::class,
Decoders\StringColorDecoder::class,
Decoders\TransparentColorDecoder::class,
Decoders\HtmlColornameDecoder::class,
]) extends AbstractInputHandler
{
})->handle($input);
}
/**
* Return the RGB red color channel
*
* @return ColorChannelInterface
*/
public function red(): ColorChannelInterface
{
return $this->channel(Red::class);
}
/**
* Return the RGB green color channel
*
* @return ColorChannelInterface
*/
public function green(): ColorChannelInterface
{
return $this->channel(Green::class);
}
/**
* Return the RGB blue color channel
*
* @return ColorChannelInterface
*/
public function blue(): ColorChannelInterface
{
return $this->channel(Blue::class);
}
/**
* Return the colors alpha channel
*
* @return ColorChannelInterface
*/
public function alpha(): ColorChannelInterface
{
return $this->channel(Alpha::class);
}
/**
* {@inheritdoc}
*
* @see ColorInterface::toArray()
*/
public function toArray(): array
{
return array_map(function (ColorChannelInterface $channel) {
return $channel->value();
}, $this->channels());
}
/**
* {@inheritdoc}
*
* @see ColorInterface::toHex()
*/
public function toHex(string $prefix = ''): string
{
if ($this->isFullyOpaque()) {
return sprintf(
'%s%02x%02x%02x',
$prefix,
$this->red()->value(),
$this->green()->value(),
$this->blue()->value()
);
}
return sprintf(
'%s%02x%02x%02x%02x',
$prefix,
$this->red()->value(),
$this->green()->value(),
$this->blue()->value(),
$this->alpha()->value()
);
}
/**
* {@inheritdoc}
*
* @see ColorInterface::convertTo()
*/
public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface
{
$colorspace = match (true) {
is_object($colorspace) => $colorspace,
default => new $colorspace(),
};
return $colorspace->convertColor($this);
}
public function isFullyOpaque(): bool
{
return $this->alpha()->value() === 255;
}
/**
* {@inheritdoc}
*
* @see ColorInterface::toString()
*/
public function toString(): string
{
if ($this->isFullyOpaque()) {
return sprintf(
'rgb(%d, %d, %d)',
$this->red()->value(),
$this->green()->value(),
$this->blue()->value()
);
}
return sprintf(
'rgba(%d, %d, %d, %.1F)',
$this->red()->value(),
$this->green()->value(),
$this->blue()->value(),
$this->alpha()->normalize(),
);
}
/**
* {@inheritdoc}
*
* @see ColorInterface::isGreyscale()
*/
public function isGreyscale(): bool
{
$values = [$this->red()->value(), $this->green()->value(), $this->blue()->value()];
return count(array_unique($values, SORT_REGULAR)) === 1;
}
/**
* {@inheritdoc}
*
* @see ColorInterface::__toString()
*/
public function __toString(): string
{
return $this->toString();
}
}
src/Colors/Rgb/Colorspace.php 0000644 00000002700 15073164571 0012121 0 ustar 00 value();
}, self::$channels, $normalized);
return new Color(...$values);
}
public function convertColor(ColorInterface $color): ColorInterface
{
return match (get_class($color)) {
CmykColor::class => $this->convertCmykColor($color),
default => $color,
};
}
protected function convertCmykColor(CmykColor $color): Color
{
return new Color(
(int) (255 * (1 - $color->cyan()->normalize()) * (1 - $color->key()->normalize())),
(int) (255 * (1 - $color->magenta()->normalize()) * (1 - $color->key()->normalize())),
(int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())),
);
}
}
src/Colors/Rgb/Decoders/HexColorDecoder.php 0000644 00000003136 15073164571 0014574 0 ustar 00 [a-f\d]{3}(?:[a-f\d]?|(?:[a-f\d]{3}(?:[a-f\d]{2})?)?)\b)$/i';
if (preg_match($pattern, $input, $matches) != 1) {
throw new DecoderException('Unable to decode input');
}
$values = str_split($matches['hex']);
$values = match (strlen($matches['hex'])) {
3, 4 => str_split($matches['hex']),
6, 8 => str_split($matches['hex'], 2),
default => throw new DecoderException('Unable to decode input'),
};
$values = array_map(function ($value) {
return match (strlen($value)) {
1 => hexdec($value . $value),
2 => hexdec($value),
default => throw new DecoderException('Unable to decode input'),
};
}, $values);
return new Color(...$values);
}
}
src/Colors/Rgb/Decoders/HtmlColornameDecoder.php 0000644 00000014050 15073164571 0015612 0 ustar 00 '#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',
];
/**
* Decode html color names
*
* @param mixed $input
* @return ImageInterface|ColorInterface
*/
public function decode($input): ImageInterface|ColorInterface
{
if (! is_string($input)) {
throw new DecoderException('Unable to decode input');
}
if (!array_key_exists(strtolower($input), static::$names)) {
throw new DecoderException('Unable to decode input');
}
return parent::decode(static::$names[strtolower($input)]);
}
}
src/Colors/Rgb/Decoders/StringColorDecoder.php 0000644 00000003326 15073164571 0015317 0 ustar 00 [0-9\.]+%?), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)(?:, ?(?P(?:1)|(?:1\.0*)|(?:0)|(?:0?\.\d+%?)|(?:\d{1,3}%)))?\)$/i';
if (preg_match($pattern, $input, $matches) != 1) {
throw new DecoderException('Unable to decode input');
}
// rgb values
$values = array_map(function ($value) {
return match (strpos($value, '%')) {
false => intval(trim($value)),
default => intval(round(floatval(trim(str_replace('%', '', $value))) / 100 * 255)),
};
}, [$matches['r'], $matches['g'], $matches['b']]);
// alpha value
if (array_key_exists('a', $matches)) {
$values[] = match (true) {
strpos($matches['a'], '%') => round(intval(trim(str_replace('%', '', $matches['a']))) / 2.55),
default => intval(round(floatval(trim($matches['a'])) * 255)),
};
}
return new Color(...$values);
}
}
src/Colors/Rgb/Decoders/TransparentColorDecoder.php 0000644 00000001167 15073164571 0016353 0 ustar 00 channels;
}
public function channel(string $classname): ColorChannelInterface
{
$channels = array_filter($this->channels(), function (ColorChannelInterface $channel) use ($classname) {
return get_class($channel) == $classname;
});
if (count($channels) == 0) {
throw new ColorException('Channel ' . $classname . ' could not be found.');
}
return reset($channels);
}
public function normalize(): array
{
return array_map(function (ColorChannelInterface $channel) {
return $channel->normalize();
}, $this->channels());
}
}
src/Drivers/AbstractDecoder.php 0000644 00000010065 15073164571 0012526 0 ustar 00 decode($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;
}
/**
* Return media type (MIME) of given input
*
* @param string $input
* @return string
*/
protected function mediaType(string $input): string
{
$pointer = $this->buildFilePointer($input);
$type = mime_content_type($pointer);
fclose($pointer);
return $type;
}
protected function decodeExifData(string $image_data): CollectionInterface
{
if (!function_exists('exif_read_data')) {
return new Collection();
}
try {
$pointer = $this->buildFilePointer($image_data);
$data = @exif_read_data($pointer, null, true);
fclose($pointer);
} catch (Exception $e) {
$data = [];
}
return new Collection(is_array($data) ? $data : []);
}
protected function isValidBase64($input): bool
{
if (!is_string($input)) {
return false;
}
return base64_encode(base64_decode($input)) === str_replace(["\n", "\r"], '', $input);
}
/**
* Parse data uri
*
* @param mixed $value
* @return object
*/
protected function parseDataUri($value): object
{
$pattern = "/^data:(?P\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/Drivers/AbstractDriver.php 0000644 00000003244 15073164571 0012415 0 ustar 00 isExternal($input)) {
return $input;
}
$driver_namespace = (new ReflectionClass($this))->getNamespaceName();
$class_path = substr(get_class($input), strlen("Intervention\\Image\\"));
$specialized = $driver_namespace . "\\" . $class_path;
if (! class_exists($specialized)) {
throw new NotSupportedException(
"Class '" . $class_path . "' is not supported by " . $this->id() . " driver."
);
}
return new $specialized($input, $this);
}
/**
* Determine if given object is external custom modifier, analyzer or encoder
*
* @param object $input
* @return bool
*/
private function isExternal(object $input): bool
{
if ($input instanceof AbstractModifier) {
return false;
}
if ($input instanceof AbstractAnalyzer) {
return false;
}
if ($input instanceof AbstractEncoder) {
return false;
}
return true;
}
}
src/Drivers/AbstractInputHandler.php 0000644 00000002633 15073164571 0013560 0 ustar 00 decoders = count($decoders) ? $decoders : $this->decoders;
}
/**
* {@inheritdoc}
*
* @see InputHandlerInterface::handle()
*/
public function handle($input): ImageInterface|ColorInterface
{
return $this->chain()->handle($input);
}
/**
* Stack the decoder array into a nested decoder object
*
* @return AbstractDecoder
*/
protected function chain(): AbstractDecoder
{
if (count($this->decoders) == 0) {
throw new DecoderException('No decoders found in ' . get_class($this));
}
// get instance of last decoder in stack
list($classname) = array_slice(array_reverse($this->decoders), 0, 1);
$chain = new $classname();
// build decoder chain
foreach (array_slice(array_reverse($this->decoders), 1) as $classname) {
$chain = new $classname($chain);
}
return $chain;
}
}
src/Drivers/AbstractTextModifier.php 0000644 00000005367 15073164571 0013575 0 ustar 00 fontSizeInPixels() * $this->font->lineHeight()));
}
public function capHeight(): int
{
return $this->boxSize('T')->height();
}
public function fontSizeInPixels(): int
{
return $this->boxSize('Hy')->height();
}
/**
* Build TextBlock object from text string and align every line
* according to text writers font object and position.
*
* @return TextBlock
*/
public function alignedTextBlock(Point $position, string $text): TextBlock
{
$lines = new TextBlock($text);
$boundingBox = $this->boundingBox($lines, $position);
$pivot = $boundingBox->last();
$leading = $this->leadingInPixels();
$blockWidth = $this->lineWidth($lines->longestLine());
$x = $pivot->x();
$y = $this->font->hasFilename() ? $pivot->y() + $this->capHeight() : $pivot->y();
$x_adjustment = 0;
foreach ($lines as $line) {
$line_width = $this->lineWidth($line);
$x_adjustment = $this->font->alignment() == 'left' ? 0 : $blockWidth - $line_width;
$x_adjustment = $this->font->alignment() == 'right' ? intval(round($x_adjustment)) : $x_adjustment;
$x_adjustment = $this->font->alignment() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment;
$position = new Point($x + $x_adjustment, $y);
$position->rotate($this->font->angle(), $pivot);
$line->setPosition($position);
$y += $leading;
}
return $lines;
}
public function boundingBox(TextBlock $block, Point $pivot = null): Polygon
{
$pivot = $pivot ? $pivot : new Point();
// bounding box
$box = (new Rectangle(
$this->lineWidth($block->longestLine()),
$this->leadingInPixels() * ($block->count() - 1) + $this->capHeight()
));
// set pivot
$box->setPivot($pivot);
// align
$box->align($this->font->alignment());
$box->valign($this->font->valignment());
$box->rotate($this->font->angle());
return $box;
}
private function lineWidth(Line $line): int
{
return $this->boxSize((string) $line)->width();
}
}
src/Drivers/DrawModifier.php 0000644 00000002047 15073164571 0012052 0 ustar 00 drawable->position();
}
public function backgroundColor(): ColorInterface
{
try {
$color = $this->driver()->handleInput($this->drawable->backgroundColor());
} catch (DecoderException $e) {
return $this->driver()->handleInput('transparent');
}
return $color;
}
public function borderColor(): ColorInterface
{
try {
$color = $this->driver()->handleInput($this->drawable->borderColor());
} catch (DecoderException $e) {
return $this->driver()->handleInput('transparent');
}
return $color;
}
}
src/Drivers/DriverAnalyzer.php 0000644 00000001237 15073164571 0012437 0 ustar 00 driver;
}
/**
* Magic method to read attributes of underlying analyzer
*
* @param string $name
* @return mixed
*/
public function __get(string $name): mixed
{
return $this->analyzer->$name;
}
}
src/Drivers/DriverEncoder.php 0000644 00000001755 15073164571 0012236 0 ustar 00 driver;
}
/**
* Magic method to read attributes of underlying endcoder
*
* @param string $name
* @return mixed
*/
public function __get(string $name): mixed
{
return $this->encoder->$name;
}
/**
* Get return value of callback through output buffer
*
* @param callable $callback
* @return string
*/
protected function getBuffered(callable $callback): string
{
ob_start();
$callback();
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
}
}
src/Drivers/DriverModifier.php 0000644 00000001705 15073164571 0012410 0 ustar 00 driver;
}
/**
* Magic method to read attributes of underlying modifier
*
* @param string $name
* @return mixed
*/
public function __get(string $name): mixed
{
return $this->modifier->$name;
}
/**
* Magic method to call methods of underlying modifier
*
* @param string $name
* @param array $arguments
* @return mixed
*/
public function __call(string $name, array $arguments): mixed
{
return $this->modifier->$name(...$arguments);
}
}
src/Drivers/Gd/Analyzers/ColorspaceAnalyzer.php 0000644 00000000546 15073164571 0015602 0 ustar 00 core()->native());
}
}
src/Drivers/Gd/Analyzers/PixelColorAnalyzer.php 0000644 00000002074 15073164571 0015566 0 ustar 00 colorAt(
$image->colorspace(),
$image->core()->frame($this->frame_key)->native()
);
}
protected function colorAt(ColorspaceInterface $colorspace, GdImage $gd): ColorInterface
{
$index = @imagecolorat($gd, $this->x, $this->y);
if ($index === false) {
throw new GeometryException(
'The specified position is not in the valid image area.'
);
}
return $this->driver()->colorProcessor($colorspace)->nativeToColor($index);
}
}
src/Drivers/Gd/Analyzers/PixelColorsAnalyzer.php 0000644 00000001102 15073164571 0015740 0 ustar 00 colorspace();
foreach ($image as $frame) {
$colors->push(
parent::colorAt($colorspace, $frame->native())
);
}
return $colors;
}
}
src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php 0000644 00000000607 15073164571 0015651 0 ustar 00 core()->native()));
}
}
src/Drivers/Gd/Analyzers/WidthAnalyzer.php 0000644 00000000504 15073164571 0014561 0 ustar 00 core()->native());
}
}
src/Drivers/Gd/ColorProcessor.php 0000644 00000004617 15073164571 0013013 0 ustar 00 channel(Red::class)->value();
$g = $color->channel(Green::class)->value();
$b = $color->channel(Blue::class)->value();
$a = $color->channel(Alpha::class)->value();
// convert alpha value to gd alpha
// ([opaque]255-0[transparent]) to ([opaque]0-127[transparent])
$a = (int) $this->convertRange($a, 0, 255, 127, 0);
return ($a << 24) + ($r << 16) + ($g << 8) + $b;
}
public function nativeToColor(mixed $value): ColorInterface
{
if (! is_int($value)) {
throw new ColorException("GD driver can only decode colors in integer format.");
}
$a = ($value >> 24) & 0xFF;
$r = ($value >> 16) & 0xFF;
$g = ($value >> 8) & 0xFF;
$b = $value & 0xFF;
// convert gd apha integer to intervention alpha integer
// ([opaque]0-127[transparent]) to ([opaque]255-0[transparent])
$a = (int) static::convertRange($a, 127, 0, 0, 255);
return new Color($r, $g, $b, $a);
}
/**
* Convert input in range (min) to (max) to the corresponding value
* in target range (targetMin) to (targetMax).
*
* @param float|int $input
* @param float|int $min
* @param float|int $max
* @param float|int $targetMin
* @param float|int $targetMax
* @return float|int
*/
protected function convertRange(
float|int $input,
float|int $min,
float|int $max,
float|int $targetMin,
float|int $targetMax
): float|int {
return ceil(((($input - $min) * ($targetMax - $targetMin)) / ($max - $min)) + $targetMin);
}
}
src/Drivers/Gd/Core.php 0000644 00000002126 15073164571 0010716 0 ustar 00 push($frame);
return $this;
}
public function native(): mixed
{
return $this->first()->native();
}
public function setNative(mixed $native): self
{
$this->empty()->push(new Frame($native));
return $this;
}
public function frame(int $position): FrameInterface
{
return $this->getAtPosition($position);
}
public function loops(): int
{
return $this->loops;
}
public function setLoops(int $loops): self
{
$this->loops = $loops;
return $this;
}
public function first(): FrameInterface
{
return parent::first();
}
public function last(): FrameInterface
{
return parent::last();
}
}
src/Drivers/Gd/Decoders/Base64ImageDecoder.php 0000644 00000001132 15073164571 0015027 0 ustar 00 isValidBase64($input)) {
throw new DecoderException('Unable to decode input');
}
return parent::decode(base64_decode($input));
}
}
src/Drivers/Gd/Decoders/BinaryImageDecoder.php 0000644 00000005314 15073164571 0015235 0 ustar 00 mediaType($input) == 'image/gif') {
return $this->decodeGif($input); // decode (animated) gif
}
// build image instance
$image = new Image(
new Driver(),
$this->coreFromString($input),
$this->decodeExifData($input)
);
// fix image orientation
return match ($image->exif('IFD0.Orientation')) {
2 => $image->flip(),
3 => $image->rotate(180),
4 => $image->rotate(180)->flip(),
5 => $image->rotate(270)->flip(),
6 => $image->rotate(270),
7 => $image->rotate(90)->flip(),
8 => $image->rotate(90),
default => $image
};
}
private function coreFromString(string $input): Core
{
$data = @imagecreatefromstring($input);
if ($data === false) {
throw new DecoderException('Unable to decode input');
}
if (!imageistruecolor($data)) {
imagepalettetotruecolor($data);
}
imagesavealpha($data, true);
return new Core([
new Frame($data)
]);
}
private function decodeGif(string $input): ImageInterface
{
$gif = GifDecoder::decode($input);
if (!$gif->isAnimated()) {
return new Image(
new Driver(),
$this->coreFromString($input)
);
}
$splitter = GifSplitter::create($gif)->split();
$delays = $splitter->getDelays();
// build core
$core = new Core();
$core->setLoops($gif->getMainApplicationExtension()?->getLoops());
foreach ($splitter->coalesceToResources() as $key => $data) {
$core->push(
(new Frame($data))->setDelay($delays[$key] / 100)
);
}
return new Image(
new Driver(),
$core
);
}
}
src/Drivers/Gd/Decoders/ColorObjectDecoder.php 0000644 00000001157 15073164571 0015254 0 ustar 00 parseDataUri($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.php 0000644 00000001532 15073164571 0015503 0 ustar 00 getRealPath());
}
}
src/Drivers/Gd/Driver.php 0000644 00000005120 15073164571 0011256 0 ustar 00 core->add(
$this->driver->handleInput($source)->core()->first()->setDelay($delay)
);
return $this;
}
public function __invoke(): ImageInterface
{
return new Image(
$this->driver,
$this->core
);
}
};
$init($animation);
return call_user_func($animation);
}
/**
* {@inheritdoc}
*
* @see DriverInterface::handleInput()
*/
public function handleInput(mixed $input): ImageInterface|ColorInterface
{
return (new InputHandler())->handle($input);
}
/**
* {@inheritdoc}
*
* @see DriverInterface::colorProcessor()
*/
public function colorProcessor(ColorspaceInterface $colorspace): ColorProcessorInterface
{
return new ColorProcessor($colorspace);
}
}
src/Drivers/Gd/Encoders/AvifEncoder.php 0000644 00000001062 15073164571 0013753 0 ustar 00 core()->native();
$data = $this->getBuffered(function () use ($gd) {
imageavif($gd, null, $this->quality);
});
return new EncodedImage($data, 'image/avif');
}
}
src/Drivers/Gd/Encoders/BmpEncoder.php 0000644 00000001256 15073164571 0013611 0 ustar 00 modify(new LimitColorsModifier($this->color_limit));
$gd = $image->core()->native();
$data = $this->getBuffered(function () use ($gd) {
imagebmp($gd, null, false);
});
return new EncodedImage($data, 'image/bmp');
}
}
src/Drivers/Gd/Encoders/GifEncoder.php 0000644 00000002423 15073164571 0013575 0 ustar 00 isAnimated()) {
return $this->encodeAnimated($image);
}
$image = $image->modify(new LimitColorsModifier($this->color_limit));
$gd = $image->core()->native();
$data = $this->getBuffered(function () use ($gd) {
imagegif($gd);
});
return new EncodedImage($data, 'image/gif');
}
protected function encodeAnimated(ImageInterface $image): EncodedImage
{
$builder = GifBuilder::canvas(
$image->width(),
$image->height(),
$image->loops()
);
foreach ($image as $frame) {
$builder->addFrame(
$this->encode($frame->toImage($image->driver())),
$frame->delay()
);
}
return new EncodedImage($builder->encode(), 'image/gif');
}
}
src/Drivers/Gd/Encoders/JpegEncoder.php 0000644 00000001062 15073164571 0013753 0 ustar 00 core()->native();
$data = $this->getBuffered(function () use ($gd) {
imagejpeg($gd, null, $this->quality);
});
return new EncodedImage($data, 'image/jpeg');
}
}
src/Drivers/Gd/Encoders/PngEncoder.php 0000644 00000001253 15073164571 0013614 0 ustar 00 modify(new LimitColorsModifier($this->color_limit));
$gd = $image->core()->native();
$data = $this->getBuffered(function () use ($gd) {
imagepng($gd, null, -1);
});
return new EncodedImage($data, 'image/png');
}
}
src/Drivers/Gd/Encoders/WebpEncoder.php 0000644 00000001062 15073164571 0013763 0 ustar 00 core()->native();
$data = $this->getBuffered(function () use ($gd) {
imagewebp($gd, null, $this->quality);
});
return new EncodedImage($data, 'image/webp');
}
}
src/Drivers/Gd/Frame.php 0000644 00000004114 15073164571 0011057 0 ustar 00 native = $native;
return $this;
}
public function native(): GdImage
{
return $this->native;
}
public function size(): SizeInterface
{
return new Rectangle(imagesx($this->native), imagesy($this->native));
}
public function delay(): float
{
return $this->delay;
}
public function setDelay(float $delay): FrameInterface
{
$this->delay = $delay;
return $this;
}
public function dispose(): 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 offsetLeft(): int
{
return $this->offset_left;
}
public function setOffsetLeft(int $offset): FrameInterface
{
$this->offset_left = $offset;
return $this;
}
public function offsetTop(): int
{
return $this->offset_top;
}
public function setOffsetTop(int $offset): FrameInterface
{
$this->offset_top = $offset;
return $this;
}
}
src/Drivers/Gd/InputHandler.php 0000644 00000003064 15073164571 0012425 0 ustar 00 amount; $i++) {
imagefilter($frame->native(), IMG_FILTER_GAUSSIAN_BLUR);
}
}
return $image;
}
}
src/Drivers/Gd/Modifiers/BrightnessModifier.php 0000644 00000000744 15073164571 0015542 0 ustar 00 native(), IMG_FILTER_BRIGHTNESS, intval($this->level * 2.55));
}
return $image;
}
}
src/Drivers/Gd/Modifiers/ColorizeModifier.php 0000644 00000001254 15073164571 0015215 0 ustar 00 red * 2.55);
$green = round($this->green * 2.55);
$blue = round($this->blue * 2.55);
foreach ($image as $frame) {
imagefilter($frame->native(), IMG_FILTER_COLORIZE, $red, $green, $blue);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/ColorspaceModifier.php 0000644 00000001267 15073164571 0015525 0 ustar 00 targetColorspace(), RgbColorspace::class)) {
throw new NotSupportedException(
'Only RGB colorspace is supported with GD driver.'
);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/ContrastModifier.php 0000644 00000000730 15073164571 0015222 0 ustar 00 native(), IMG_FILTER_CONTRAST, ($this->level * -1));
}
return $image;
}
}
src/Drivers/Gd/Modifiers/CropModifier.php 0000644 00000003540 15073164571 0014332 0 ustar 00 crop($image);
foreach ($image as $frame) {
$this->cropFrame($frame, $crop);
}
return $image;
}
protected function cropFrame(FrameInterface $frame, SizeInterface $resizeTo): void
{
// create new image
$modified = $this->driver()
->createImage($resizeTo->width(), $resizeTo->height())
->core()
->native();
// get original image
$original = $frame->native();
// preserve transparency
$transIndex = imagecolortransparent($original);
if ($transIndex != -1) {
$rgba = imagecolorsforindex($modified, $transIndex);
$transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127);
imagefill($modified, 0, 0, $transColor);
imagecolortransparent($modified, $transColor);
}
// copy content from resource
imagecopyresampled(
$modified,
$original,
0,
0,
$resizeTo->pivot()->x() + $this->offset_x,
$resizeTo->pivot()->y() + $this->offset_y,
$resizeTo->width(),
$resizeTo->height(),
$resizeTo->width(),
$resizeTo->height(),
);
// set new content as recource
$frame->setNative($modified);
}
}
src/Drivers/Gd/Modifiers/DrawEllipseModifier.php 0000644 00000004433 15073164571 0015644 0 ustar 00 drawable->hasBorder()) {
// slightly smaller ellipse to keep 1px bordered edges clean
if ($this->drawable->hasBackgroundColor()) {
imagefilledellipse(
$frame->native(),
$this->position()->x(),
$this->position()->y(),
$this->drawable->width() - 1,
$this->drawable->height() - 1,
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
)
);
}
// gd's imageellipse ignores imagesetthickness
// so i use imagearc with 360 degrees instead.
imagesetthickness(
$frame->native(),
$this->drawable->borderSize(),
);
imagearc(
$frame->native(),
$this->position()->x(),
$this->position()->y(),
$this->drawable->width(),
$this->drawable->height(),
0,
360,
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->borderColor()
)
);
} else {
imagefilledellipse(
$frame->native(),
$this->position()->x(),
$this->position()->y(),
$this->drawable->width(),
$this->drawable->height(),
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
)
);
}
}
return $image;
}
}
src/Drivers/Gd/Modifiers/DrawLineModifier.php 0000644 00000001634 15073164571 0015136 0 ustar 00 native(),
$this->drawable->start()->x(),
$this->drawable->start()->y(),
$this->drawable->end()->x(),
$this->drawable->end()->y(),
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
)
);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/DrawPixelModifier.php 0000644 00000001447 15073164571 0015332 0 ustar 00 driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->color)
);
foreach ($image as $frame) {
imagesetpixel(
$frame->native(),
$this->position->x(),
$this->position->y(),
$color
);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/DrawPolygonModifier.php 0000644 00000002600 15073164571 0015670 0 ustar 00 drawable->hasBackgroundColor()) {
imagefilledpolygon(
$frame->native(),
$this->drawable->toArray(),
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
)
);
}
if ($this->drawable->hasBorder()) {
imagesetthickness($frame->native(), $this->drawable->borderSize());
imagepolygon(
$frame->native(),
$this->drawable->toArray(),
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->borderColor()
)
);
}
}
return $image;
}
}
src/Drivers/Gd/Modifiers/DrawRectangleModifier.php 0000644 00000003531 15073164571 0016151 0 ustar 00 drawable->hasBackgroundColor()) {
imagefilledrectangle(
$frame->native(),
$this->position()->x(),
$this->position()->y(),
$this->position()->x() + $this->drawable->width(),
$this->position()->y() + $this->drawable->height(),
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
)
);
}
// draw border
if ($this->drawable->hasBorder()) {
imagesetthickness($frame->native(), $this->drawable->borderSize());
imagerectangle(
$frame->native(),
$this->position()->x(),
$this->position()->y(),
$this->position()->x() + $this->drawable->width(),
$this->position()->y() + $this->drawable->height(),
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->borderColor()
)
);
}
}
return $image;
}
}
src/Drivers/Gd/Modifiers/FillModifier.php 0000644 00000003015 15073164571 0014312 0 ustar 00 color($image);
foreach ($image as $frame) {
if ($this->hasPosition()) {
$this->floodFillWithColor($frame, $color);
} else {
$this->fillAllWithColor($frame, $color);
}
}
return $image;
}
private function color(ImageInterface $image): int
{
return $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->color)
);
}
private function floodFillWithColor(Frame $frame, int $color): void
{
imagefill(
$frame->native(),
$this->position->x(),
$this->position->y(),
$color
);
}
private function fillAllWithColor(Frame $frame, int $color): void
{
imagealphablending($frame->native(), true);
imagefilledrectangle(
$frame->native(),
0,
0,
$frame->size()->width() - 1,
$frame->size()->height() - 1,
$color
);
}
}
src/Drivers/Gd/Modifiers/FitDownModifier.php 0000644 00000000544 15073164571 0015002 0 ustar 00 scaleDown($this->width, $this->height);
}
}
src/Drivers/Gd/Modifiers/FitModifier.php 0000644 00000003602 15073164571 0014150 0 ustar 00 getCropSize($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 = $this->driver()->createImage(
$resize->width(),
$resize->height()
)->core()->native();
// get original image
$original = $frame->native();
// preserve transparency
$transIndex = imagecolortransparent($original);
if ($transIndex != -1) {
$rgba = imagecolorsforindex($modified, $transIndex);
$transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127);
imagefill($modified, 0, 0, $transColor);
imagecolortransparent($modified, $transColor);
}
// copy content from resource
imagecopyresampled(
$modified,
$original,
0,
0,
$crop->pivot()->x(),
$crop->pivot()->y(),
$resize->width(),
$resize->height(),
$crop->width(),
$crop->height()
);
// set new content as resource
$frame->setNative($modified);
}
}
src/Drivers/Gd/Modifiers/FlipModifier.php 0000644 00000000633 15073164571 0014321 0 ustar 00 native(), IMG_FLIP_VERTICAL);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/FlopModifier.php 0000644 00000000635 15073164571 0014331 0 ustar 00 native(), IMG_FLIP_HORIZONTAL);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/GammaModifier.php 0000644 00000000704 15073164571 0014450 0 ustar 00 native(), 1, $this->gamma);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/GreyscaleModifier.php 0000644 00000000645 15073164571 0015350 0 ustar 00 native(), IMG_FILTER_GRAYSCALE);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/InvertModifier.php 0000644 00000000637 15073164571 0014702 0 ustar 00 native(), IMG_FILTER_NEGATE);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/LimitColorsModifier.php 0000644 00000003112 15073164571 0015662 0 ustar 00 limit === 0) {
return $image;
}
// limit is over threshold: no reduction
if ($this->limit > $this->threshold) {
return $image;
}
$width = $image->width();
$height = $image->height();
foreach ($image as $frame) {
// create empty gd
$reduced = imagecreatetruecolor($width, $height);
// create matte
$matte = imagecolorallocatealpha($reduced, 255, 255, 255, 127);
// fill with matte
imagefill($reduced, 0, 0, $matte);
imagealphablending($reduced, false);
// set transparency and get transparency index
imagecolortransparent($reduced, $matte);
// copy original image
imagecopy($reduced, $frame->native(), 0, 0, 0, 0, $width, $height);
// reduce limit by one to include possible transparency in palette
$limit = imagecolortransparent($frame->native()) === -1 ? $this->limit : $this->limit - 1;
// decrease colors
imagetruecolortopalette($reduced, true, $limit);
$frame->setNative($reduced);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/PadDownModifier.php 0000644 00000000151 15073164571 0014756 0 ustar 00 getCropSize($image);
$resize = $this->getResizeSize($image);
$background = $this->driver()->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 gd image
$modified = $this->driver()->createImage(
$resize->width(),
$resize->height()
)->modify(
new FillModifier($background)
)->core()->native();
// make image area transparent to keep transparency
// even if background-color is set
$transparent = imagecolorallocatealpha($modified, 255, 0, 255, 127);
imagealphablending($modified, false); // do not blend / just overwrite
imagecolortransparent($modified, $transparent);
imagefilledrectangle(
$modified,
$crop->pivot()->x(),
$crop->pivot()->y(),
$crop->pivot()->x() + $crop->width() - 1,
$crop->pivot()->y() + $crop->height() - 1,
$transparent
);
// copy image from original with blending alpha
imagealphablending($modified, true);
imagecopyresampled(
$modified,
$frame->native(),
$crop->pivot()->x(),
$crop->pivot()->y(),
0,
0,
$crop->width(),
$crop->height(),
$frame->size()->width(),
$frame->size()->height()
);
// set new content as recource
$frame->setNative($modified);
}
}
src/Drivers/Gd/Modifiers/PixelateModifier.php 0000644 00000000725 15073164571 0015204 0 ustar 00 native(), IMG_FILTER_PIXELATE, $this->size, true);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/PlaceModifier.php 0000644 00000002005 15073164571 0014446 0 ustar 00 driver()->handleInput($this->element);
$position = $this->getPosition($image, $watermark);
foreach ($image as $frame) {
imagealphablending($frame->native(), true);
imagecopy(
$frame->native(),
$watermark->core()->native(),
$position->x(),
$position->y(),
0,
0,
$watermark->width(),
$watermark->height()
);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/ProfileModifier.php 0000644 00000001055 15073164571 0015026 0 ustar 00 core()->setNative(
$this->chosenFrame($image, $this->position)->native()
);
return $image;
}
}
src/Drivers/Gd/Modifiers/ResizeDownModifier.php 0000644 00000000564 15073164571 0015523 0 ustar 00 size()->resizeDown($this->width, $this->height);
}
}
src/Drivers/Gd/Modifiers/ResizeModifier.php 0000644 00000004031 15073164571 0014664 0 ustar 00 getAdjustedSize($image);
foreach ($image as $frame) {
$this->resizeFrame($frame, $resizeTo);
}
return $image;
}
private function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): void
{
// create new image
$modified = imagecreatetruecolor(
$resizeTo->width(),
$resizeTo->height()
);
// get current GDImage
$current = $frame->native();
// preserve transparency
$transIndex = imagecolortransparent($current);
if ($transIndex != -1) {
$rgba = imagecolorsforindex($modified, $transIndex);
$transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127);
imagefill($modified, 0, 0, $transColor);
imagecolortransparent($modified, $transColor);
} else {
imagealphablending($modified, false);
imagesavealpha($modified, true);
}
// copy content from resource
imagecopyresampled(
$modified,
$current,
$resizeTo->pivot()->x(),
$resizeTo->pivot()->y(),
0,
0,
$resizeTo->width(),
$resizeTo->height(),
$frame->size()->width(),
$frame->size()->height()
);
// set new content as recource
$frame->setNative($modified);
}
protected function getAdjustedSize(ImageInterface $image): SizeInterface
{
return $image->size()->resize($this->width, $this->height);
}
}
src/Drivers/Gd/Modifiers/ResolutionModifier.php 0000644 00000001031 15073164571 0015563 0 ustar 00 x));
$y = intval(round($this->y));
foreach ($image as $frame) {
imageresolution($frame->native(), $x, $y);
}
return $image;
}
}
src/Drivers/Gd/Modifiers/RotateModifier.php 0000644 00000006316 15073164571 0014671 0 ustar 00 driver()->handleInput($this->background);
foreach ($image as $frame) {
$this->modifyFrame($frame, $background);
}
return $image;
}
/**
* Apply rotation modification on given frame, given background
* color is used for newly create image areas
*
* @param FrameInterface $frame
* @param ColorInterface $background
* @return void
*/
protected function modifyFrame(FrameInterface $frame, ColorInterface $background): void
{
// get transparent color from frame core
$transparent = match ($transparent = imagecolortransparent($frame->native())) {
-1 => imagecolorallocatealpha(
$frame->native(),
$background->channel(Red::class)->value(),
$background->channel(Green::class)->value(),
$background->channel(Blue::class)->value(),
127
),
default => $transparent,
};
// rotate original image against transparent background
$rotated = imagerotate(
$frame->native(),
$this->rotationAngle(),
$transparent
);
// create size from original after rotation
$container = (new Rectangle(
imagesx($rotated),
imagesy($rotated),
))->movePivot('center');
// create size from original and rotate points
$cutout = (new Rectangle(
imagesx($frame->native()),
imagesy($frame->native()),
$container->pivot()
))->align('center')
->valign('center')
->rotate($this->rotationAngle() * -1);
// create new gd image
$modified = $this->driver()->createImage(
imagesx($rotated),
imagesy($rotated)
)->modify(new FillModifier($background))
->core()
->native();
// draw the cutout on new gd image to have a transparent
// background where the rotated image will be placed
imagealphablending($modified, false);
imagefilledpolygon(
$modified,
$cutout->toArray(),
imagecolortransparent($modified)
);
// place rotated image on new gd image
imagealphablending($modified, true);
imagecopy(
$modified,
$rotated,
0,
0,
0,
0,
imagesx($rotated),
imagesy($rotated)
);
$frame->setNative($modified);
}
}
src/Drivers/Gd/Modifiers/ScaleDownModifier.php 0000644 00000000653 15073164571 0015310 0 ustar 00 size()->scaleDown($this->width, $this->height);
}
}
src/Drivers/Gd/Modifiers/ScaleModifier.php 0000644 00000000643 15073164571 0014457 0 ustar 00 size()->scale($this->width, $this->height);
}
}
src/Drivers/Gd/Modifiers/SharpenModifier.php 0000644 00000001453 15073164571 0015030 0 ustar 00 matrix();
foreach ($image as $frame) {
imageconvolution($frame->native(), $matrix, 1, 0);
}
return $image;
}
private 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/TextModifier.php 0000644 00000007032 15073164571 0014353 0 ustar 00 alignedTextBlock($this->position, $this->text);
$color = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->font->color())
);
foreach ($image as $frame) {
if ($this->font->hasFilename()) {
foreach ($lines as $line) {
imagettftext(
$frame->native(),
$this->adjustedFontSize(),
$this->font->angle() * -1,
$line->position()->x(),
$line->position()->y(),
$color,
$this->font->filename(),
$line
);
}
} else {
foreach ($lines as $line) {
imagestring(
$frame->native(),
$this->getGdFont(),
$line->position()->x(),
$line->position()->y(),
$line,
$color
);
}
}
}
return $image;
}
/**
* Calculate size of bounding box of given text
*
* @return Polygon
*/
protected function boxSize(string $text): Polygon
{
if (!$this->font->hasFilename()) {
// 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->adjustedFontSize(),
0,
$this->font->filename(),
$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;
}
private function adjustedFontSize(): float
{
return floatval(ceil($this->font->size() * .75));
}
private function getGdFont(): int
{
if (is_numeric($this->font->filename())) {
return intval($this->font->filename());
}
return 1;
}
private function getGdFontWidth(): int
{
return $this->getGdFont() + 4;
}
private 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/Imagick/Analyzers/ColorspaceAnalyzer.php 0000644 00000001146 15073164571 0016611 0 ustar 00 core()->native()->getImageColorspace()) {
Imagick::COLORSPACE_CMYK => new CmykColorspace(),
default => new RgbColorspace(),
};
}
}
src/Drivers/Imagick/Analyzers/HeightAnalyzer.php 0000644 00000000523 15073164571 0015725 0 ustar 00 core()->native()->getImageHeight();
}
}
src/Drivers/Imagick/Analyzers/PixelColorAnalyzer.php 0000644 00000001615 15073164571 0016600 0 ustar 00 colorAt(
$image->colorspace(),
$image->core()->frame($this->frame_key)->native()
);
}
protected function colorAt(ColorspaceInterface $colorspace, Imagick $imagick): ColorInterface
{
return $this->driver()
->colorProcessor($colorspace)
->nativeToColor(
$imagick->getImagePixelColor($this->x, $this->y)
);
}
}
src/Drivers/Imagick/Analyzers/PixelColorsAnalyzer.php 0000644 00000001107 15073164571 0016757 0 ustar 00 colorspace();
foreach ($image as $frame) {
$colors->push(
parent::colorAt($colorspace, $frame->native())
);
}
return $colors;
}
}
src/Drivers/Imagick/Analyzers/ProfileAnalyzer.php 0000644 00000001156 15073164571 0016120 0 ustar 00 core()->native()->getImageProfiles('icc');
if (!array_key_exists('icc', $profiles)) {
throw new ColorException('No ICC profile found in image.');
}
return new Profile($profiles['icc']);
}
}
src/Drivers/Imagick/Analyzers/ResolutionAnalyzer.php 0000644 00000000621 15073164571 0016657 0 ustar 00 core()->native()->getImageResolution());
}
}
src/Drivers/Imagick/Analyzers/WidthAnalyzer.php 0000644 00000000521 15073164571 0015572 0 ustar 00 core()->native()->getImageWidth();
}
}
src/Drivers/Imagick/ColorProcessor.php 0000644 00000002664 15073164571 0014025 0 ustar 00 convertTo($this->colorspace)
);
}
public function nativeToColor(mixed $native): ColorInterface
{
return match (get_class($this->colorspace)) {
CmykColorspace::class => $this->colorspace->colorFromNormalized([
$native->getColorValue(Imagick::COLOR_CYAN),
$native->getColorValue(Imagick::COLOR_MAGENTA),
$native->getColorValue(Imagick::COLOR_YELLOW),
$native->getColorValue(Imagick::COLOR_BLACK),
]),
default => $this->colorspace->colorFromNormalized([
$native->getColorValue(Imagick::COLOR_RED),
$native->getColorValue(Imagick::COLOR_GREEN),
$native->getColorValue(Imagick::COLOR_BLUE),
$native->getColorValue(Imagick::COLOR_ALPHA),
]),
};
}
}
src/Drivers/Imagick/Core.php 0000644 00000005226 15073164571 0011734 0 ustar 00 native();
$imagick->setImageDelay($frame->delay());
$imagick->setImageDispose($frame->dispose());
$size = $frame->size();
$imagick->setImagePage(
$size->width(),
$size->height(),
$frame->offsetLeft(),
$frame->offsetTop()
);
$this->imagick->addImage($imagick);
return $this;
}
public function count(): int
{
return $this->imagick->getNumberImages();
}
public function current(): mixed
{
$this->imagick->setIteratorIndex($this->iteratorIndex);
return new Frame($this->imagick->current());
}
public function next(): void
{
$this->iteratorIndex = $this->iteratorIndex + 1;
}
public function key(): mixed
{
return $this->iteratorIndex;
}
public function valid(): bool
{
try {
$result = $this->imagick->setIteratorIndex($this->iteratorIndex);
} catch (ImagickException $e) {
return false;
}
return $result;
}
public function rewind(): void
{
$this->iteratorIndex = 0;
}
public function native(): mixed
{
return $this->imagick;
}
public function setNative(mixed $native): CoreInterface
{
$this->imagick = $native;
return $this;
}
public function frame(int $position): FrameInterface
{
foreach ($this->imagick as $core) {
if ($core->getIteratorIndex() == $position) {
return new Frame($core);
}
}
throw new AnimationException('Frame #' . $position . ' is not be found in the image.');
}
public function loops(): int
{
return $this->imagick->getImageIterations();
}
public function setLoops(int $loops): CoreInterface
{
$this->imagick = $this->imagick->coalesceImages();
$this->imagick->setImageIterations($loops);
return $this;
}
public function first(): FrameInterface
{
return $this->frame(0);
}
public function last(): FrameInterface
{
return $this->frame($this->count());
}
}
src/Drivers/Imagick/Decoders/Base64ImageDecoder.php 0000644 00000001137 15073164571 0016046 0 ustar 00 isValidBase64($input)) {
throw new DecoderException('Unable to decode input');
}
return parent::decode(base64_decode($input));
}
}
src/Drivers/Imagick/Decoders/BinaryImageDecoder.php 0000644 00000003324 15073164571 0016246 0 ustar 00 readImageBlob($input);
} catch (ImagickException $e) {
throw new DecoderException('Unable to decode input');
}
$imagick = $imagick->coalesceImages();
// fix image orientation
switch ($imagick->getImageOrientation()) {
case Imagick::ORIENTATION_BOTTOMRIGHT:
$imagick->rotateimage("#000", 180);
break;
case Imagick::ORIENTATION_RIGHTTOP:
$imagick->rotateimage("#000", 90);
break;
case Imagick::ORIENTATION_LEFTBOTTOM:
$imagick->rotateimage("#000", -90);
break;
}
// set new orientation in image
$imagick->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
$image = new Image(
new Driver(),
new Core($imagick),
$this->decodeExifData($input)
);
return $image;
}
}
src/Drivers/Imagick/Decoders/ColorObjectDecoder.php 0000644 00000001164 15073164571 0016264 0 ustar 00 parseDataUri($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.php 0000644 00000001537 15073164571 0016522 0 ustar 00 getRealPath());
}
}
src/Drivers/Imagick/Driver.php 0000644 00000005326 15073164571 0012300 0 ustar 00 newImage($width, $height, $background, 'png');
$imagick->setType(Imagick::IMGTYPE_UNDEFINED);
$imagick->setImageType(Imagick::IMGTYPE_UNDEFINED);
$imagick->setColorspace(Imagick::COLORSPACE_SRGB);
$imagick->setImageResolution(96, 96);
return new Image($this, new Core($imagick));
}
/**
* {@inheritdoc}
*
* @see DriverInterface::createAnimation()
*/
public function createAnimation(callable $init): ImageInterface
{
$imagick = new Imagick();
$imagick->setFormat('gif');
$animation = new class ($this, $imagick)
{
public function __construct(
protected DriverInterface $driver,
public Imagick $imagick
) {
}
public function add($source, float $delay = 1): self
{
$native = $this->driver->handleInput($source)->core()->native();
$native->setImageDelay($delay * 100);
$this->imagick->addImage($native);
return $this;
}
public function __invoke(): ImageInterface
{
return new Image(
$this->driver,
new Core($this->imagick)
);
}
};
$init($animation);
return call_user_func($animation);
}
/**
* {@inheritdoc}
*
* @see DriverInterface::handleInput()
*/
public function handleInput(mixed $input): ImageInterface|ColorInterface
{
return (new InputHandler())->handle($input);
}
/**
* {@inheritdoc}
*
* @see DriverInterface::colorProcessor()
*/
public function colorProcessor(ColorspaceInterface $colorspace): ColorProcessorInterface
{
return new ColorProcessor($colorspace);
}
}
src/Drivers/Imagick/Encoders/AvifEncoder.php 0000644 00000001535 15073164571 0014772 0 ustar 00 core()->native();
$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/avif');
}
}
src/Drivers/Imagick/Encoders/BmpEncoder.php 0000644 00000001552 15073164571 0014622 0 ustar 00 modify(new LimitColorsModifier($this->color_limit));
$imagick = $image->core()->native();
$imagick->setFormat($format);
$imagick->setImageFormat($format);
$imagick->setCompression($compression);
$imagick->setImageCompression($compression);
return new EncodedImage($imagick->getImagesBlob(), 'image/bmp');
}
}
src/Drivers/Imagick/Encoders/GifEncoder.php 0000644 00000001556 15073164571 0014615 0 ustar 00 modify(new LimitColorsModifier($this->color_limit));
$format = 'gif';
$compression = Imagick::COMPRESSION_LZW;
$imagick = $image->core()->native();
$imagick->setFormat($format);
$imagick->setImageFormat($format);
$imagick->setCompression($compression);
$imagick->setImageCompression($compression);
return new EncodedImage($imagick->getImagesBlob(), 'image/gif');
}
}
src/Drivers/Imagick/Encoders/JpegEncoder.php 0000644 00000001701 15073164571 0014765 0 ustar 00 core()->native();
$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.php 0000644 00000001553 15073164571 0014631 0 ustar 00 modify(new LimitColorsModifier($this->color_limit));
$imagick = $image->core()->native();
$imagick->setFormat($format);
$imagick->setImageFormat($format);
$imagick->setCompression($compression);
$imagick->setImageCompression($compression);
return new EncodedImage($imagick->getImagesBlob(), 'image/png');
}
}
src/Drivers/Imagick/Encoders/WebpEncoder.php 0000644 00000001716 15073164571 0015003 0 ustar 00 core()->native();
$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/Frame.php 0000644 00000006422 15073164571 0012075 0 ustar 00 native()));
}
/**
* {@inheritdoc}
*
* @see DriverInterface::setNative()
*/
public function setNative($native): FrameInterface
{
$this->native = $native;
return $this;
}
/**
* {@inheritdoc}
*
* @see DriverInterface::native()
*/
public function native(): Imagick
{
return $this->native;
}
/**
* {@inheritdoc}
*
* @see DriverInterface::size()
*/
public function size(): SizeInterface
{
return new Rectangle(
$this->native->getImageWidth(),
$this->native->getImageHeight()
);
}
/**
* {@inheritdoc}
*
* @see DriverInterface::delay()
*/
public function delay(): float
{
return $this->native->getImageDelay() / 100;
}
/**
* {@inheritdoc}
*
* @see DriverInterface::setDelay()
*/
public function setDelay(float $delay): FrameInterface
{
$this->native->setImageDelay(intval(round($delay * 100)));
return $this;
}
/**
* {@inheritdoc}
*
* @see DriverInterface::dispose()
*/
public function dispose(): int
{
return $this->native->getImageDispose();
}
/**
* {@inheritdoc}
*
* @see DriverInterface::setDispose()
*/
public function setDispose(int $dispose): FrameInterface
{
$this->native->setImageDispose($dispose);
return $this;
}
/**
* {@inheritdoc}
*
* @see DriverInterface::setOffset()
*/
public function setOffset(int $left, int $top): FrameInterface
{
$this->native->setImagePage(
$this->native->getImageWidth(),
$this->native->getImageHeight(),
$left,
$top
);
return $this;
}
/**
* {@inheritdoc}
*
* @see DriverInterface::offsetLeft()
*/
public function offsetLeft(): int
{
return $this->native->getImagePage()['x'];
}
/**
* {@inheritdoc}
*
* @see DriverInterface::setOffsetLeft()
*/
public function setOffsetLeft(int $offset): FrameInterface
{
return $this->setOffset($offset, $this->offsetTop());
}
/**
* {@inheritdoc}
*
* @see DriverInterface::offsetTop()
*/
public function offsetTop(): int
{
return $this->native->getImagePage()['y'];
}
/**
* {@inheritdoc}
*
* @see DriverInterface::setOffsetTop()
*/
public function setOffsetTop(int $offset): FrameInterface
{
return $this->setOffset($this->offsetLeft(), $offset);
}
}
src/Drivers/Imagick/InputHandler.php 0000644 00000003141 15073164571 0013433 0 ustar 00 native()->blurImage(1 * $this->amount, 0.5 * $this->amount);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/BrightnessModifier.php 0000644 00000000725 15073164571 0016553 0 ustar 00 native()->modulateImage(100 + $this->level, 100, 100);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/ColorizeModifier.php 0000644 00000002161 15073164571 0016225 0 ustar 00 normalizeLevel($this->red);
$green = $this->normalizeLevel($this->green);
$blue = $this->normalizeLevel($this->blue);
foreach ($image as $frame) {
$qrange = $frame->native()->getQuantumRange();
$frame->native()->levelImage(0, $red, $qrange['quantumRangeLong'], Imagick::CHANNEL_RED);
$frame->native()->levelImage(0, $green, $qrange['quantumRangeLong'], Imagick::CHANNEL_GREEN);
$frame->native()->levelImage(0, $blue, $qrange['quantumRangeLong'], Imagick::CHANNEL_BLUE);
}
return $image;
}
private function normalizeLevel(int $level): int
{
return $level > 0 ? intval(round($level / 5)) : intval(round(($level + 100) / 100));
}
}
src/Drivers/Imagick/Modifiers/ColorspaceModifier.php 0000644 00000002427 15073164571 0016536 0 ustar 00 Imagick::COLORSPACE_SRGB,
CmykColorspace::class => Imagick::COLORSPACE_CMYK,
];
public function apply(ImageInterface $image): ImageInterface
{
$colorspace = $this->targetColorspace();
$imagick = $image->core()->native();
$imagick->transformImageColorspace(
$this->getImagickColorspace($colorspace)
);
return $image;
}
private function getImagickColorspace(ColorspaceInterface $colorspace): int
{
if (!array_key_exists(get_class($colorspace), self::$mapping)) {
throw new NotSupportedException('Given colorspace is not supported.');
}
return self::$mapping[get_class($colorspace)];
}
}
src/Drivers/Imagick/Modifiers/ContrastModifier.php 0000644 00000000752 15073164571 0016240 0 ustar 00 native()->sigmoidalContrastImage($this->level > 0, abs($this->level / 4), 0);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/CropModifier.php 0000644 00000001426 15073164571 0015345 0 ustar 00 crop($image);
foreach ($image as $frame) {
$frame->native()->extentImage(
$crop->width(),
$crop->height(),
$crop->pivot()->x() + $this->offset_x,
$crop->pivot()->y() + $this->offset_y
);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/DrawEllipseModifier.php 0000644 00000002477 15073164571 0016664 0 ustar 00 driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
);
$border_color = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->borderColor()
);
foreach ($image as $frame) {
$drawing = new ImagickDraw();
$drawing->setFillColor($background_color);
if ($this->drawable->hasBorder()) {
$drawing->setStrokeWidth($this->drawable->borderSize());
$drawing->setStrokeColor($border_color);
}
$drawing->ellipse(
$this->position()->x(),
$this->position()->y(),
$this->drawable->width() / 2,
$this->drawable->height() / 2,
0,
360
);
$frame->native()->drawImage($drawing);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/DrawLineModifier.php 0000644 00000002065 15073164571 0016147 0 ustar 00 setStrokeWidth($this->drawable->width());
$drawing->setStrokeColor(
$this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
)
);
$drawing->line(
$this->drawable->start()->x(),
$this->drawable->start()->y(),
$this->drawable->end()->x(),
$this->drawable->end()->y(),
);
foreach ($image as $frame) {
$frame->native()->drawImage($drawing);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/DrawPixelModifier.php 0000644 00000001515 15073164571 0016340 0 ustar 00 driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->color)
);
$pixel = new ImagickDraw();
$pixel->setFillColor($color);
$pixel->point($this->position->x(), $this->position->y());
foreach ($image as $frame) {
$frame->native()->drawImage($pixel);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/DrawPolygonModifier.php 0000644 00000003052 15073164571 0016704 0 ustar 00 drawable->hasBackgroundColor()) {
$background_color = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
);
$drawing->setFillColor($background_color);
}
if ($this->drawable->hasBorder()) {
$border_color = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->borderColor()
);
$drawing->setStrokeColor($border_color);
$drawing->setStrokeWidth($this->drawable->borderSize());
}
$drawing->polygon($this->points());
foreach ($image as $frame) {
$frame->native()->drawImage($drawing);
}
return $image;
}
private function points(): array
{
$points = [];
foreach ($this->drawable as $point) {
$points[] = ['x' => $point->x(), 'y' => $point->y()];
}
return $points;
}
}
src/Drivers/Imagick/Modifiers/DrawRectangleModifier.php 0000644 00000002775 15073164571 0017174 0 ustar 00 driver()->colorProcessor($image->colorspace())->colorToNative(
$this->backgroundColor()
);
$border_color = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->borderColor()
);
$drawing->setFillColor($background_color);
if ($this->drawable->hasBorder()) {
$drawing->setStrokeColor($border_color);
$drawing->setStrokeWidth($this->drawable->borderSize());
}
// build rectangle
$drawing->rectangle(
$this->position()->x(),
$this->position()->y(),
$this->position()->x() + $this->drawable->width(),
$this->position()->y() + $this->drawable->height()
);
foreach ($image as $frame) {
$frame->native()->drawImage($drawing);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/FillModifier.php 0000644 00000003403 15073164571 0015325 0 ustar 00 driver()->handleInput($this->color);
$pixel = $this->driver()
->colorProcessor($image->colorspace())
->colorToNative($color);
foreach ($image as $frame) {
if ($this->hasPosition()) {
$this->floodFillWithColor($frame, $pixel);
} else {
$this->fillAllWithColor($frame, $pixel);
}
}
return $image;
}
private function floodFillWithColor(Frame $frame, ImagickPixel $pixel): void
{
$target = $frame->native()->getImagePixelColor(
$this->position->x(),
$this->position->y()
);
$frame->native()->floodfillPaintImage(
$pixel,
100,
$target,
$this->position->x(),
$this->position->y(),
false,
Imagick::CHANNEL_ALL
);
}
private function fillAllWithColor(Frame $frame, ImagickPixel $pixel): void
{
$draw = new ImagickDraw();
$draw->setFillColor($pixel);
$draw->rectangle(
0,
0,
$frame->native()->getImageWidth(),
$frame->native()->getImageHeight()
);
$frame->native()->drawImage($draw);
}
}
src/Drivers/Imagick/Modifiers/FitDownModifier.php 0000644 00000000551 15073164571 0016012 0 ustar 00 scaleDown($this->width, $this->height);
}
}
src/Drivers/Imagick/Modifiers/FitModifier.php 0000644 00000001664 15073164571 0015170 0 ustar 00 getCropSize($image);
$resize = $this->getResizeSize($crop);
foreach ($image as $frame) {
$frame->native()->extentImage(
$crop->width(),
$crop->height(),
$crop->pivot()->x(),
$crop->pivot()->y()
);
$frame->native()->scaleImage(
$resize->width(),
$resize->height()
);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/FlipModifier.php 0000644 00000000617 15073164571 0015335 0 ustar 00 native()->flipImage();
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/FlopModifier.php 0000644 00000000617 15073164571 0015343 0 ustar 00 native()->flopImage();
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/GammaModifier.php 0000644 00000000677 15073164571 0015473 0 ustar 00 native()->gammaImage($this->gamma);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/GreyscaleModifier.php 0000644 00000000643 15073164571 0016360 0 ustar 00 native()->modulateImage(100, 0, 100);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/InvertModifier.php 0000644 00000000630 15073164571 0015705 0 ustar 00 native()->negateImage(false);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/LimitColorsModifier.php 0000644 00000001557 15073164571 0016707 0 ustar 00 limit === 0) {
return $image;
}
// limit is over threshold: no reduction
if ($this->limit > $this->threshold) {
return $image;
}
foreach ($image as $frame) {
$frame->native()->quantizeImage(
$this->limit,
$frame->native()->getImageColorspace(),
0,
false,
false
);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/PadDownModifier.php 0000644 00000000156 15073164571 0015775 0 ustar 00 getCropSize($image);
$resize = $this->getResizeSize($image);
$background = $this->driver()->handleInput($this->background);
$imagick = new Imagick();
foreach ($image as $frame) {
// resize current core
$frame->native()->scaleImage(
$crop->width(),
$crop->height()
);
// create new canvas, to get newly emerged background color
$canvas = $this->buildBaseCanvas($crop, $resize, $background);
// place current core onto canvas
$canvas->compositeImage(
$frame->native(),
Imagick::COMPOSITE_DEFAULT,
$crop->pivot()->x(),
$crop->pivot()->y()
);
$imagick->addImage($canvas);
}
return new Image(
$image->driver(),
new Core($imagick),
$image->exif()
);
}
protected function buildBaseCanvas(SizeInterface $crop, SizeInterface $resize, Color $background): Imagick
{
// build base canvas in target size
$canvas = $this->driver()->createImage(
$resize->width(),
$resize->height()
)->modify(
new FillModifier($background)
)->core()->native();
// 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(new ImagickPixel($fill));
$draw->rectangle(
$crop->pivot()->x(),
$crop->pivot()->y(),
$crop->pivot()->x() + $crop->width() - 1,
$crop->pivot()->y() + $crop->height() - 1
);
$canvas->drawImage($draw);
$canvas->transparentPaintImage($fill, 0, 0, false);
return $canvas;
}
}
src/Drivers/Imagick/Modifiers/PixelateModifier.php 0000644 00000001465 15073164571 0016220 0 ustar 00 pixelateFrame($frame);
}
return $image;
}
protected function pixelateFrame(Frame $frame): void
{
$size = $frame->size();
$frame->native()->scaleImage(
round(max(1, ($size->width() / $this->size))),
round(max(1, ($size->height() / $this->size)))
);
$frame->native()->scaleImage($size->width(), $size->height());
}
}
src/Drivers/Imagick/Modifiers/PlaceModifier.php 0000644 00000001617 15073164571 0015470 0 ustar 00 driver()->handleInput($this->element);
$position = $this->getPosition($image, $watermark);
foreach ($image as $frame) {
$frame->native()->compositeImage(
$watermark->core()->native(),
Imagick::COMPOSITE_DEFAULT,
$position->x(),
$position->y()
);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/ProfileModifier.php 0000644 00000001275 15073164571 0016044 0 ustar 00 core()->native();
$result = $imagick->profileImage('icc', (string) $this->profile);
if ($result === false) {
throw new ColorException('ICC color profile could not be set.');
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/ProfileRemovalModifier.php 0000644 00000001122 15073164571 0017361 0 ustar 00 core()->native();
$result = $imagick->profileImage('icc', null);
if ($result === false) {
throw new ColorException('ICC color profile could not be removed.');
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/RemoveAnimationModifier.php 0000644 00000001337 15073164571 0017540 0 ustar 00 chosenFrame($image, $this->position);
$imagick->addImage($frame->native()->getImage());
// set new imagick to image
$image->core()->setNative($imagick);
return $image;
}
}
src/Drivers/Imagick/Modifiers/ResizeDownModifier.php 0000644 00000000571 15073164571 0016533 0 ustar 00 size()->resizeDown($this->width, $this->height);
}
}
src/Drivers/Imagick/Modifiers/ResizeModifier.php 0000644 00000001456 15073164571 0015706 0 ustar 00 getAdjustedSize($image);
foreach ($image as $frame) {
$frame->native()->scaleImage(
$resizeTo->width(),
$resizeTo->height()
);
}
return $image;
}
protected function getAdjustedSize(ImageInterface $image): SizeInterface
{
return $image->size()->resize($this->width, $this->height);
}
}
src/Drivers/Imagick/Modifiers/ResolutionModifier.php 0000644 00000000722 15073164571 0016603 0 ustar 00 core()->native();
$imagick->setImageResolution($this->x, $this->y);
return $image;
}
}
src/Drivers/Imagick/Modifiers/RotateModifier.php 0000644 00000001325 15073164571 0015676 0 ustar 00 driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->background)
);
foreach ($image as $frame) {
$frame->native()->rotateImage(
$background,
$this->rotationAngle() * -1
);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/ScaleDownModifier.php 0000644 00000000660 15073164571 0016320 0 ustar 00 size()->scaleDown($this->width, $this->height);
}
}
src/Drivers/Imagick/Modifiers/ScaleModifier.php 0000644 00000000650 15073164571 0015467 0 ustar 00 size()->scale($this->width, $this->height);
}
}
src/Drivers/Imagick/Modifiers/SharpenModifier.php 0000644 00000000727 15073164571 0016045 0 ustar 00 native()->unsharpMaskImage(1, 1, $this->amount / 6.25, 0);
}
return $image;
}
}
src/Drivers/Imagick/Modifiers/TextModifier.php 0000644 00000004752 15073164571 0015373 0 ustar 00 alignedTextBlock($this->position, $this->text);
$color = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->font->color())
);
$draw = $this->toImagickDraw($color);
foreach ($image as $frame) {
foreach ($lines as $line) {
$frame->native()->annotateImage(
$draw,
$line->position()->x(),
$line->position()->y(),
$this->font->angle(),
$line
);
}
}
return $image;
}
/**
* Calculate box size of current font
*
* @return Polygon
*/
protected function boxSize(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'])),
));
}
private function toImagickDraw(?ImagickPixel $color = null): ImagickDraw
{
if (!$this->font->hasFilename()) {
throw new FontException('No font file specified.');
}
$draw = new ImagickDraw();
$draw->setStrokeAntialias(true);
$draw->setTextAntialias(true);
$draw->setFont($this->font->filename());
$draw->setFontSize($this->font->size());
$draw->setTextAlignment(Imagick::ALIGN_LEFT);
if ($color) {
$draw->setFillColor($color);
}
return $draw;
}
}
src/Drivers/Imagick/Traits/CanHandleColors.php 0000644 00000003754 15073164571 0015315 0 ustar 00 $colorspace->colorFromNormalized([
$pixel->getColorValue(Imagick::COLOR_CYAN),
$pixel->getColorValue(Imagick::COLOR_MAGENTA),
$pixel->getColorValue(Imagick::COLOR_YELLOW),
$pixel->getColorValue(Imagick::COLOR_BLACK),
]),
default => $colorspace->colorFromNormalized([
$pixel->getColorValue(Imagick::COLOR_RED),
$pixel->getColorValue(Imagick::COLOR_GREEN),
$pixel->getColorValue(Imagick::COLOR_BLUE),
$pixel->getColorValue(Imagick::COLOR_ALPHA),
]),
};
}
/**
* Transforms given color to the corresponding ImagickPixel in given colorspace
*
* Colorspaces of color might be different from given colorspace. You might
* have a CMYK Image but give an rgb color to a method. This makes sure
* that the color is converted to the given colorspace. In this case
* the image colorspace which is passed to this method.
*
* @param ColorInterface $color
* @param ColorspaceInterface $colorspace
* @return ImagickPixel
*/
public function colorToPixel(ColorInterface $color, ColorspaceInterface $colorspace): ImagickPixel
{
return new ImagickPixel(
(string) $color->convertTo($colorspace)
);
}
}
src/EncodedImage.php 0000644 00000001510 15073164571 0010356 0 ustar 00 mimetype;
}
/**
* Transform encoded image data into an data uri string
*
* @return string
*/
public function toDataUri(): string
{
return sprintf('data:%s;base64,%s', $this->mimetype, base64_encode($this->data));
}
}
src/Encoders/AbstractEncoder.php 0000644 00000000677 15073164571 0012674 0 ustar 00 encode($this);
}
}
src/Encoders/AvifEncoder.php 0000644 00000000243 15073164571 0012003 0 ustar 00 data;
}
/**
* Create file pointer from encoded image
*
* @return resource
*/
public function toFilePointer()
{
return $this->buildFilePointer($this->toString());
}
/**
* Return byte size of encoded image
*
* @return int
*/
public function size(): int
{
return mb_strlen($this->data);
}
/**
* Cast encoded image object to string
*
* @return string
*/
public function __toString(): string
{
return $this->toString();
}
}
src/Geometry/Circle.php 0000644 00000001426 15073164571 0011054 0 ustar 00 setWidth($diameter);
$this->setHeight($diameter);
}
public function setDiameter(int $diameter): self
{
$this->setWidth($diameter);
$this->setHeight($diameter);
return $this;
}
public function diameter(): int
{
return $this->diameter;
}
public function setRadius(int $radius): self
{
return $this->setDiameter(intval($radius * 2));
}
public function radius(): int
{
return intval($this->diameter / 2);
}
}
src/Geometry/Ellipse.php 0000644 00000002270 15073164571 0011246 0 ustar 00 pivot;
}
public function pivot(): PointInterface
{
return $this->pivot;
}
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 width(): int
{
return $this->width;
}
public function height(): int
{
return $this->height;
}
}
src/Geometry/Factories/CircleFactory.php 0000644 00000000601 15073164571 0014315 0 ustar 00 ellipse->setSize($radius * 2, $radius * 2);
return $this;
}
public function diameter(int $diameter): self
{
$this->ellipse->setSize($diameter, $diameter);
return $this;
}
}
src/Geometry/Factories/EllipseFactory.php 0000644 00000001702 15073164571 0014514 0 ustar 00 ellipse = is_a($init, Ellipse::class) ? $init : new Ellipse(0, 0, $pivot);
if (is_callable($init)) {
$init($this);
}
}
public function size(int $width, int $height): self
{
$this->ellipse->setSize($width, $height);
return $this;
}
public function background(mixed $color): self
{
$this->ellipse->setBackgroundColor($color);
return $this;
}
public function border(mixed $color, int $size = 1): self
{
$this->ellipse->setBorder($color, $size);
return $this;
}
public function __invoke(): Ellipse
{
return $this->ellipse;
}
}
src/Geometry/Factories/LineFactory.php 0000644 00000002661 15073164571 0014013 0 ustar 00 line = is_a($init, Line::class) ? $init : new Line(new Point(), new Point());
if (is_callable($init)) {
$init($this);
}
}
public function color(mixed $color): self
{
$this->line->setBackgroundColor($color);
$this->line->setBorderColor($color);
return $this;
}
public function background(mixed $color): self
{
$this->line->setBackgroundColor($color);
$this->line->setBorderColor($color);
return $this;
}
public function border(mixed $color, int $size = 1): self
{
$this->line->setBackgroundColor($color);
$this->line->setBorderColor($color);
$this->line->setWidth($size);
return $this;
}
public function width(int $size): self
{
$this->line->setWidth($size);
return $this;
}
public function from(int $x, int $y): self
{
$this->line->setStart(new Point($x, $y));
return $this;
}
public function to(int $x, int $y): self
{
$this->line->setEnd(new Point($x, $y));
return $this;
}
public function __invoke(): Line
{
return $this->line;
}
}
src/Geometry/Factories/PolygonFactory.php 0000644 00000001633 15073164571 0014551 0 ustar 00 polygon = is_a($init, Polygon::class) ? $init : new Polygon([]);
if (is_callable($init)) {
$init($this);
}
}
public function point(int $x, int $y): self
{
$this->polygon->addPoint(new Point($x, $y));
return $this;
}
public function background(mixed $color): self
{
$this->polygon->setBackgroundColor($color);
return $this;
}
public function border(mixed $color, int $size = 1): self
{
$this->polygon->setBorder($color, $size);
return $this;
}
public function __invoke(): Polygon
{
return $this->polygon;
}
}
src/Geometry/Factories/RectangleFactory.php 0000644 00000001734 15073164571 0015030 0 ustar 00 rectangle = is_a($init, Rectangle::class) ? $init : new Rectangle(0, 0, $pivot);
if (is_callable($init)) {
$init($this);
}
}
public function size(int $width, int $height): self
{
$this->rectangle->setSize($width, $height);
return $this;
}
public function background(mixed $color): self
{
$this->rectangle->setBackgroundColor($color);
return $this;
}
public function border(mixed $color, int $size = 1): self
{
$this->rectangle->setBorder($color, $size);
return $this;
}
public function __invoke(): Rectangle
{
return $this->rectangle;
}
}
src/Geometry/Line.php 0000644 00000002642 15073164571 0010543 0 ustar 00 start;
}
public function width(): int
{
return $this->width;
}
public function setWidth(int $width): self
{
$this->width = $width;
return $this;
}
public function start(): Point
{
return $this->start;
}
public function end(): Point
{
return $this->end;
}
public function setStart(Point $start): self
{
$this->start = $start;
return $this;
}
public function from(int $x, int $y): self
{
$this->start()->setX($x);
$this->start()->setY($y);
return $this;
}
public function to(int $x, int $y): self
{
$this->end()->setX($x);
$this->end()->setY($y);
return $this;
}
public function setEnd(Point $end): self
{
$this->end = $end;
return $this;
}
}
src/Geometry/Pixel.php 0000644 00000001013 15073164571 0010724 0 ustar 00 background = $background;
return $this;
}
public function backgroundColor(): ColorInterface
{
return $this->background;
}
}
src/Geometry/Point.php 0000644 00000004145 15073164571 0010745 0 ustar 00 x = $x;
return $this;
}
/**
* Get X coordinate
*
* @return integer
*/
public function x(): 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 y(): 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.php 0000644 00000022121 15073164571 0011275 0 ustar 00 pivot;
}
public function getIterator(): Traversable
{
return new ArrayIterator($this->points);
}
/**
* Return current pivot point
*
* @return PointInterface
*/
public function pivot(): PointInterface
{
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): mixed
{
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;
}
/**
* Calculate total horizontal span of polygon
*
* @return int
*/
public function width(): int
{
return abs($this->mostLeftPoint()->x() - $this->mostRightPoint()->x());
}
/**
* Calculate total vertical span of polygon
*
* @return int
*/
public function height(): int
{
return abs($this->mostBottomPoint()->y() - $this->mostTopPoint()->y());
}
/**
* Return most left point of all points in polygon
*
* @return Point
*/
public function mostLeftPoint(): Point
{
$points = [];
foreach ($this->points as $point) {
$points[] = $point;
}
usort($points, function ($a, $b) {
if ($a->x() === $b->x()) {
return 0;
}
return ($a->x() < $b->x()) ? -1 : 1;
});
return $points[0];
}
/**
* Return most right point in polygon
*
* @return Point
*/
public function mostRightPoint(): Point
{
$points = [];
foreach ($this->points as $point) {
$points[] = $point;
}
usort($points, function ($a, $b) {
if ($a->x() === $b->x()) {
return 0;
}
return ($a->x() > $b->x()) ? -1 : 1;
});
return $points[0];
}
/**
* Return most top point in polygon
*
* @return Point
*/
public function mostTopPoint(): Point
{
$points = [];
foreach ($this->points as $point) {
$points[] = $point;
}
usort($points, function ($a, $b) {
if ($a->y() === $b->y()) {
return 0;
}
return ($a->y() > $b->y()) ? -1 : 1;
});
return $points[0];
}
/**
* Return most bottom point in polygon
*
* @return Point
*/
public function mostBottomPoint(): Point
{
$points = [];
foreach ($this->points as $point) {
$points[] = $point;
}
usort($points, function ($a, $b) {
if ($a->y() === $b->y()) {
return 0;
}
return ($a->y() < $b->y()) ? -1 : 1;
});
return $points[0];
}
/**
* Return point in absolute center of the polygon
*
* @return Point
*/
public function centerPoint(): Point
{
return new Point(
$this->mostRightPoint()->x() - (intval(round($this->width() / 2))),
$this->mostTopPoint()->y() - (intval(round($this->height() / 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->centerPoint()->x() - $this->pivot()->x());
break;
case 'right':
$diff = ($this->mostRightPoint()->x() - $this->pivot()->x());
break;
default:
case 'left':
$diff = ($this->mostLeftPoint()->x() - $this->pivot()->x());
break;
}
foreach ($this->points as $point) {
$point->setX(
intval($point->x() - $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->centerPoint()->y() - $this->pivot()->y());
break;
case 'top':
$diff = ($this->mostTopPoint()->y() - $this->pivot()->y()) - $this->height();
break;
default:
case 'bottom':
$diff = ($this->mostBottomPoint()->y() - $this->pivot()->y()) + $this->height();
break;
}
foreach ($this->points as $point) {
$point->setY(
intval($point->y() - $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(
intval($point->x() - $this->pivot()->x()),
);
$point->setY(
intval($point->y() - $this->pivot()->y()),
);
// rotate point
$x = $point->x() * $cos - $point->y() * $sin;
$y = $point->x() * $sin + $point->y() * $cos;
// translate point back
$point->setX(
intval($x + $this->pivot()->x()),
);
$point->setY(
intval($y + $this->pivot()->y()),
);
}
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->x();
$coordinates[] = $point->y();
}
return $coordinates;
}
}
src/Geometry/Rectangle.php 0000644 00000014214 15073164571 0011556 0 ustar 00 addPoint(new Point($this->pivot->x(), $this->pivot->y()));
$this->addPoint(new Point($this->pivot->x() + $width, $this->pivot->y()));
$this->addPoint(new Point($this->pivot->x() + $width, $this->pivot->y() - $height));
$this->addPoint(new Point($this->pivot->x(), $this->pivot->y() - $height));
}
public function setSize(int $width, int $height): self
{
return $this->setWidth($width)->setHeight($height);
}
public function setWidth(int $width): self
{
$this[1]->setX($this[0]->x() + $width);
$this[2]->setX($this[3]->x() + $width);
return $this;
}
public function setHeight(int $height): self
{
$this[2]->setY($this[1]->y() + $height);
$this[3]->setY($this[0]->y() + $height);
return $this;
}
public function pivot(): PointInterface
{
return $this->pivot;
}
public function setPivot(PointInterface $pivot): self
{
$this->pivot = $pivot;
return $this;
}
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->width() / 2) + $offset_x;
$y = 0 + $offset_y;
break;
case 'top-right':
case 'right-top':
$x = $this->width() - $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->height() / 2) + $offset_y;
break;
case 'right':
case 'right-center':
case 'right-middle':
case 'center-right':
case 'middle-right':
$x = $this->width() - $offset_x;
$y = intval($this->height() / 2) + $offset_y;
break;
case 'bottom-left':
case 'left-bottom':
$x = 0 + $offset_x;
$y = $this->height() - $offset_y;
break;
case 'bottom':
case 'bottom-center':
case 'bottom-middle':
case 'center-bottom':
case 'middle-bottom':
$x = intval($this->width() / 2) + $offset_x;
$y = $this->height() - $offset_y;
break;
case 'bottom-right':
case 'right-bottom':
$x = $this->width() - $offset_x;
$y = $this->height() - $offset_y;
break;
case 'center':
case 'middle':
case 'center-center':
case 'middle-middle':
$x = intval($this->width() / 2) + $offset_x;
$y = intval($this->height() / 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->width(), $size->height());
$reference->movePivot($position);
$this->movePivot($position)->setPivot(
$reference->relativePositionTo($this)
);
return $this;
}
public function relativePositionTo(SizeInterface $rectangle): PointInterface
{
return new Point(
$this->pivot()->x() - $rectangle->pivot()->x(),
$this->pivot()->y() - $rectangle->pivot()->y()
);
}
public function aspectRatio(): float
{
return $this->width() / $this->height();
}
public function fitsInto(SizeInterface $size): bool
{
if ($this->width() > $size->width()) {
return false;
}
if ($this->height() > $size->height()) {
return false;
}
return true;
}
public function isLandscape(): bool
{
return $this->width() > $this->height();
}
public function isPortrait(): bool
{
return $this->width() < $this->height();
}
public function topLeftPoint(): PointInterface
{
return $this->points[0];
}
public function bottomRightPoint(): PointInterface
{
return $this->points[2];
}
protected function resizer(?int $width = null, ?int $height = null): RectangleResizer
{
return new RectangleResizer($width, $height);
}
public function resize(?int $width = null, ?int $height = null): SizeInterface
{
return $this->resizer($width, $height)->resize($this);
}
public function resizeDown(?int $width = null, ?int $height = null): SizeInterface
{
return $this->resizer($width, $height)->resizeDown($this);
}
public function scale(?int $width = null, ?int $height = null): SizeInterface
{
return $this->resizer($width, $height)->scale($this);
}
public function scaleDown(?int $width = null, ?int $height = null): SizeInterface
{
return $this->resizer($width, $height)->scaleDown($this);
}
public function cover(int $width, int $height): SizeInterface
{
return $this->resizer($width, $height)->cover($this);
}
public function contain(int $width, int $height): SizeInterface
{
return $this->resizer($width, $height)->contain($this);
}
}
src/Geometry/Tools/RectangleResizer.php 0000644 00000015271 15073164571 0014226 0 ustar 00 width);
}
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->width();
$this->height = $size->height();
return $this;
}
protected function getProportionalWidth(SizeInterface $size): int
{
if (! $this->hasTargetHeight()) {
return $size->width();
}
return (int) round($this->height * $size->aspectRatio());
}
protected function getProportionalHeight(SizeInterface $size): int
{
if (! $this->hasTargetWidth()) {
return $size->height();
}
return (int) round($this->width / $size->aspectRatio());
}
public function resize(SizeInterface $size): SizeInterface
{
$resized = new Rectangle($size->width(), $size->height());
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->width(), $size->height());
if ($width = $this->getTargetWidth()) {
$resized->setWidth(
min($width, $size->width())
);
}
if ($height = $this->getTargetHeight()) {
$resized->setHeight(
min($height, $size->height())
);
}
return $resized;
}
public function scale(SizeInterface $size): SizeInterface
{
$resized = new Rectangle($size->width(), $size->height());
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->width(), $size->height());
if ($this->hasTargetWidth() && $this->hasTargetHeight()) {
$resized->setWidth(min(
$this->getProportionalWidth($size),
$this->getTargetWidth(),
$size->width()
));
$resized->setHeight(min(
$this->getProportionalHeight($size),
$this->getTargetHeight(),
$size->height()
));
} elseif ($this->hasTargetWidth()) {
$resized->setWidth(min(
$this->getTargetWidth(),
$size->width()
));
$resized->setHeight(min(
$this->getProportionalHeight($size),
$size->height()
));
} elseif ($this->hasTargetHeight()) {
$resized->setWidth(min(
$this->getProportionalWidth($size),
$size->width()
));
$resized->setHeight(min(
$this->getTargetHeight(),
$size->height()
));
}
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->width(), $size->height());
// 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->width(), $size->height());
// 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.php 0000644 00000000724 15073164571 0014633 0 ustar 00 backgroundColor = $color;
return $this;
}
public function backgroundColor(): mixed
{
return $this->backgroundColor;
}
public function hasBackgroundColor(): bool
{
return !empty($this->backgroundColor);
}
}
src/Geometry/Traits/HasBorder.php 0000644 00000001511 15073164571 0012765 0 ustar 00 setBorderSize($size)->setBorderColor($color);
}
public function setBorderSize(int $size): self
{
$this->borderSize = $size;
return $this;
}
public function borderSize(): int
{
return $this->borderSize;
}
public function setBorderColor(mixed $color): self
{
$this->borderColor = $color;
return $this;
}
public function borderColor(): mixed
{
return $this->borderColor;
}
public function hasBorder(): bool
{
return $this->borderSize > 0 && !is_null($this->borderColor);
}
}
src/Image.php 0000644 00000044016 15073164571 0007104 0 ustar 00 driver;
}
/**
* {@inheritdoc}
*
* @see ImageInterface::core()
*/
public function core(): CoreInterface
{
return $this->core;
}
/**
* {@inheritdoc}
*
* @see ImageInterface::count()
*/
public function count(): int
{
return $this->core->count();
}
/**
* Implementation of IteratorAggregate
*
* @return Traversable
*/
public function getIterator(): Traversable
{
return $this->core;
}
/**
* {@inheritdoc}
*
* @see ImageInterface::isAnimated()
*/
public function isAnimated(): bool
{
return $this->count() > 1;
}
/**
* {@inheritdoc}
*
* @see ImageInterface::removeAnimation(
*/
public function removeAnimation(int|string $position = 0): ImageInterface
{
return $this->modify(new RemoveAnimationModifier($position));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::loops()
*/
public function loops(): int
{
return $this->core->loops();
}
/**
* {@inheritdoc}
*
* @see ImageInterface::setLoops()
*/
public function setLoops(int $loops): ImageInterface
{
$this->core->setLoops($loops);
return $this;
}
/**
* {@inheritdoc}
*
* @see ImageInterface::exif()
*/
public function exif(?string $query = null): mixed
{
return is_null($query) ? $this->exif : $this->exif->get($query);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::modify()
*/
public function modify(ModifierInterface $modifier): ImageInterface
{
return $this->driver->resolve($modifier)->apply($this);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::analyze()
*/
public function analyze(AnalyzerInterface $analyzer): mixed
{
return $this->driver->resolve($analyzer)->analyze($this);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::encode()
*/
public function encode(EncoderInterface $encoder): EncodedImage
{
return $this->driver->resolve($encoder)->encode($this);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::width()
*/
public function width(): int
{
return $this->analyze(new WidthAnalyzer());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::height()
*/
public function height(): int
{
return $this->analyze(new HeightAnalyzer());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::size()
*/
public function size(): SizeInterface
{
return new Rectangle($this->width(), $this->height());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::colorspace()
*/
public function colorspace(): ColorspaceInterface
{
return $this->analyze(new ColorspaceAnalyzer());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::setColorspace()
*/
public function setColorspace(string|ColorspaceInterface $colorspace): ImageInterface
{
return $this->modify(new ColorspaceModifier($colorspace));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::resolution()
*/
public function resolution(): ResolutionInterface
{
return $this->analyze(new ResolutionAnalyzer());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::setResolution()
*/
public function setResolution(float $x, float $y): ImageInterface
{
return $this->modify(new ResolutionModifier($x, $y));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::pickColor()
*/
public function pickColor(int $x, int $y, int $frame_key = 0): ColorInterface
{
return $this->analyze(new PixelColorAnalyzer($x, $y, $frame_key));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::pickColors()
*/
public function pickColors(int $x, int $y): CollectionInterface
{
return $this->analyze(new PixelColorsAnalyzer($x, $y));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::profile()
*/
public function profile(): ProfileInterface
{
return $this->analyze(new ProfileAnalyzer());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::setProfile()
*/
public function setProfile(ProfileInterface $profile): ImageInterface
{
return $this->modify(new ProfileModifier($profile));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::removeProfile()
*/
public function removeProfile(): ImageInterface
{
return $this->modify(new ProfileRemovalModifier());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::sharpen()
*/
public function sharpen(int $amount = 10): ImageInterface
{
return $this->modify(new SharpenModifier($amount));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::invert()
*/
public function invert(): ImageInterface
{
return $this->modify(new InvertModifier());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::pixelate()
*/
public function pixelate(int $size): ImageInterface
{
return $this->modify(new PixelateModifier($size));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::greyscale()
*/
public function greyscale(): ImageInterface
{
return $this->modify(new GreyscaleModifier());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::brightness()
*/
public function brightness(int $level): ImageInterface
{
return $this->modify(new BrightnessModifier($level));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::contrast()
*/
public function contrast(int $level): ImageInterface
{
return $this->modify(new ContrastModifier($level));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::gamma()
*/
public function gamma(float $gamma): ImageInterface
{
return $this->modify(new GammaModifier($gamma));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::colorize()
*/
public function colorize(int $red = 0, int $green = 0, int $blue = 0): ImageInterface
{
return $this->modify(new ColorizeModifier($red, $green, $blue));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::flip()
*/
public function flip(): ImageInterface
{
return $this->modify(new FlipModifier());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::flop()
*/
public function flop(): ImageInterface
{
return $this->modify(new FlopModifier());
}
/**
* {@inheritdoc}
*
* @see ImageInterface::blur()
*/
public function blur(int $amount = 5): ImageInterface
{
return $this->modify(new BlurModifier($amount));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::rotate()
*/
public function rotate(float $angle, mixed $background = 'ffffff'): ImageInterface
{
return $this->modify(new RotateModifier($angle, $background));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::text()
*/
public function text(string $text, int $x, int $y, callable|FontInterface $font): ImageInterface
{
return $this->modify(
new TextModifier(
$text,
new Point($x, $y),
call_user_func(new FontFactory($font)),
),
);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::resize()
*/
public function resize(?int $width = null, ?int $height = null): ImageInterface
{
return $this->modify(new ResizeModifier($width, $height));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::resizeDown()
*/
public function resizeDown(?int $width = null, ?int $height = null): ImageInterface
{
return $this->modify(new ResizeDownModifier($width, $height));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::scale()
*/
public function scale(?int $width = null, ?int $height = null): ImageInterface
{
return $this->modify(new ScaleModifier($width, $height));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::scaleDown()
*/
public function scaleDown(?int $width = null, ?int $height = null): ImageInterface
{
return $this->modify(new ScaleDownModifier($width, $height));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::fit()
*/
public function fit(int $width, int $height, string $position = 'center'): ImageInterface
{
return $this->modify(new FitModifier($width, $height, $position));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::fitDown()
*/
public function fitDown(int $width, int $height, string $position = 'center'): ImageInterface
{
return $this->modify(new FitDownModifier($width, $height, $position));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::pad()
*/
public function pad(
int $width,
int $height,
mixed $background = 'ffffff',
string $position = 'center'
): ImageInterface {
return $this->modify(new PadModifier($width, $height, $background, $position));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::padDown()
*/
public function padDown(
int $width,
int $height,
mixed $background = 'ffffff',
string $position = 'center'
): ImageInterface {
return $this->modify(new PadModifier($width, $height, $background, $position));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::crop()
*/
public function crop(
int $width,
int $height,
int $offset_x = 0,
int $offset_y = 0,
string $position = 'top-left'
): ImageInterface {
return $this->modify(new CropModifier($width, $height, $offset_x, $offset_y, $position));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::place()
*/
public function place(
mixed $element,
string $position = 'top-left',
int $offset_x = 0,
int $offset_y = 0
): ImageInterface {
return $this->modify(new PlaceModifier($element, $position, $offset_x, $offset_y));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::fill()
*/
public function fill(mixed $color, ?int $x = null, ?int $y = null): ImageInterface
{
return $this->modify(new FillModifier($color, new Point($x, $y)));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::drawPixel()
*/
public function drawPixel(int $x, int $y, mixed $color): ImageInterface
{
return $this->modify(new DrawPixelModifier(new Point($x, $y), $color));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::drawRectangle()
*/
public function drawRectangle(int $x, int $y, callable|Rectangle $init): ImageInterface
{
return $this->modify(
new DrawRectangleModifier(
call_user_func(new RectangleFactory(new Point($x, $y), $init)),
),
);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::drawEllipse()
*/
public function drawEllipse(int $x, int $y, callable $init): ImageInterface
{
return $this->modify(
new DrawEllipseModifier(
call_user_func(new EllipseFactory(new Point($x, $y), $init)),
),
);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::drawCircle()
*/
public function drawCircle(int $x, int $y, callable $init): ImageInterface
{
return $this->modify(
new DrawEllipseModifier(
call_user_func(new CircleFactory(new Point($x, $y), $init)),
),
);
}
public function drawPolygon(callable $init): ImageInterface
{
return $this->modify(
new DrawPolygonModifier(
call_user_func(new PolygonFactory($init)),
),
);
}
public function drawLine(callable $init): ImageInterface
{
return $this->modify(
new DrawLineModifier(
call_user_func(new LineFactory($init)),
),
);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::toJpeg()
*/
public function toJpeg(int $quality = 75): EncodedImageInterface
{
return $this->encode(new JpegEncoder($quality));
}
/**
* Alias of self::toJpeg()
*
* @param int $quality
* @return EncodedImageInterface
*/
public function toJpg(int $quality = 75): EncodedImageInterface
{
return $this->toJpeg($quality);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::toPng()
*/
public function toPng(int $color_limit = 0): EncodedImageInterface
{
return $this->encode(new PngEncoder($color_limit));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::toGif()
*/
public function toGif(int $color_limit = 0): EncodedImageInterface
{
return $this->encode(new GifEncoder($color_limit));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::toWebp()
*/
public function toWebp(int $quality = 75): EncodedImageInterface
{
return $this->encode(new WebpEncoder($quality));
}
/**
* {@inheritdoc}
*
* @see ImageInterface::toBitmap()
*/
public function toBitmap(int $color_limit = 0): EncodedImageInterface
{
return $this->encode(new BmpEncoder($color_limit));
}
/**
* Alias if self::toBitmap()
*
* @param int $color_limit
* @return EncodedImageInterface
*/
public function toBmp(int $color_limit = 0): EncodedImageInterface
{
return $this->toBitmap($color_limit);
}
/**
* {@inheritdoc}
*
* @see ImageInterface::toAvif()
*/
public function toAvif(int $quality = 75): EncodedImageInterface
{
return $this->encode(new AvifEncoder($quality));
}
}
src/ImageManager.php 0000644 00000004763 15073164571 0010404 0 ustar 00 driver = $this->resolveDriver($driver);
}
/**
* Create image mangager with given driver
*
* @param string|DriverInterface $driver
* @return ImageManager
*/
public static function withDriver(string|DriverInterface $driver): self
{
return new self(self::resolveDriver($driver));
}
/**
* Create image manager with GD driver
*
* @return ImageManager
*/
public static function gd(): self
{
return self::withDriver(GdDriver::class);
}
/**
* Create image manager with Imagick driver
*
* @return ImageManager
*/
public static function imagick(): self
{
return self::withDriver(ImagickDriver::class);
}
/**
* Create new image instance with given width & height
*
* @param int $width
* @param int $height
* @return ImageInterface
*/
public function create(int $width, int $height): ImageInterface
{
return $this->driver->createImage($width, $height);
}
/**
* Create new image instance from given source which can be one of the following
*
* - Path in filesystem
* - File Pointer resource
* - SplFileInfo object
* - Raw binary image data
* - Base64 encoded image data
* - Data Uri
* - Intervention Image Instance
*
* @param mixed $input
* @return ImageInterface
*/
public function read(mixed $input): ImageInterface
{
return $this->driver->handleInput($input);
}
/**
* Create new animated image by given callback
*
* @param callable $init
* @return ImageInterface
*/
public function animate(callable $init): ImageInterface
{
return $this->driver->createAnimation($init);
}
/**
* Return driver object
*
* @param string|DriverInterface $driver
* @return DriverInterface
*/
private static function resolveDriver(string|DriverInterface $driver): DriverInterface
{
if (is_object($driver)) {
return $driver;
}
return new $driver();
}
}
src/Interfaces/AnalyzerInterface.php 0000644 00000000425 15073164571 0013547 0 ustar 00 modifiers as $modifier) {
$modifier->apply($image);
}
return $image;
}
public function push(ModifierInterface $modifier): self
{
$this->modifiers[] = $modifier;
return $this;
}
}
src/Modifiers/AbstractModifier.php 0000644 00000000637 15073164571 0013226 0 ustar 00 modify($this);
}
}
src/Modifiers/BlurModifier.php 0000644 00000000240 15073164571 0012355 0 ustar 00 target)) {
return $this->target;
}
if (in_array($this->target, ['rgb', 'RGB', RgbColorspace::class])) {
return new RgbColorspace();
}
if (in_array($this->target, ['cmyk', 'CMYK', CmykColorspace::class])) {
return new CmykColorspace();
}
throw new NotSupportedException('Given colorspace is not supported.');
}
}
src/Modifiers/ContrastModifier.php 0000644 00000000243 15073164571 0013251 0 ustar 00 width, $this->height);
$crop->align($this->position);
return $crop->alignPivotTo(
$image->size(),
$this->position
);
}
}
src/Modifiers/DrawEllipseModifier.php 0000644 00000000327 15073164571 0013672 0 ustar 00 position);
}
}
src/Modifiers/FitDownModifier.php 0000644 00000000136 15073164571 0013027 0 ustar 00 size();
$crop = new Rectangle($this->width, $this->height);
$crop = $crop->contain(
$imagesize->width(),
$imagesize->height()
)->alignPivotTo($imagesize, $this->position);
return $crop;
}
public function getResizeSize(SizeInterface $size): SizeInterface
{
return $size->scale($this->width, $this->height);
}
}
src/Modifiers/FlipModifier.php 0000644 00000000140 15073164571 0012342 0 ustar 00 getResizeSize($image);
return $image->size()
->contain($resize->width(), $resize->height())
->alignPivotTo($resize, $this->position);
}
public function getResizeSize(ImageInterface $image): SizeInterface
{
return (new Rectangle($this->width, $this->height))
->resizeDown($image->width(), $image->height());
}
}
src/Modifiers/PadModifier.php 0000644 00000001453 15073164571 0012164 0 ustar 00 size()
->contain($this->width, $this->height)
->alignPivotTo($this->getResizeSize($image), $this->position);
}
public function getResizeSize(ImageInterface $image): SizeInterface
{
return new Rectangle($this->width, $this->height);
}
}
src/Modifiers/PixelateModifier.php 0000644 00000000242 15073164571 0013226 0 ustar 00 size()->movePivot(
$this->position,
$this->offset_x,
$this->offset_y
);
$watermark_size = $watermark->size()->movePivot(
$this->position
);
return $image_size->relativePositionTo($watermark_size);
}
}
src/Modifiers/ProfileModifier.php 0000644 00000000346 15073164571 0013060 0 ustar 00 core()->frame($position);
}
if (preg_match("/^(?P[0-9]{1,3})%$/", $position, $matches) != 1) {
throw new InputException(
'Position must be either integer or a percent value as string.'
);
}
$total = count($image);
$position = intval(round($total / 100 * intval($matches['percent'])));
$position = $position == $total ? $position - 1 : $position;
return $image->core()->frame($position);
}
}
src/Modifiers/ResizeDownModifier.php 0000644 00000000144 15073164571 0013545 0 ustar 00 angle, 360);
}
}
src/Modifiers/ScaleDownModifier.php 0000644 00000000142 15073164571 0013331 0 ustar 00 x;
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::setX()
*/
public function setX(float $x): self
{
$this->x = $x;
return $this;
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::y()
*/
public function y(): float
{
return $this->y;
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::setY()
*/
public function setY(float $y): self
{
$this->y = $y;
return $this;
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::setPerUnit()
*/
protected function setPerUnit(int $per_unit): self
{
$this->per_unit = $per_unit;
return $this;
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::unit()
*/
public function unit(): string
{
return match ($this->per_unit) {
self::PER_CM => 'dpcm',
default => 'dpi',
};
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::perInch()
*/
public function perInch(): self
{
return match ($this->per_unit) {
self::PER_CM => $this
->setPerUnit(self::PER_INCH)
->setX($this->x * (1 / 2.54))
->setY($this->y * (1 / 2.54)),
default => $this
};
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::perCm()
*/
public function perCm(): self
{
return match ($this->per_unit) {
self::PER_INCH => $this
->setPerUnit(self::PER_CM)
->setX($this->x / (1 / 2.54))
->setY($this->y / (1 / 2.54)),
default => $this,
};
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::toString()
*/
public function toString(): string
{
return sprintf("%1\$.2f x %2\$.2f %3\$s", $this->x, $this->y, $this->unit());
}
/**
* {@inheritdoc}
*
* @see ResolutionInterface::__toString()
*/
public function __toString(): string
{
return $this->toString();
}
}
src/Traits/CanBuildFilePointer.php 0000644 00000000414 15073164571 0013144 0 ustar 00 filename = $filename;
}
/**
* {@inheritdoc}
*
* @see FontInterface::setSize()
*/
public function setSize(float $size): FontInterface
{
$this->size = $size;
return $this;
}
/**
* {@inheritdoc}
*
* @see FontInterface::size()
*/
public function size(): float
{
return $this->size;
}
/**
* {@inheritdoc}
*
* @see FontInterface::setAngle()
*/
public function setAngle(float $angle): FontInterface
{
$this->angle = $angle;
return $this;
}
/**
* {@inheritdoc}
*
* @see FontInterface::angle()
*/
public function angle(): float
{
return $this->angle;
}
/**
* {@inheritdoc}
*
* @see FontInterface::setFilename()
*/
public function setFilename(string $filename): FontInterface
{
$this->filename = $filename;
return $this;
}
/**
* {@inheritdoc}
*
* @see FontInterface::filename()
*/
public function filename(): ?string
{
return $this->filename;
}
/**
* {@inheritdoc}
*
* @see FontInterface::hasFilename()
*/
public function hasFilename(): bool
{
return !is_null($this->filename) && is_file($this->filename);
}
/**
* {@inheritdoc}
*
* @see FontInterface::setColor()
*/
public function setColor(mixed $color): FontInterface
{
$this->color = $color;
return $this;
}
/**
* {@inheritdoc}
*
* @see FontInterface::color()
*/
public function color(): mixed
{
return $this->color;
}
/**
* {@inheritdoc}
*
* @see FontInterface::alignment()
*/
public function alignment(): string
{
return $this->alignment;
}
/**
* {@inheritdoc}
*
* @see FontInterface::setAlignment()
*/
public function setAlignment(string $value): FontInterface
{
$this->alignment = $value;
return $this;
}
/**
* {@inheritdoc}
*
* @see FontInterface::valignment()
*/
public function valignment(): string
{
return $this->valignment;
}
/**
* {@inheritdoc}
*
* @see FontInterface::setValignment()
*/
public function setValignment(string $value): FontInterface
{
$this->valignment = $value;
return $this;
}
/**
* {@inheritdoc}
*
* @see FontInterface::setLineHeight()
*/
public function setLineHeight(float $height): FontInterface
{
$this->lineHeight = $height;
return $this;
}
/**
* {@inheritdoc}
*
* @see FontInterface::lineHeight()
*/
public function lineHeight(): float
{
return $this->lineHeight;
}
}
src/Typography/FontFactory.php 0000644 00000002475 15073164571 0012471 0 ustar 00 font = is_a($init, FontInterface::class) ? $init : new Font();
if (is_callable($init)) {
$init($this);
}
}
public function __invoke(): FontInterface
{
return $this->font;
}
public function filename(string $value): self
{
$this->font->setFilename($value);
return $this;
}
public function color(mixed $value): self
{
$this->font->setColor($value);
return $this;
}
public function size(float $value): self
{
$this->font->setSize($value);
return $this;
}
public function align(string $value): self
{
$this->font->setAlignment($value);
return $this;
}
public function valign(string $value): self
{
$this->font->setValignment($value);
return $this;
}
public function lineHeight(float $value): self
{
$this->font->setLineHeight($value);
return $this;
}
public function angle(float $value): self
{
$this->font->setAngle($value);
return $this;
}
}
src/Typography/Line.php 0000644 00000001112 15073164571 0011105 0 ustar 00 position;
}
public function setPosition(Point $point): self
{
$this->position = $point;
return $this;
}
public function __toString(): string
{
return $this->text;
}
}
src/Typography/TextBlock.php 0000644 00000002023 15073164571 0012117 0 ustar 00 push(new Line($line));
}
}
/**
* Return array of lines in text block
*
* @return array
*/
public function lines(): array
{
return $this->items;
}
public function line($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];
}
}