refactor: susun semula struktur folder — Laravel source ke src/
This commit is contained in:
23
vendor/intervention/image/src/Drivers/Gd/Analyzers/ColorspaceAnalyzer.php
vendored
Normal file
23
vendor/intervention/image/src/Drivers/Gd/Analyzers/ColorspaceAnalyzer.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Analyzers;
|
||||
|
||||
use Intervention\Image\Analyzers\ColorspaceAnalyzer as GenericColorspaceAnalyzer;
|
||||
use Intervention\Image\Colors\Rgb\Colorspace;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class ColorspaceAnalyzer extends GenericColorspaceAnalyzer implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see AnalyzerInterface::analyze()
|
||||
*/
|
||||
public function analyze(ImageInterface $image): mixed
|
||||
{
|
||||
return new Colorspace();
|
||||
}
|
||||
}
|
||||
22
vendor/intervention/image/src/Drivers/Gd/Analyzers/HeightAnalyzer.php
vendored
Normal file
22
vendor/intervention/image/src/Drivers/Gd/Analyzers/HeightAnalyzer.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Analyzers;
|
||||
|
||||
use Intervention\Image\Analyzers\HeightAnalyzer as GenericHeightAnalyzer;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class HeightAnalyzer extends GenericHeightAnalyzer implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see AnalyzerInterface::analyze()
|
||||
*/
|
||||
public function analyze(ImageInterface $image): mixed
|
||||
{
|
||||
return imagesy($image->core()->native());
|
||||
}
|
||||
}
|
||||
61
vendor/intervention/image/src/Drivers/Gd/Analyzers/PixelColorAnalyzer.php
vendored
Normal file
61
vendor/intervention/image/src/Drivers/Gd/Analyzers/PixelColorAnalyzer.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Analyzers;
|
||||
|
||||
use Intervention\Image\Analyzers\PixelColorAnalyzer as GenericPixelColorAnalyzer;
|
||||
use Intervention\Image\Exceptions\AnalyzerException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ColorInterface;
|
||||
use Intervention\Image\Interfaces\ColorProcessorInterface;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use ValueError;
|
||||
|
||||
class PixelColorAnalyzer extends GenericPixelColorAnalyzer implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see AnalyzerInterface::analyze()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws AnalyzerException
|
||||
* @throws StateException
|
||||
*/
|
||||
public function analyze(ImageInterface $image): mixed
|
||||
{
|
||||
$colorProcessor = $this->driver()->colorProcessor($image);
|
||||
|
||||
return $this->colorAt($colorProcessor, $image->core()->frame($this->frame));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws AnalyzerException
|
||||
*/
|
||||
protected function colorAt(ColorProcessorInterface $processor, FrameInterface $frame): ColorInterface
|
||||
{
|
||||
$gd = $frame->native();
|
||||
$index = @imagecolorat($gd, $this->x, $this->y);
|
||||
|
||||
if (!is_int($index)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The specified position (' . $this->x . ', ' . $this->y . ') is not within the image area',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$index = imagecolorsforindex($gd, $index);
|
||||
} catch (ValueError) {
|
||||
throw new AnalyzerException(
|
||||
'The specified index is outside of the range',
|
||||
);
|
||||
}
|
||||
|
||||
return $processor->import($index);
|
||||
}
|
||||
}
|
||||
30
vendor/intervention/image/src/Drivers/Gd/Analyzers/PixelColorsAnalyzer.php
vendored
Normal file
30
vendor/intervention/image/src/Drivers/Gd/Analyzers/PixelColorsAnalyzer.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Analyzers;
|
||||
|
||||
use Intervention\Image\Collection;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
class PixelColorsAnalyzer extends PixelColorAnalyzer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see AnalyzerInterface::analyze()
|
||||
*/
|
||||
public function analyze(ImageInterface $image): mixed
|
||||
{
|
||||
$colors = new Collection();
|
||||
$colorProcessor = $this->driver()->colorProcessor($image);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$colors->push(
|
||||
parent::colorAt($colorProcessor, $frame)
|
||||
);
|
||||
}
|
||||
|
||||
return $colors;
|
||||
}
|
||||
}
|
||||
219
vendor/intervention/image/src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php
vendored
Normal file
219
vendor/intervention/image/src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Analyzers;
|
||||
|
||||
use Intervention\Image\Analyzers\ResolutionAnalyzer as GenericResolutionAnalyzer;
|
||||
use Intervention\Image\Exceptions\AnalyzerException;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\MissingDependencyException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\OriginInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Resolution;
|
||||
use Intervention\Image\Traits\CanBuildStream;
|
||||
use Throwable;
|
||||
|
||||
class ResolutionAnalyzer extends GenericResolutionAnalyzer implements SpecializedInterface
|
||||
{
|
||||
use CanBuildStream;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see AnalyzerInterface::analyze()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws AnalyzerException
|
||||
*/
|
||||
public function analyze(ImageInterface $image): mixed
|
||||
{
|
||||
$result = imageresolution($image->core()->native());
|
||||
|
||||
if (!is_array($result)) {
|
||||
throw new AnalyzerException('Failed to read image resolution');
|
||||
}
|
||||
|
||||
// GD returns 96x96 as resolution by default even if the image has no resolution.
|
||||
// This is problematic because it is impossible to tell whether the image
|
||||
// really has this resolution or whether it just corresponds to the default value.
|
||||
//
|
||||
// If GD's default resolution is returned here and the resolution is still unchanged
|
||||
// we will make an attempt to find the resolution from origin.
|
||||
if ($this->isGdDefaultResolution($result) && $image->core()->meta()->get('resolutionChanged') !== true) {
|
||||
try {
|
||||
$alternativeResoltion = $this->readResolutionFromOrigin($image->origin());
|
||||
} catch (Throwable) {
|
||||
$alternativeResoltion = [96, 96];
|
||||
}
|
||||
|
||||
$result = $alternativeResoltion !== $result ? $alternativeResoltion : $result;
|
||||
}
|
||||
|
||||
return new Resolution(...$result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|float> $resolution
|
||||
*/
|
||||
private function isGdDefaultResolution(array $resolution): bool
|
||||
{
|
||||
return intval($resolution[0] ?? 0) === 96 && intval($resolution[1] ?? 0) === 96;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AnalyzerException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
* @return array<float>
|
||||
*/
|
||||
private function readResolutionFromOrigin(OriginInterface $origin): array
|
||||
{
|
||||
$handle = self::buildStreamOrFail(file_get_contents($origin->filePath()));
|
||||
|
||||
try {
|
||||
try {
|
||||
return $this->resolutionFromJfifHeader($handle);
|
||||
} catch (Throwable) {
|
||||
# code ...
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->resolutionFromExifHeader($handle);
|
||||
} catch (Throwable) {
|
||||
# code ...
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->resolutionFromPngPhys($handle);
|
||||
} catch (Throwable) {
|
||||
# code ...
|
||||
}
|
||||
|
||||
throw new AnalyzerException('Unable to read resolution from path');
|
||||
} finally {
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @throws AnalyzerException
|
||||
* @return array<float>
|
||||
*/
|
||||
private function resolutionFromJfifHeader($handle): array
|
||||
{
|
||||
// read first 20 bytes
|
||||
rewind($handle);
|
||||
$header = fread($handle, 20);
|
||||
|
||||
// find the JFIF segment
|
||||
$offset = strpos($header, 'JFIF');
|
||||
if ($offset === false) {
|
||||
throw new AnalyzerException('Unable to read JFIF header');
|
||||
}
|
||||
|
||||
// read bytes at known offsets relative to JFIF
|
||||
$units = ord($header[$offset + 7]);
|
||||
$x = unpack('n', substr($header, $offset + 8, 2))[1];
|
||||
$y = unpack('n', substr($header, $offset + 10, 2))[1];
|
||||
|
||||
if ($units === 2) { // unit is dots per cm → convert to DPI
|
||||
return [round($x * 2.54), round($y * 2.54)];
|
||||
}
|
||||
|
||||
return [$x, $y]; // unit is DPI or no unit
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @throws MissingDependencyException
|
||||
* @throws AnalyzerException
|
||||
* @return array<float>
|
||||
*/
|
||||
private function resolutionFromExifHeader($handle): array
|
||||
{
|
||||
if (!function_exists('exif_read_data')) {
|
||||
throw new MissingDependencyException('Unable to read exif data');
|
||||
}
|
||||
|
||||
rewind($handle);
|
||||
$data = @exif_read_data($handle, null, true);
|
||||
|
||||
if ($data === false) {
|
||||
throw new AnalyzerException('Unable to read exif data');
|
||||
}
|
||||
|
||||
if (isset($data['XResolution']) && isset($data['YResolution'])) {
|
||||
$resolution = [$data['XResolution'], $data['YResolution']];
|
||||
}
|
||||
|
||||
if (isset($data['IFD0']) && isset($data['IFD0']['XResolution']) && isset($data['IFD0']['YResolution'])) {
|
||||
$resolution = [$data['IFD0']['XResolution'], $data['IFD0']['YResolution']];
|
||||
}
|
||||
|
||||
if (!isset($resolution)) {
|
||||
throw new AnalyzerException('Unable to read exif data');
|
||||
}
|
||||
|
||||
return array_map(function (mixed $value): int|float {
|
||||
if (strpos($value, '/') === false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$values = array_map(fn(string $value): int => intval($value), explode('/', $value));
|
||||
|
||||
if ($values[1] === 0) {
|
||||
throw new AnalyzerException('Unable to read exif data, division by zero');
|
||||
}
|
||||
|
||||
return $values[0] / $values[1];
|
||||
}, $resolution);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @throws AnalyzerException
|
||||
* @return array<float>
|
||||
*/
|
||||
private function resolutionFromPngPhys($handle): array
|
||||
{
|
||||
rewind($handle);
|
||||
$signature = fread($handle, 8);
|
||||
|
||||
// no PNG content
|
||||
if ($signature !== "\x89PNG\x0D\x0A\x1A\x0A") {
|
||||
throw new AnalyzerException('Input must be PNG format');
|
||||
}
|
||||
|
||||
$marker = '';
|
||||
|
||||
while (!feof($handle)) {
|
||||
$marker = strlen($marker) < 4 ? $marker . fread($handle, 1) : substr($marker, 1) . fread($handle, 1);
|
||||
|
||||
// find pHYs chunk
|
||||
if ($marker === 'pHYs') {
|
||||
// find length
|
||||
fseek($handle, -8, SEEK_CUR);
|
||||
$length = fread($handle, 4);
|
||||
$length = unpack('N', $length)[1];
|
||||
fseek($handle, 4, SEEK_CUR);
|
||||
|
||||
// read data
|
||||
$data = fread($handle, $length);
|
||||
|
||||
$x = unpack('N', substr($data, 0, 4))[1];
|
||||
$y = unpack('N', substr($data, 4, 4))[1];
|
||||
|
||||
return [
|
||||
round($x * .0254),
|
||||
round($y * .0254),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [0, 0];
|
||||
}
|
||||
}
|
||||
22
vendor/intervention/image/src/Drivers/Gd/Analyzers/WidthAnalyzer.php
vendored
Normal file
22
vendor/intervention/image/src/Drivers/Gd/Analyzers/WidthAnalyzer.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Analyzers;
|
||||
|
||||
use Intervention\Image\Analyzers\WidthAnalyzer as GenericWidthAnalyzer;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class WidthAnalyzer extends GenericWidthAnalyzer implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see AnalyzerInterface::analyze()
|
||||
*/
|
||||
public function analyze(ImageInterface $image): mixed
|
||||
{
|
||||
return imagesx($image->core()->native());
|
||||
}
|
||||
}
|
||||
100
vendor/intervention/image/src/Drivers/Gd/Cloner.php
vendored
Normal file
100
vendor/intervention/image/src/Drivers/Gd/Cloner.php
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd;
|
||||
|
||||
use GdImage;
|
||||
use Intervention\Image\Colors\Rgb\Color;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
use Intervention\Image\Size;
|
||||
|
||||
class Cloner
|
||||
{
|
||||
/**
|
||||
* Create a clone of the given GdImage
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public static function clone(GdImage $gd): GdImage
|
||||
{
|
||||
// create empty canvas with same size
|
||||
$clone = static::cloneEmpty($gd);
|
||||
|
||||
// transfer actual image to clone
|
||||
imagecopy($clone, $gd, 0, 0, 0, 0, imagesx($gd), imagesy($gd));
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an "empty" clone of the given GdImage
|
||||
*
|
||||
* This only retains the basic data without transferring the actual image.
|
||||
* It is optionally possible to change the size of the result and set a
|
||||
* background color.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public static function cloneEmpty(
|
||||
GdImage $gd,
|
||||
?SizeInterface $size = null,
|
||||
Color $background = new Color(255, 255, 255, 0)
|
||||
): GdImage {
|
||||
// define size
|
||||
$size = $size ?: new Size(imagesx($gd), imagesy($gd));
|
||||
|
||||
if ($size->width() < 1 || $size->height() < 1) {
|
||||
throw new InvalidArgumentException('Invalid image size');
|
||||
}
|
||||
|
||||
// create new gd image with same size or new given size
|
||||
$clone = imagecreatetruecolor($size->width(), $size->height());
|
||||
if ($clone === false) {
|
||||
throw new DriverException('Failed to create new image while cloning');
|
||||
}
|
||||
|
||||
// copy resolution to clone
|
||||
$resolution = imageresolution($gd);
|
||||
if (is_array($resolution) && array_key_exists(0, $resolution) && array_key_exists(1, $resolution)) {
|
||||
imageresolution($clone, $resolution[0], $resolution[1]);
|
||||
}
|
||||
|
||||
// fill with background
|
||||
$processor = new ColorProcessor();
|
||||
|
||||
imagefill($clone, 0, 0, $processor->export($background));
|
||||
imagealphablending($clone, true);
|
||||
imagesavealpha($clone, true);
|
||||
|
||||
// set background image as transparent if alpha channel value if color is below .5
|
||||
// comes into effect when the end format only supports binary transparency (like GIF)
|
||||
if ($background->alpha()->value() < .5) {
|
||||
imagecolortransparent($clone, $processor->export($background));
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of an GdImage that is positioned on the specified background color.
|
||||
* Possible transparent areas are mixed with this color.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public static function cloneBlended(GdImage $gd, Color $background): GdImage
|
||||
{
|
||||
// create empty canvas with same size
|
||||
$clone = static::cloneEmpty($gd, background: $background);
|
||||
|
||||
// transfer actual image to clone
|
||||
imagecopy($clone, $gd, 0, 0, 0, 0, imagesx($gd), imagesy($gd));
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
156
vendor/intervention/image/src/Drivers/Gd/ColorProcessor.php
vendored
Normal file
156
vendor/intervention/image/src/Drivers/Gd/ColorProcessor.php
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd;
|
||||
|
||||
use Intervention\Image\Colors\Rgb\Channels\Alpha;
|
||||
use Intervention\Image\Colors\Rgb\Channels\Blue;
|
||||
use Intervention\Image\Colors\Rgb\Channels\Green;
|
||||
use Intervention\Image\Colors\Rgb\Channels\Red;
|
||||
use Intervention\Image\Colors\Rgb\Color;
|
||||
use Intervention\Image\Colors\Rgb\Colorspace as Rgb;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\RuntimeException;
|
||||
use Intervention\Image\Interfaces\ColorInterface;
|
||||
use Intervention\Image\Interfaces\ColorProcessorInterface;
|
||||
use Intervention\Image\Interfaces\ColorspaceInterface;
|
||||
use Intervention\Image\Traits\CanConvertRange;
|
||||
|
||||
class ColorProcessor implements ColorProcessorInterface
|
||||
{
|
||||
use CanConvertRange;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ColorProcessorInterface::colorspace()
|
||||
*/
|
||||
public function colorspace(): ColorspaceInterface
|
||||
{
|
||||
return new Rgb();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ColorProcessorInterface::export()
|
||||
*
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function export(ColorInterface $color): int
|
||||
{
|
||||
// convert color to colorspace
|
||||
$color = $color->toColorspace($this->colorspace());
|
||||
|
||||
// gd only supports rgb so the channels can be accessed directly
|
||||
$r = $color->channel(Red::class)->value();
|
||||
$g = $color->channel(Green::class)->value();
|
||||
$b = $color->channel(Blue::class)->value();
|
||||
$a = $color->channel(Alpha::class)->value();
|
||||
|
||||
try {
|
||||
// convert alpha value to gd alpha
|
||||
// ([opaque]1-0[transparent]) to ([opaque]0-127[transparent])
|
||||
$a = (int) round(self::convertRange($a, Alpha::min(), Alpha::max(), 127, 0));
|
||||
} catch (RuntimeException $e) {
|
||||
throw new DriverException('Failed to export color', previous: $e);
|
||||
}
|
||||
|
||||
return ($a << 24) + ($r << 16) + ($g << 8) + $b;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ColorProcessorInterface::import()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function import(mixed $color): ColorInterface
|
||||
{
|
||||
if (!is_int($color) && !is_array($color)) {
|
||||
throw new InvalidArgumentException('GD driver can only decode colors in integer or array format');
|
||||
}
|
||||
|
||||
if (is_array($color)) {
|
||||
// array conversion
|
||||
if (!$this->isValidArrayColor($color)) {
|
||||
throw new InvalidArgumentException(
|
||||
'GD driver can only decode array color format array{red: int, green: int, blue: int, alpha: int}',
|
||||
);
|
||||
}
|
||||
|
||||
$r = $color['red'];
|
||||
$g = $color['green'];
|
||||
$b = $color['blue'];
|
||||
$a = $color['alpha'];
|
||||
} else {
|
||||
// integer conversion
|
||||
$a = ($color >> 24) & 0xFF;
|
||||
$r = ($color >> 16) & 0xFF;
|
||||
$g = ($color >> 8) & 0xFF;
|
||||
$b = $color & 0xFF;
|
||||
}
|
||||
|
||||
try {
|
||||
// convert gd apha integer to intervention alpha integer
|
||||
// ([opaque]0-127[transparent]) to ([opaque]1-0[transparent])
|
||||
$a = self::convertRange($a, 127, 0, 0, 1);
|
||||
} catch (RuntimeException $e) {
|
||||
throw new DriverException('Failed to import color', previous: $e);
|
||||
}
|
||||
|
||||
try {
|
||||
return new Color($r, $g, $b, $a);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DriverException('Failed to import color', previous: $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given array is valid color format
|
||||
* array{red: int, green: int, blue: int, alpha: int}
|
||||
* i.e. result of imagecolorsforindex()
|
||||
*
|
||||
* @param array<mixed> $color
|
||||
*/
|
||||
private function isValidArrayColor(array $color): bool
|
||||
{
|
||||
if (!array_key_exists('red', $color)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!array_key_exists('green', $color)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!array_key_exists('blue', $color)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!array_key_exists('alpha', $color)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_int($color['red'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_int($color['green'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_int($color['blue'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_int($color['alpha'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
145
vendor/intervention/image/src/Drivers/Gd/Core.php
vendored
Normal file
145
vendor/intervention/image/src/Drivers/Gd/Core.php
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd;
|
||||
|
||||
use Intervention\Image\Collection;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\CollectionInterface;
|
||||
use Intervention\Image\Interfaces\CoreInterface;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
|
||||
class Core extends Collection implements CoreInterface
|
||||
{
|
||||
protected int $loops = 0;
|
||||
protected CollectionInterface $meta;
|
||||
|
||||
/**
|
||||
* Create new core
|
||||
*
|
||||
* @param array<int|string, FrameInterface> $items
|
||||
*/
|
||||
public function __construct(array $items = [])
|
||||
{
|
||||
parent::__construct($items);
|
||||
|
||||
$this->meta = new Collection();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CoreInterface::add()
|
||||
*/
|
||||
public function add(FrameInterface $frame): CoreInterface
|
||||
{
|
||||
$this->push($frame);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CoreInterface::native()
|
||||
*/
|
||||
public function native(): mixed
|
||||
{
|
||||
return $this->first()->native();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CoreInterface::setNative()
|
||||
*/
|
||||
public function setNative(mixed $native): CoreInterface
|
||||
{
|
||||
$this->clear()->push(new Frame($native));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CoreInterface::frame()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function frame(int $position): FrameInterface
|
||||
{
|
||||
$frame = $this->at($position);
|
||||
|
||||
if ($frame === null || $position < 0 || $position > $this->count()) {
|
||||
throw new InvalidArgumentException('Frame #' . $position . ' could not be found in the image');
|
||||
}
|
||||
|
||||
return $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CoreInterface::loops()
|
||||
*/
|
||||
public function loops(): int
|
||||
{
|
||||
return $this->loops;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CoreInterface::setLoops()
|
||||
*/
|
||||
public function setLoops(int $loops): CoreInterface
|
||||
{
|
||||
$this->loops = $loops;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CollectionInterface::first()
|
||||
*/
|
||||
public function first(): FrameInterface
|
||||
{
|
||||
return parent::first();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CollectionInterface::last()
|
||||
*/
|
||||
public function last(): FrameInterface
|
||||
{
|
||||
return parent::last();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see CoreInterface::meta()
|
||||
*/
|
||||
public function meta(): CollectionInterface
|
||||
{
|
||||
return $this->meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone instance
|
||||
*/
|
||||
public function __clone(): void
|
||||
{
|
||||
$this->meta = clone $this->meta;
|
||||
|
||||
foreach ($this->items as $key => $frame) {
|
||||
$this->items[$key] = clone $frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
93
vendor/intervention/image/src/Drivers/Gd/Decoders/AbstractDecoder.php
vendored
Normal file
93
vendor/intervention/image/src/Drivers/Gd/Decoders/AbstractDecoder.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use Intervention\Image\Drivers\SpecializableDecoder;
|
||||
use Intervention\Image\Exceptions\DirectoryNotFoundException;
|
||||
use Intervention\Image\Exceptions\FileNotFoundException;
|
||||
use Intervention\Image\Exceptions\FileNotReadableException;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\MediaType;
|
||||
use Intervention\Image\Traits\CanParseFilePath;
|
||||
use TypeError;
|
||||
use ValueError;
|
||||
|
||||
abstract class AbstractDecoder extends SpecializableDecoder implements SpecializedInterface
|
||||
{
|
||||
use CanParseFilePath;
|
||||
|
||||
/**
|
||||
* Return media (mime) type of the file at given file path
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ImageDecoderException
|
||||
* @throws NotSupportedException
|
||||
* @throws DirectoryNotFoundException
|
||||
* @throws FileNotFoundException
|
||||
* @throws FileNotReadableException
|
||||
*/
|
||||
protected function mediaTypeByFilePath(string $filepath): MediaType
|
||||
{
|
||||
$filepath = self::readableFilePathOrFail($filepath);
|
||||
|
||||
if (function_exists('finfo_file') && function_exists('finfo_open')) {
|
||||
$mediaType = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $filepath);
|
||||
if (is_string($mediaType)) {
|
||||
try {
|
||||
return MediaType::from($mediaType);
|
||||
} catch (ValueError | TypeError) {
|
||||
throw new NotSupportedException('Unsupported media type (MIME) ' . $mediaType . '.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$info = @getimagesize($filepath);
|
||||
|
||||
if (!is_array($info)) {
|
||||
throw new ImageDecoderException('Failed to read media (MIME) type from data in file path');
|
||||
}
|
||||
|
||||
try {
|
||||
return MediaType::from($info['mime']);
|
||||
} catch (ValueError | TypeError) {
|
||||
throw new NotSupportedException('Unsupported media type (MIME) ' . $info['mime'] . '.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return media (mime) type of the given image data
|
||||
*
|
||||
* @throws ImageDecoderException
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
protected function mediaTypeByBinary(string $data): MediaType
|
||||
{
|
||||
if (function_exists('finfo_buffer') && function_exists('finfo_open')) {
|
||||
$mediaType = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
|
||||
if (is_string($mediaType)) {
|
||||
try {
|
||||
return MediaType::from($mediaType);
|
||||
} catch (ValueError | TypeError) {
|
||||
throw new NotSupportedException('Unsupported media type (MIME) ' . $mediaType . '.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$info = @getimagesizefromstring($data);
|
||||
|
||||
if (!is_array($info)) {
|
||||
throw new ImageDecoderException('Failed to read media (MIME) type from binary data');
|
||||
}
|
||||
|
||||
try {
|
||||
return MediaType::from($info['mime']);
|
||||
} catch (ValueError | TypeError) {
|
||||
throw new NotSupportedException('Unsupported media type (MIME) ' . $info['mime'] . '.');
|
||||
}
|
||||
}
|
||||
}
|
||||
46
vendor/intervention/image/src/Drivers/Gd/Decoders/Base64ImageDecoder.php
vendored
Normal file
46
vendor/intervention/image/src/Drivers/Gd/Decoders/Base64ImageDecoder.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Interfaces\DecoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Traits\CanDetectImageSources;
|
||||
|
||||
class Base64ImageDecoder extends BinaryImageDecoder implements DecoderInterface
|
||||
{
|
||||
use CanDetectImageSources;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::supports()
|
||||
*/
|
||||
public function supports(mixed $input): bool
|
||||
{
|
||||
return $this->couldBeBase64Data($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::decode()
|
||||
*/
|
||||
public function decode(mixed $input): ImageInterface
|
||||
{
|
||||
try {
|
||||
$data = $this->decodeBase64Data($input);
|
||||
} catch (DecoderException) {
|
||||
throw new ImageDecoderException('Unable to Base64-decode image from string');
|
||||
}
|
||||
|
||||
try {
|
||||
return parent::decode($data);
|
||||
} catch (DecoderException) {
|
||||
throw new ImageDecoderException('Base64-encoded data contains unsupported image type');
|
||||
}
|
||||
}
|
||||
}
|
||||
99
vendor/intervention/image/src/Drivers/Gd/Decoders/BinaryImageDecoder.php
vendored
Normal file
99
vendor/intervention/image/src/Drivers/Gd/Decoders/BinaryImageDecoder.php
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Interfaces\DecoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Format;
|
||||
use Intervention\Image\Modifiers\OrientModifier;
|
||||
use Intervention\Image\Traits\CanDetectImageSources;
|
||||
use Stringable;
|
||||
|
||||
class BinaryImageDecoder extends NativeObjectDecoder implements DecoderInterface
|
||||
{
|
||||
use CanDetectImageSources;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::supports()
|
||||
*/
|
||||
public function supports(mixed $input): bool
|
||||
{
|
||||
return $this->couldBeBinaryData($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::decode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ImageDecoderException
|
||||
* @throws DriverException
|
||||
* @throws StateException
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
public function decode(mixed $input): ImageInterface
|
||||
{
|
||||
if (!is_string($input) && !$input instanceof Stringable) {
|
||||
throw new InvalidArgumentException(
|
||||
'Image source must be binary data of type string or instance of ' . Stringable::class,
|
||||
);
|
||||
}
|
||||
|
||||
$input = (string) $input;
|
||||
|
||||
if ($input === '') {
|
||||
throw new InvalidArgumentException('Unable to decode binary data from empty string');
|
||||
}
|
||||
|
||||
return $this->isGifFormat($input) ? $this->decodeGif($input) : $this->decodeBinary($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode image from given binary data
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ImageDecoderException
|
||||
* @throws DriverException
|
||||
* @throws StateException
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
private function decodeBinary(string $input): ImageInterface
|
||||
{
|
||||
$gd = @imagecreatefromstring($input);
|
||||
|
||||
if ($gd === false) {
|
||||
throw new ImageDecoderException('Failed to decode unsupported image format from binary data');
|
||||
}
|
||||
|
||||
// create image instance
|
||||
$image = parent::decode($gd);
|
||||
|
||||
// get media type
|
||||
$mediaType = $this->mediaTypeByBinary($input);
|
||||
|
||||
// extract & set exif data for appropriate formats
|
||||
if (in_array($mediaType->format(), [Format::JPEG, Format::TIFF])) {
|
||||
$image->setExif($this->extractExifData($input));
|
||||
}
|
||||
|
||||
// set mediaType on origin
|
||||
$image->origin()->setMediaType($mediaType);
|
||||
|
||||
// adjust image orientation
|
||||
if ($this->driver()->config()->autoOrientation) {
|
||||
$image->modify(new OrientModifier());
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
65
vendor/intervention/image/src/Drivers/Gd/Decoders/DataUriImageDecoder.php
vendored
Normal file
65
vendor/intervention/image/src/Drivers/Gd/Decoders/DataUriImageDecoder.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use Intervention\Image\DataUri;
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\DecoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Traits\CanDetectImageSources;
|
||||
|
||||
class DataUriImageDecoder extends BinaryImageDecoder implements DecoderInterface
|
||||
{
|
||||
use CanDetectImageSources;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::supports()
|
||||
*/
|
||||
public function supports(mixed $input): bool
|
||||
{
|
||||
return $this->couldBeDataUrl($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::decode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
* @throws ImageDecoderException
|
||||
* @throws StateException
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
public function decode(mixed $input): ImageInterface
|
||||
{
|
||||
if ($input instanceof DataUri) {
|
||||
try {
|
||||
return parent::decode($input->data());
|
||||
} catch (DecoderException) {
|
||||
throw new ImageDecoderException('Data Uri contains unsupported image type');
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_string($input)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Image source must be data uri scheme of type string or ' . DataUri::class,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return parent::decode(DataUri::parse($input)->data());
|
||||
} catch (DecoderException) {
|
||||
throw new ImageDecoderException('Data Uri contains unsupported image type');
|
||||
}
|
||||
}
|
||||
}
|
||||
52
vendor/intervention/image/src/Drivers/Gd/Decoders/EncodedImageObjectDecoder.php
vendored
Normal file
52
vendor/intervention/image/src/Drivers/Gd/Decoders/EncodedImageObjectDecoder.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\EncodedImageInterface;
|
||||
|
||||
class EncodedImageObjectDecoder extends BinaryImageDecoder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::supports()
|
||||
*/
|
||||
public function supports(mixed $input): bool
|
||||
{
|
||||
return $input instanceof EncodedImageInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::decode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ImageDecoderException
|
||||
* @throws DriverException
|
||||
* @throws StateException
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
public function decode(mixed $input): ImageInterface
|
||||
{
|
||||
if (!$input instanceof EncodedImageInterface) {
|
||||
throw new InvalidArgumentException('Image source must be of type ' . EncodedImage::class);
|
||||
}
|
||||
|
||||
try {
|
||||
return parent::decode($input->toString());
|
||||
} catch (DecoderException) {
|
||||
throw new ImageDecoderException(EncodedImage::class . ' contains unsupported image type');
|
||||
}
|
||||
}
|
||||
}
|
||||
119
vendor/intervention/image/src/Drivers/Gd/Decoders/FilePathImageDecoder.php
vendored
Normal file
119
vendor/intervention/image/src/Drivers/Gd/Decoders/FilePathImageDecoder.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Exceptions\DirectoryNotFoundException;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\FileNotFoundException;
|
||||
use Intervention\Image\Exceptions\FileNotReadableException;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Format;
|
||||
use Intervention\Image\Interfaces\DecoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\MediaType;
|
||||
use Intervention\Image\Modifiers\OrientModifier;
|
||||
use Intervention\Image\Traits\CanDetectImageSources;
|
||||
use Throwable;
|
||||
|
||||
class FilePathImageDecoder extends NativeObjectDecoder implements DecoderInterface
|
||||
{
|
||||
use CanDetectImageSources;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::supports()
|
||||
*/
|
||||
public function supports(mixed $input): bool
|
||||
{
|
||||
return $this->couldBeFilePath($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::decode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ImageDecoderException
|
||||
* @throws DriverException
|
||||
* @throws StateException
|
||||
* @throws FileNotFoundException
|
||||
* @throws FileNotReadableException
|
||||
* @throws DirectoryNotFoundException
|
||||
*/
|
||||
public function decode(mixed $input): ImageInterface
|
||||
{
|
||||
// make sure path is valid
|
||||
$path = self::readableFilePathOrFail($input);
|
||||
|
||||
try {
|
||||
// detect media (mime) type
|
||||
$mediaType = $this->mediaTypeByFilePath($path);
|
||||
} catch (Throwable) {
|
||||
throw new ImageDecoderException('File contains unsupported image format');
|
||||
}
|
||||
|
||||
$image = match ($mediaType->format()) {
|
||||
// gif files might be animated and therefore cannot
|
||||
// be handled by the standard GD decoder.
|
||||
Format::GIF => $this->decodeGif($path),
|
||||
default => $this->decodeDefault($path, $mediaType),
|
||||
};
|
||||
|
||||
// set file path & mediaType on origin
|
||||
$image->origin()->setFilePath($path);
|
||||
$image->origin()->setMediaType($mediaType);
|
||||
|
||||
// extract exif for the appropriate formats
|
||||
if ($mediaType->format() === Format::JPEG) {
|
||||
$image->setExif($this->extractExifData($path));
|
||||
}
|
||||
|
||||
// adjust image orientation
|
||||
if ($this->driver()->config()->autoOrientation) {
|
||||
$image->modify(new OrientModifier());
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to decode data from file path as given image format
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ImageDecoderException
|
||||
* @throws StateException
|
||||
* @throws DriverException
|
||||
*/
|
||||
private function decodeDefault(string $path, MediaType $mediaType): ImageInterface
|
||||
{
|
||||
$gdImage = match ($mediaType->format()) {
|
||||
Format::JPEG => @imagecreatefromjpeg($path),
|
||||
Format::WEBP => @imagecreatefromwebp($path),
|
||||
Format::PNG => @imagecreatefrompng($path),
|
||||
Format::AVIF => @imagecreatefromavif($path),
|
||||
Format::BMP => @imagecreatefrombmp($path),
|
||||
default => throw new ImageDecoderException('File contains unsupported image format'),
|
||||
};
|
||||
|
||||
if ($gdImage === false) {
|
||||
throw new ImageDecoderException(
|
||||
'Failed to decode data from file "' . $path . '" as image format "' . $mediaType->value . '"',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return parent::decode($gdImage);
|
||||
} catch (DecoderException) {
|
||||
throw new ImageDecoderException(
|
||||
'Failed to decode data from file "' . $path . '" as image format "' . $mediaType->value . '"',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
117
vendor/intervention/image/src/Drivers/Gd/Decoders/NativeObjectDecoder.php
vendored
Normal file
117
vendor/intervention/image/src/Drivers/Gd/Decoders/NativeObjectDecoder.php
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use GdImage;
|
||||
use Intervention\Gif\Exceptions\GifException;
|
||||
use Intervention\Gif\Splitter as GifSplitter;
|
||||
use Intervention\Image\Drivers\Gd\Core;
|
||||
use Intervention\Image\Drivers\Gd\Frame;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Image;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
class NativeObjectDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::supports()
|
||||
*/
|
||||
public function supports(mixed $input): bool
|
||||
{
|
||||
return $input instanceof GdImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::decode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
* @throws StateException
|
||||
*/
|
||||
public function decode(mixed $input): ImageInterface
|
||||
{
|
||||
if (!$input instanceof GdImage) {
|
||||
throw new InvalidArgumentException('Image source must be of type ' . GdImage::class);
|
||||
}
|
||||
|
||||
if (!imageistruecolor($input)) {
|
||||
$result = imagepalettetotruecolor($input);
|
||||
if ($result === false) {
|
||||
throw new DriverException('Failed to convert image to true color');
|
||||
}
|
||||
}
|
||||
|
||||
imagesavealpha($input, true);
|
||||
|
||||
// build image instance
|
||||
return new Image(
|
||||
$this->driver(),
|
||||
new Core([
|
||||
new Frame($input)
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode image from given GIF source which can be either a file path or binary data.
|
||||
*
|
||||
* Depending on the configuration, this is taken over by the native GD function
|
||||
* or, if animations are required, by our own extended decoder.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ImageDecoderException
|
||||
* @throws DriverException
|
||||
* @throws StateException
|
||||
*/
|
||||
protected function decodeGif(mixed $input): ImageInterface
|
||||
{
|
||||
// create non-animated image depending on config
|
||||
if ($this->driver()->config()->decodeAnimation === false) {
|
||||
$native = $this->isGifFormat($input) ? @imagecreatefromstring($input) : @imagecreatefromgif($input);
|
||||
|
||||
if ($native === false) {
|
||||
throw new ImageDecoderException('Failed to decode GIF format');
|
||||
}
|
||||
|
||||
$image = self::decode($native);
|
||||
$image->origin()->setMediaType('image/gif');
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
try {
|
||||
// create empty core
|
||||
$core = new Core();
|
||||
|
||||
// add frames to core
|
||||
$splitter = GifSplitter::decode($input)
|
||||
->split()
|
||||
->flatten()
|
||||
->each(function (GdImage $native, int $delay) use ($core): void {
|
||||
$core->push(new Frame($native, $delay / 100));
|
||||
});
|
||||
|
||||
// set loops on core
|
||||
$core->setLoops($splitter->loops());
|
||||
} catch (GifException $e) {
|
||||
throw new ImageDecoderException('Failed to decode GIF format', previous: $e);
|
||||
}
|
||||
|
||||
// create (possibly) animated image
|
||||
$image = new Image($this->driver(), $core);
|
||||
|
||||
// set media type
|
||||
$image->origin()->setMediaType('image/gif');
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
59
vendor/intervention/image/src/Drivers/Gd/Decoders/SplFileInfoImageDecoder.php
vendored
Normal file
59
vendor/intervention/image/src/Drivers/Gd/Decoders/SplFileInfoImageDecoder.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use SplFileInfo;
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Exceptions\DirectoryNotFoundException;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\FileNotFoundException;
|
||||
use Intervention\Image\Exceptions\FileNotReadableException;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\DecoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Traits\CanParseFilePath;
|
||||
|
||||
class SplFileInfoImageDecoder extends FilePathImageDecoder implements DecoderInterface
|
||||
{
|
||||
use CanParseFilePath;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::supports()
|
||||
*/
|
||||
public function supports(mixed $input): bool
|
||||
{
|
||||
return $input instanceof SplFileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::decode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ImageDecoderException
|
||||
* @throws DriverException
|
||||
* @throws StateException
|
||||
* @throws DirectoryNotFoundException
|
||||
* @throws FileNotFoundException
|
||||
* @throws FileNotReadableException
|
||||
*/
|
||||
public function decode(mixed $input): ImageInterface
|
||||
{
|
||||
if (!$input instanceof SplFileInfo) {
|
||||
throw new InvalidArgumentException('Image source must be of type ' . SplFileInfo::class);
|
||||
}
|
||||
|
||||
try {
|
||||
return parent::decode(self::filePathFromSplFileInfoOrFail($input));
|
||||
} catch (DecoderException) {
|
||||
throw new ImageDecoderException(SplFileInfo::class . ' contains unsupported image type');
|
||||
}
|
||||
}
|
||||
}
|
||||
70
vendor/intervention/image/src/Drivers/Gd/Decoders/StreamImageDecoder.php
vendored
Normal file
70
vendor/intervention/image/src/Drivers/Gd/Decoders/StreamImageDecoder.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Decoders;
|
||||
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\ImageDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
class StreamImageDecoder extends BinaryImageDecoder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::supports()
|
||||
*/
|
||||
public function supports(mixed $input): bool
|
||||
{
|
||||
return is_resource($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DecoderInterface::decode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
* @throws DriverException
|
||||
* @throws StateException
|
||||
* @throws ImageDecoderException
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
public function decode(mixed $input): ImageInterface
|
||||
{
|
||||
if (!is_resource($input) || !in_array(get_resource_type($input), ['file', 'stream'])) {
|
||||
throw new InvalidArgumentException("Image source must be a resource of type 'file' or 'stream'");
|
||||
}
|
||||
|
||||
$contents = '';
|
||||
$result = rewind($input);
|
||||
|
||||
if ($result === false) {
|
||||
throw new StreamException('Failed to rewind position of stream');
|
||||
}
|
||||
|
||||
while (!feof($input)) {
|
||||
$chunk = fread($input, 1024);
|
||||
if ($chunk === false) {
|
||||
throw new StreamException('Failed to read image from stream');
|
||||
}
|
||||
|
||||
$contents .= $chunk;
|
||||
}
|
||||
|
||||
try {
|
||||
return parent::decode($contents);
|
||||
} catch (DecoderException) {
|
||||
throw new ImageDecoderException(
|
||||
'Failed to decode image from stream, could be unsupported image format',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
135
vendor/intervention/image/src/Drivers/Gd/Driver.php
vendored
Normal file
135
vendor/intervention/image/src/Drivers/Gd/Driver.php
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd;
|
||||
|
||||
use GdImage;
|
||||
use Intervention\Image\Drivers\AbstractDriver;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\MissingDependencyException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Format;
|
||||
use Intervention\Image\FileExtension;
|
||||
use Intervention\Image\Image;
|
||||
use Intervention\Image\Interfaces\ColorProcessorInterface;
|
||||
use Intervention\Image\Interfaces\CoreInterface;
|
||||
use Intervention\Image\Interfaces\FontProcessorInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\MediaType;
|
||||
|
||||
class Driver extends AbstractDriver
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DriverInterface::id()
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return 'GD';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DriverInterface::checkHealth()
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function checkHealth(): void
|
||||
{
|
||||
if (!extension_loaded('gd') || !function_exists('gd_info')) {
|
||||
throw new MissingDependencyException(
|
||||
'GD PHP extension must be installed to use this driver'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DriverInterface::createImage()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function createImage(int $width, int $height): ImageInterface
|
||||
{
|
||||
if ($width < 1 || $height < 1) {
|
||||
throw new InvalidArgumentException('Invalid image size. Only use int<1, max>');
|
||||
}
|
||||
|
||||
// build new transparent GDImage
|
||||
$data = imagecreatetruecolor($width, $height);
|
||||
if (!$data instanceof GDImage) {
|
||||
throw new DriverException('Failed to create new image');
|
||||
}
|
||||
|
||||
imagesavealpha($data, true);
|
||||
$background = imagecolorallocatealpha($data, 255, 255, 255, 127);
|
||||
|
||||
imagealphablending($data, false);
|
||||
imagefill($data, 0, 0, $background);
|
||||
imagecolortransparent($data, $background);
|
||||
imageresolution($data, 72, 72);
|
||||
|
||||
return new Image($this, new Core([new Frame($data)]));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DriverInterface::createCore()
|
||||
*/
|
||||
public function createCore(array $frames): CoreInterface
|
||||
{
|
||||
return new Core($frames);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DriverInterface::colorProcessor()
|
||||
*/
|
||||
public function colorProcessor(ImageInterface $image): ColorProcessorInterface
|
||||
{
|
||||
return new ColorProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DriverInterface::fontProcessor()
|
||||
*/
|
||||
public function fontProcessor(): FontProcessorInterface
|
||||
{
|
||||
return new FontProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see DriverInterface::supports()
|
||||
*/
|
||||
public function supports(string|Format|FileExtension|MediaType $identifier): bool
|
||||
{
|
||||
return match (Format::tryCreate($identifier)) {
|
||||
Format::JPEG => boolval(imagetypes() & IMG_JPEG),
|
||||
Format::WEBP => boolval(imagetypes() & IMG_WEBP),
|
||||
Format::GIF => boolval(imagetypes() & IMG_GIF),
|
||||
Format::PNG => boolval(imagetypes() & IMG_PNG),
|
||||
Format::AVIF => boolval(imagetypes() & IMG_AVIF),
|
||||
Format::BMP => boolval(imagetypes() & IMG_BMP),
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return version of GD library
|
||||
*/
|
||||
public function version(): string
|
||||
{
|
||||
return gd_info()['GD Version'];
|
||||
}
|
||||
}
|
||||
30
vendor/intervention/image/src/Drivers/Gd/Encoders/AvifEncoder.php
vendored
Normal file
30
vendor/intervention/image/src/Drivers/Gd/Encoders/AvifEncoder.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use Intervention\Image\Encoders\AvifEncoder as GenericAvifEncoder;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\EncodedImageInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class AvifEncoder extends GenericAvifEncoder implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see EncoderInterface::encode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
*/
|
||||
public function encode(ImageInterface $image): EncodedImageInterface
|
||||
{
|
||||
return $this->createEncodedImage(function ($stream) use ($image): void {
|
||||
imageavif($image->core()->native(), $stream, $this->quality);
|
||||
}, 'image/avif');
|
||||
}
|
||||
}
|
||||
30
vendor/intervention/image/src/Drivers/Gd/Encoders/BmpEncoder.php
vendored
Normal file
30
vendor/intervention/image/src/Drivers/Gd/Encoders/BmpEncoder.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use Intervention\Image\Encoders\BmpEncoder as GenericBmpEncoder;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\EncodedImageInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class BmpEncoder extends GenericBmpEncoder implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see EncoderInterface::encode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
*/
|
||||
public function encode(ImageInterface $image): EncodedImageInterface
|
||||
{
|
||||
return $this->createEncodedImage(function ($stream) use ($image): void {
|
||||
imagebmp($image->core()->native(), $stream, false);
|
||||
}, 'image/bmp');
|
||||
}
|
||||
}
|
||||
75
vendor/intervention/image/src/Drivers/Gd/Encoders/GifEncoder.php
vendored
Normal file
75
vendor/intervention/image/src/Drivers/Gd/Encoders/GifEncoder.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use Intervention\Gif\Builder as GifBuilder;
|
||||
use Intervention\Gif\Exceptions\GifException;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\EncodedImage;
|
||||
use Intervention\Image\Encoders\GifEncoder as GenericGifEncoder;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\EncoderException;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\FilesystemException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\EncodedImageInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class GifEncoder extends GenericGifEncoder implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see EncoderInterface::encode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws EncoderException
|
||||
* @throws DriverException
|
||||
* @throws StreamException
|
||||
*/
|
||||
public function encode(ImageInterface $image): EncodedImageInterface
|
||||
{
|
||||
if ($image->isAnimated()) {
|
||||
return $this->encodeAnimated($image);
|
||||
}
|
||||
|
||||
$gd = Cloner::clone($image->core()->native());
|
||||
|
||||
return $this->createEncodedImage(function ($stream) use ($gd): void {
|
||||
imageinterlace($gd, $this->interlaced);
|
||||
imagegif($gd, $stream);
|
||||
}, 'image/gif');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws EncoderException
|
||||
* @throws DriverException
|
||||
*/
|
||||
protected function encodeAnimated(ImageInterface $image): EncodedImageInterface
|
||||
{
|
||||
try {
|
||||
$builder = GifBuilder::canvas(
|
||||
$image->width(),
|
||||
$image->height()
|
||||
);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$builder->addFrame(
|
||||
source: $this->encode($frame->toImage($image->driver()))->toStream(),
|
||||
delay: $frame->delay(),
|
||||
interlaced: $this->interlaced
|
||||
);
|
||||
}
|
||||
|
||||
$builder->setLoops($image->loops());
|
||||
|
||||
return new EncodedImage($builder->encode(), 'image/gif');
|
||||
} catch (GifException | FilesystemException $e) {
|
||||
throw new EncoderException('Failed to encode image to GIF format', previous: $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
vendor/intervention/image/src/Drivers/Gd/Encoders/JpegEncoder.php
vendored
Normal file
54
vendor/intervention/image/src/Drivers/Gd/Encoders/JpegEncoder.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use Intervention\Image\Colors\Rgb\Color as RgbColor;
|
||||
use Intervention\Image\Colors\Rgb\Colorspace as Rgb;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Encoders\JpegEncoder as GenericJpegEncoder;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\EncodedImageInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class JpegEncoder extends GenericJpegEncoder implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see EncoderInterface::encode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StateException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
* @throws StreamException
|
||||
*/
|
||||
public function encode(ImageInterface $image): EncodedImageInterface
|
||||
{
|
||||
$backgroundColor = $this->driver()->decodeColor(
|
||||
$this->driver()->config()->backgroundColor
|
||||
)->toColorspace(Rgb::class);
|
||||
|
||||
|
||||
if (!$backgroundColor instanceof RgbColor) {
|
||||
throw new ModifierException('Failed to normalize background color to rgb color space');
|
||||
}
|
||||
|
||||
$output = Cloner::cloneBlended(
|
||||
$image->core()->native(),
|
||||
background: $backgroundColor
|
||||
);
|
||||
|
||||
return $this->createEncodedImage(function ($stream) use ($output): void {
|
||||
imageinterlace($output, $this->progressive);
|
||||
imagejpeg($output, $stream, $this->quality);
|
||||
}, 'image/jpeg');
|
||||
}
|
||||
}
|
||||
55
vendor/intervention/image/src/Drivers/Gd/Encoders/PngEncoder.php
vendored
Normal file
55
vendor/intervention/image/src/Drivers/Gd/Encoders/PngEncoder.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use GdImage;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Encoders\PngEncoder as GenericPngEncoder;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\EncodedImageInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class PngEncoder extends GenericPngEncoder implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see EncoderInterface::encode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function encode(ImageInterface $image): EncodedImageInterface
|
||||
{
|
||||
$output = $this->prepareOutput($image);
|
||||
|
||||
return $this->createEncodedImage(function ($stream) use ($output): void {
|
||||
imageinterlace($output, $this->interlaced);
|
||||
imagepng($output, $stream, -1);
|
||||
}, 'image/png');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare given image instance for PNG format output according to encoder settings
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
*/
|
||||
private function prepareOutput(ImageInterface $image): GdImage
|
||||
{
|
||||
if ($this->indexed) {
|
||||
$output = clone $image;
|
||||
$output->reduceColors(256);
|
||||
|
||||
return $output->core()->native();
|
||||
}
|
||||
|
||||
return Cloner::clone($image->core()->native());
|
||||
}
|
||||
}
|
||||
32
vendor/intervention/image/src/Drivers/Gd/Encoders/WebpEncoder.php
vendored
Normal file
32
vendor/intervention/image/src/Drivers/Gd/Encoders/WebpEncoder.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
use Intervention\Image\Encoders\WebpEncoder as GenericWebpEncoder;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\EncodedImageInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
|
||||
class WebpEncoder extends GenericWebpEncoder implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see EncoderInterface::encode()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
*/
|
||||
public function encode(ImageInterface $image): EncodedImageInterface
|
||||
{
|
||||
$quality = $this->quality === 100 && defined('IMG_WEBP_LOSSLESS') ? IMG_WEBP_LOSSLESS : $this->quality;
|
||||
|
||||
return $this->createEncodedImage(function ($stream) use ($image, $quality): void {
|
||||
imagewebp($image->core()->native(), $stream, $quality);
|
||||
}, 'image/webp');
|
||||
}
|
||||
}
|
||||
106
vendor/intervention/image/src/Drivers/Gd/FontProcessor.php
vendored
Normal file
106
vendor/intervention/image/src/Drivers/Gd/FontProcessor.php
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd;
|
||||
|
||||
use Intervention\Image\Drivers\AbstractFontProcessor;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Geometry\Point;
|
||||
use Intervention\Image\Interfaces\FontInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
use Intervention\Image\Size;
|
||||
|
||||
class FontProcessor extends AbstractFontProcessor
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FontProcessorInterface::boxSize()
|
||||
*
|
||||
* @throws DriverException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function boxSize(string $text, FontInterface $font): SizeInterface
|
||||
{
|
||||
// if the font has no ttf file the box size is calculated
|
||||
// with gd's internal font system: integer values from 1-5
|
||||
if (!$font->hasFile()) {
|
||||
// font size to gd's internal fonts (1-5)
|
||||
$gdFont = (int) $font->size();
|
||||
|
||||
// calculate box size from gd font
|
||||
$box = new Size(0, 0);
|
||||
$chars = mb_strlen($text);
|
||||
if ($chars > 0) {
|
||||
$box->setWidth(
|
||||
$chars * $this->gdCharacterWidth($gdFont)
|
||||
);
|
||||
$box->setHeight(
|
||||
$this->gdCharacterHeight($gdFont)
|
||||
);
|
||||
}
|
||||
return $box;
|
||||
}
|
||||
|
||||
// calculate box size from ttf font file with angle 0
|
||||
$box = imageftbbox(
|
||||
size: $this->nativeFontSize($font),
|
||||
angle: 0,
|
||||
font_filename: $font->filepath(),
|
||||
string: $text,
|
||||
);
|
||||
|
||||
if ($box === false) {
|
||||
throw new DriverException('Unable to calculate box size of font');
|
||||
}
|
||||
|
||||
// build size from points
|
||||
return new Size(
|
||||
width: intval(abs($box[6] - $box[4])), // difference of upper-left-x and upper-right-x
|
||||
height: intval(abs($box[7] - $box[1])), // difference if upper-left-y and lower-left-y
|
||||
pivot: new Point($box[6], $box[7]), // position of upper-left corner
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FontProcessorInterface::nativeFontSize()
|
||||
*/
|
||||
public function nativeFontSize(FontInterface $font): float
|
||||
{
|
||||
return floatval(round($font->size() * .76, 6));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FontProcessorInterface::leading()
|
||||
*/
|
||||
public function leading(FontInterface $font): int
|
||||
{
|
||||
return (int) round(parent::leading($font) * .8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return width of a single character
|
||||
*/
|
||||
protected function gdCharacterWidth(int $gdfont): int
|
||||
{
|
||||
return $gdfont + 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return height of a single character
|
||||
*/
|
||||
protected function gdCharacterHeight(int $gdfont): int
|
||||
{
|
||||
return match ($gdfont) {
|
||||
2, 3 => 14,
|
||||
4, 5 => 16,
|
||||
default => 8,
|
||||
};
|
||||
}
|
||||
}
|
||||
202
vendor/intervention/image/src/Drivers/Gd/Frame.php
vendored
Normal file
202
vendor/intervention/image/src/Drivers/Gd/Frame.php
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd;
|
||||
|
||||
use GdImage;
|
||||
use Intervention\Image\Drivers\AbstractFrame;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Image;
|
||||
use Intervention\Image\Interfaces\DriverInterface;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
use Intervention\Image\Size;
|
||||
|
||||
class Frame extends AbstractFrame implements FrameInterface
|
||||
{
|
||||
/**
|
||||
* Create new frame instance
|
||||
*/
|
||||
public function __construct(
|
||||
protected GdImage $native,
|
||||
protected float $delay = 0,
|
||||
protected int $disposalMethod = 1,
|
||||
protected int $offsetLeft = 0,
|
||||
protected int $offsetTop = 0
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::toImage()
|
||||
*/
|
||||
public function toImage(DriverInterface $driver): ImageInterface
|
||||
{
|
||||
return new Image($driver, new Core([$this]));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::setNative()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setNative(mixed $native): FrameInterface
|
||||
{
|
||||
if (!$native instanceof GdImage) {
|
||||
throw new InvalidArgumentException(
|
||||
'Value for argument setNative() "$native" must be instanceof of ' . GdImage::class,
|
||||
);
|
||||
}
|
||||
|
||||
$this->native = $native;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::native()
|
||||
*/
|
||||
public function native(): GdImage
|
||||
{
|
||||
return $this->native;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::size()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function size(): SizeInterface
|
||||
{
|
||||
return new Size(imagesx($this->native), imagesy($this->native));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::delay()
|
||||
*/
|
||||
public function delay(): float
|
||||
{
|
||||
return $this->delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::setDelay()
|
||||
*/
|
||||
public function setDelay(float $delay): FrameInterface
|
||||
{
|
||||
$this->delay = $delay;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::disposalMethod()
|
||||
*/
|
||||
public function disposalMethod(): int
|
||||
{
|
||||
return $this->disposalMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::setDisposalMethod()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setDisposalMethod(int $method): FrameInterface
|
||||
{
|
||||
if (!in_array($method, [0, 1, 2, 3])) {
|
||||
throw new InvalidArgumentException('Value for disposal method "$method" must be 0, 1, 2 or 3');
|
||||
}
|
||||
|
||||
$this->disposalMethod = $method;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::setOffset()
|
||||
*/
|
||||
public function setOffset(int $left, int $top): FrameInterface
|
||||
{
|
||||
$this->offsetLeft = $left;
|
||||
$this->offsetTop = $top;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::offsetLeft()
|
||||
*/
|
||||
public function offsetLeft(): int
|
||||
{
|
||||
return $this->offsetLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::setOffsetLeft()
|
||||
*/
|
||||
public function setOffsetLeft(int $offset): FrameInterface
|
||||
{
|
||||
$this->offsetLeft = $offset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::offsetTop()
|
||||
*/
|
||||
public function offsetTop(): int
|
||||
{
|
||||
return $this->offsetTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see FrameInterface::setOffsetTop()
|
||||
*/
|
||||
public function setOffsetTop(int $offset): FrameInterface
|
||||
{
|
||||
$this->offsetTop = $offset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This workaround helps cloning GdImages which is currently not possible.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function __clone(): void
|
||||
{
|
||||
$this->native = Cloner::clone($this->native);
|
||||
}
|
||||
}
|
||||
36
vendor/intervention/image/src/Drivers/Gd/Modifiers/BlurModifier.php
vendored
Normal file
36
vendor/intervention/image/src/Drivers/Gd/Modifiers/BlurModifier.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\BlurModifier as GenericBlurModifier;
|
||||
|
||||
class BlurModifier extends GenericBlurModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
for ($i = 0; $i < $this->level; $i++) {
|
||||
$result = imagefilter($frame->native(), IMG_FILTER_GAUSSIAN_BLUR);
|
||||
if ($result === false) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', unable to process blur effect',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
34
vendor/intervention/image/src/Drivers/Gd/Modifiers/BrightnessModifier.php
vendored
Normal file
34
vendor/intervention/image/src/Drivers/Gd/Modifiers/BrightnessModifier.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\BrightnessModifier as GenericBrightnessModifier;
|
||||
|
||||
class BrightnessModifier extends GenericBrightnessModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
$result = imagefilter($frame->native(), IMG_FILTER_BRIGHTNESS, intval($this->level * 2.55));
|
||||
if ($result === false) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', unable to set image brightness',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
37
vendor/intervention/image/src/Drivers/Gd/Modifiers/ColorizeModifier.php
vendored
Normal file
37
vendor/intervention/image/src/Drivers/Gd/Modifiers/ColorizeModifier.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ColorizeModifier as GenericColorizeModifier;
|
||||
|
||||
class ColorizeModifier extends GenericColorizeModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
// normalize colorize levels
|
||||
$red = (int) round($this->red * 2.55);
|
||||
$green = (int) round($this->green * 2.55);
|
||||
$blue = (int) round($this->blue * 2.55);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$result = imagefilter($frame->native(), IMG_FILTER_COLORIZE, $red, $green, $blue);
|
||||
if ($result === false) {
|
||||
throw new ModifierException('Failed to apply colorize effect');
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
32
vendor/intervention/image/src/Drivers/Gd/Modifiers/ColorspaceModifier.php
vendored
Normal file
32
vendor/intervention/image/src/Drivers/Gd/Modifiers/ColorspaceModifier.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace;
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ColorspaceModifier as GenericColorspaceModifier;
|
||||
|
||||
class ColorspaceModifier extends GenericColorspaceModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
if (!$this->targetColorspace() instanceof RgbColorspace) {
|
||||
throw new NotSupportedException(
|
||||
'Only RGB colorspace is supported by GD driver'
|
||||
);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
27
vendor/intervention/image/src/Drivers/Gd/Modifiers/ContainDownModifier.php
vendored
Normal file
27
vendor/intervention/image/src/Drivers/Gd/Modifiers/ContainDownModifier.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
|
||||
class ContainDownModifier extends ContainModifier
|
||||
{
|
||||
/**
|
||||
* Calculate crop size of the contain down resizing process.
|
||||
*/
|
||||
protected function cropSize(ImageInterface $image): SizeInterface
|
||||
{
|
||||
return $image->size()
|
||||
->containDown(
|
||||
$this->width,
|
||||
$this->height
|
||||
)
|
||||
->alignPivotTo(
|
||||
$this->resizeSize($image),
|
||||
$this->alignment
|
||||
);
|
||||
}
|
||||
}
|
||||
103
vendor/intervention/image/src/Drivers/Gd/Modifiers/ContainModifier.php
vendored
Normal file
103
vendor/intervention/image/src/Drivers/Gd/Modifiers/ContainModifier.php
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Colors\Rgb\Colorspace as Rgb;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ContainModifier as GenericContainModifier;
|
||||
use Intervention\Image\Colors\Rgb\Color as RgbColor;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
|
||||
class ContainModifier extends GenericContainModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$crop = $this->cropSize($image);
|
||||
$resize = $this->resizeSize($image);
|
||||
$backgroundColor = $this->backgroundColor()->toColorspace(Rgb::class);
|
||||
|
||||
if (!$backgroundColor instanceof RgbColor) {
|
||||
throw new ModifierException('Failed to normalize background color to RGB color space');
|
||||
}
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$this->modify($frame, $crop, $resize, $backgroundColor);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
*/
|
||||
private function modify(
|
||||
FrameInterface $frame,
|
||||
SizeInterface $crop,
|
||||
SizeInterface $resize,
|
||||
RgbColor $backgroundColor
|
||||
): void {
|
||||
// create new gd image
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resize, $backgroundColor);
|
||||
|
||||
// make image area transparent to keep transparency
|
||||
// even if background-color is set
|
||||
$transparent = imagecolorallocatealpha(
|
||||
$modified,
|
||||
$backgroundColor->red()->value(),
|
||||
$backgroundColor->green()->value(),
|
||||
$backgroundColor->blue()->value(),
|
||||
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 background 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 resource
|
||||
$frame->setNative($modified);
|
||||
}
|
||||
}
|
||||
32
vendor/intervention/image/src/Drivers/Gd/Modifiers/ContrastModifier.php
vendored
Normal file
32
vendor/intervention/image/src/Drivers/Gd/Modifiers/ContrastModifier.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ContrastModifier as GenericContrastModifier;
|
||||
|
||||
class ContrastModifier extends GenericContrastModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
$result = imagefilter($frame->native(), IMG_FILTER_CONTRAST, ($this->level * -1));
|
||||
if ($result === false) {
|
||||
throw new ModifierException('Failed to set image contrast');
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
18
vendor/intervention/image/src/Drivers/Gd/Modifiers/CoverDownModifier.php
vendored
Normal file
18
vendor/intervention/image/src/Drivers/Gd/Modifiers/CoverDownModifier.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
|
||||
class CoverDownModifier extends CoverModifier
|
||||
{
|
||||
/**
|
||||
* Calculate resizing size of the cover down process
|
||||
*/
|
||||
protected function resizeSize(SizeInterface $size): SizeInterface
|
||||
{
|
||||
return $size->resizeDown($this->width, $this->height);
|
||||
}
|
||||
}
|
||||
67
vendor/intervention/image/src/Drivers/Gd/Modifiers/CoverModifier.php
vendored
Normal file
67
vendor/intervention/image/src/Drivers/Gd/Modifiers/CoverModifier.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\CoverModifier as GenericCoverModifier;
|
||||
|
||||
class CoverModifier extends GenericCoverModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$crop = $this->cropSize($image);
|
||||
$resize = $this->resizeSize($crop);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$this->modifyFrame($frame, $crop, $resize);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
*/
|
||||
protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void
|
||||
{
|
||||
// create new image
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resize);
|
||||
|
||||
// copy content from resource
|
||||
imagecopyresampled(
|
||||
$modified,
|
||||
$frame->native(),
|
||||
0,
|
||||
0,
|
||||
$crop->pivot()->x(),
|
||||
$crop->pivot()->y(),
|
||||
$resize->width(),
|
||||
$resize->height(),
|
||||
$crop->width(),
|
||||
$crop->height()
|
||||
);
|
||||
|
||||
// set new content as resource
|
||||
$frame->setNative($modified);
|
||||
}
|
||||
}
|
||||
93
vendor/intervention/image/src/Drivers/Gd/Modifiers/CropModifier.php
vendored
Normal file
93
vendor/intervention/image/src/Drivers/Gd/Modifiers/CropModifier.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\CropModifier as GenericCropModifier;
|
||||
use Intervention\Image\Colors\Rgb\Color as RgbColor;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
|
||||
class CropModifier extends GenericCropModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$originalSize = $image->size();
|
||||
$crop = $this->crop($image);
|
||||
$background = $this->backgroundColor()->toColorspace(RgbColorspace::class);
|
||||
|
||||
if (!$background instanceof RgbColor) {
|
||||
throw new ModifierException('Failed to normalize background color to RGB color space');
|
||||
}
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$this->cropFrame($frame, $originalSize, $crop, $background);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
*/
|
||||
private function cropFrame(
|
||||
FrameInterface $frame,
|
||||
SizeInterface $originalSize,
|
||||
SizeInterface $resizeTo,
|
||||
RgbColor $background
|
||||
): void {
|
||||
// create new image with transparent background
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo, $background);
|
||||
|
||||
// define offset
|
||||
$offsetX = $resizeTo->pivot()->x() + $this->x;
|
||||
$offsetY = $resizeTo->pivot()->y() + $this->y;
|
||||
|
||||
// define target width & height
|
||||
$targetWidth = min($resizeTo->width(), $originalSize->width());
|
||||
$targetHeight = min($resizeTo->height(), $originalSize->height());
|
||||
$targetWidth = $targetWidth < $originalSize->width() ? $targetWidth + $offsetX : $targetWidth;
|
||||
$targetHeight = $targetHeight < $originalSize->height() ? $targetHeight + $offsetY : $targetHeight;
|
||||
|
||||
// don't alpha blend for copy operation to keep transparent areas of original image
|
||||
imagealphablending($modified, false);
|
||||
|
||||
// copy content from resource
|
||||
imagecopyresampled(
|
||||
$modified,
|
||||
$frame->native(),
|
||||
$offsetX * -1,
|
||||
$offsetY * -1,
|
||||
0,
|
||||
0,
|
||||
$targetWidth,
|
||||
$targetHeight,
|
||||
$targetWidth,
|
||||
$targetHeight
|
||||
);
|
||||
|
||||
// set new content as resource
|
||||
$frame->setNative($modified);
|
||||
}
|
||||
}
|
||||
270
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawBezierModifier.php
vendored
Normal file
270
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawBezierModifier.php
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ColorDecoderException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\DrawBezierModifier as GenericDrawBezierModifier;
|
||||
|
||||
class DrawBezierModifier extends GenericDrawBezierModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws ColorDecoderException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
if ($this->drawable->count() !== 3 && $this->drawable->count() !== 4) {
|
||||
throw new InvalidArgumentException('You must specify either 3 or 4 points to create a bezier curve');
|
||||
}
|
||||
|
||||
[$polygon, $polygonBorderSegments] = $this->calculateBezierPoints();
|
||||
|
||||
if ($this->drawable->hasBackgroundColor() || $this->drawable->hasBorder()) {
|
||||
$result = imagealphablending($frame->native(), true);
|
||||
$this->abortUnless($result, 'Unable to set alpha blending');
|
||||
|
||||
$result = imageantialias($frame->native(), true);
|
||||
$this->abortUnless($result, 'Unable to set image antialias option');
|
||||
}
|
||||
|
||||
if ($this->drawable->hasBackgroundColor()) {
|
||||
$backgroundColor = $this->driver()->colorProcessor($image)->export(
|
||||
$this->backgroundColor()
|
||||
);
|
||||
|
||||
$result = imagesetthickness($frame->native(), 0);
|
||||
$this->abortUnless($result, 'Unable to set line thickness');
|
||||
|
||||
$result = imagefilledpolygon(
|
||||
$frame->native(),
|
||||
$polygon,
|
||||
$backgroundColor
|
||||
);
|
||||
|
||||
$this->abortUnless($result, 'Unable to draw line on image');
|
||||
}
|
||||
|
||||
if ($this->drawable->hasBorder() && $this->drawable->borderSize() > 0) {
|
||||
$borderColor = $this->driver()->colorProcessor($image)->export(
|
||||
$this->borderColor()
|
||||
);
|
||||
|
||||
if ($this->drawable->borderSize() === 1) {
|
||||
$result = imagesetthickness($frame->native(), $this->drawable->borderSize());
|
||||
$this->abortUnless($result, 'Unable to set line thickness');
|
||||
|
||||
$count = count($polygon);
|
||||
for ($i = 0; $i < $count; $i += 2) {
|
||||
if (array_key_exists($i + 2, $polygon) && array_key_exists($i + 3, $polygon)) {
|
||||
$result = imageline(
|
||||
$frame->native(),
|
||||
$polygon[$i],
|
||||
$polygon[$i + 1],
|
||||
$polygon[$i + 2],
|
||||
$polygon[$i + 3],
|
||||
$borderColor
|
||||
);
|
||||
|
||||
$this->abortUnless($result, 'Unable to draw line on image');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$polygonBorderSegmentsTotal = count($polygonBorderSegments);
|
||||
|
||||
for ($i = 0; $i < $polygonBorderSegmentsTotal; $i += 1) {
|
||||
$result = imagefilledpolygon(
|
||||
$frame->native(),
|
||||
$polygonBorderSegments[$i],
|
||||
$borderColor
|
||||
);
|
||||
|
||||
$this->abortUnless($result, 'Unable to draw line on image');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate interpolation points for quadratic beziers using the Bernstein polynomial form
|
||||
*
|
||||
* @return array{'x': float, 'y': float}
|
||||
*/
|
||||
private function calculateQuadraticBezierInterpolationPoint(float $t = 0.05): array
|
||||
{
|
||||
$remainder = 1 - $t;
|
||||
$controlPoint1Multiplier = $remainder * $remainder;
|
||||
$controlPoint2Multiplier = $remainder * $t * 2;
|
||||
$controlPoint3Multiplier = $t * $t;
|
||||
|
||||
$x = (
|
||||
$this->drawable->first()->x() * $controlPoint1Multiplier +
|
||||
$this->drawable->second()->x() * $controlPoint2Multiplier +
|
||||
$this->drawable->last()->x() * $controlPoint3Multiplier
|
||||
);
|
||||
$y = (
|
||||
$this->drawable->first()->y() * $controlPoint1Multiplier +
|
||||
$this->drawable->second()->y() * $controlPoint2Multiplier +
|
||||
$this->drawable->last()->y() * $controlPoint3Multiplier
|
||||
);
|
||||
|
||||
return ['x' => $x, 'y' => $y];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate interpolation points for cubic beziers using the Bernstein polynomial form
|
||||
*
|
||||
* @return array{'x': float, 'y': float}
|
||||
*/
|
||||
private function calculateCubicBezierInterpolationPoint(float $t = 0.05): array
|
||||
{
|
||||
$remainder = 1 - $t;
|
||||
$tSquared = $t * $t;
|
||||
$remainderSquared = $remainder * $remainder;
|
||||
$controlPoint1Multiplier = $remainderSquared * $remainder;
|
||||
$controlPoint2Multiplier = $remainderSquared * $t * 3;
|
||||
$controlPoint3Multiplier = $tSquared * $remainder * 3;
|
||||
$controlPoint4Multiplier = $tSquared * $t;
|
||||
|
||||
$x = (
|
||||
$this->drawable->first()->x() * $controlPoint1Multiplier +
|
||||
$this->drawable->second()->x() * $controlPoint2Multiplier +
|
||||
$this->drawable->third()->x() * $controlPoint3Multiplier +
|
||||
$this->drawable->last()->x() * $controlPoint4Multiplier
|
||||
);
|
||||
$y = (
|
||||
$this->drawable->first()->y() * $controlPoint1Multiplier +
|
||||
$this->drawable->second()->y() * $controlPoint2Multiplier +
|
||||
$this->drawable->third()->y() * $controlPoint3Multiplier +
|
||||
$this->drawable->last()->y() * $controlPoint4Multiplier
|
||||
);
|
||||
|
||||
return ['x' => $x, 'y' => $y];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the points needed to draw a quadratic or cubic bezier with optional border/stroke
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @return array{0: array<mixed>, 1: array<mixed>}
|
||||
*/
|
||||
private function calculateBezierPoints(): array
|
||||
{
|
||||
if ($this->drawable->count() !== 3 && $this->drawable->count() !== 4) {
|
||||
throw new InvalidArgumentException('You must specify either 3 or 4 points to create a bezier curve');
|
||||
}
|
||||
|
||||
$polygon = [];
|
||||
$innerPolygon = [];
|
||||
$outerPolygon = [];
|
||||
$polygonBorderSegments = [];
|
||||
|
||||
// define ratio t; equivalent to 5 percent distance along edge
|
||||
$t = 0.05;
|
||||
|
||||
$polygon[] = $this->drawable->first()->x();
|
||||
$polygon[] = $this->drawable->first()->y();
|
||||
for ($i = $t; $i < 1; $i += $t) {
|
||||
if ($this->drawable->count() === 3) {
|
||||
$ip = $this->calculateQuadraticBezierInterpolationPoint($i);
|
||||
} elseif ($this->drawable->count() === 4) {
|
||||
$ip = $this->calculateCubicBezierInterpolationPoint($i);
|
||||
}
|
||||
$polygon[] = (int) $ip['x'];
|
||||
$polygon[] = (int) $ip['y'];
|
||||
}
|
||||
$polygon[] = $this->drawable->last()->x();
|
||||
$polygon[] = $this->drawable->last()->y();
|
||||
|
||||
if ($this->drawable->hasBorder() && $this->drawable->borderSize() > 1) {
|
||||
// create the border/stroke effect by calculating two new curves with offset positions
|
||||
// from the main polygon and then connecting the inner/outer curves to create separate
|
||||
// 4-point polygon segments
|
||||
$polygonTotalPoints = count($polygon);
|
||||
$offset = ($this->drawable->borderSize() / 2);
|
||||
|
||||
for ($i = 0; $i < $polygonTotalPoints; $i += 2) {
|
||||
if (array_key_exists($i + 2, $polygon) && array_key_exists($i + 3, $polygon)) {
|
||||
$dx = $polygon[$i + 2] - $polygon[$i];
|
||||
$dy = $polygon[$i + 3] - $polygon[$i + 1];
|
||||
$dxySqrt = sqrt($dx * $dx + $dy * $dy);
|
||||
|
||||
// prevent division by zero
|
||||
if ($dxySqrt === 0.0) {
|
||||
throw new ModifierException('Failed to apply ' . self::class . ', division by zero');
|
||||
}
|
||||
|
||||
// inner polygon
|
||||
$scale = $offset / $dxySqrt;
|
||||
$ox = -$dy * $scale;
|
||||
$oy = $dx * $scale;
|
||||
|
||||
$innerPolygon[] = $ox + $polygon[$i];
|
||||
$innerPolygon[] = $oy + $polygon[$i + 1];
|
||||
$innerPolygon[] = $ox + $polygon[$i + 2];
|
||||
$innerPolygon[] = $oy + $polygon[$i + 3];
|
||||
|
||||
// outer polygon
|
||||
$scale = -$offset / $dxySqrt;
|
||||
$ox = -$dy * $scale;
|
||||
$oy = $dx * $scale;
|
||||
|
||||
$outerPolygon[] = $ox + $polygon[$i];
|
||||
$outerPolygon[] = $oy + $polygon[$i + 1];
|
||||
$outerPolygon[] = $ox + $polygon[$i + 2];
|
||||
$outerPolygon[] = $oy + $polygon[$i + 3];
|
||||
}
|
||||
}
|
||||
|
||||
$innerPolygonTotalPoints = count($innerPolygon);
|
||||
|
||||
for ($i = 0; $i < $innerPolygonTotalPoints; $i += 2) {
|
||||
if (array_key_exists($i + 2, $innerPolygon) && array_key_exists($i + 3, $innerPolygon)) {
|
||||
$polygonBorderSegments[] = [
|
||||
$innerPolygon[$i],
|
||||
$innerPolygon[$i + 1],
|
||||
$outerPolygon[$i],
|
||||
$outerPolygon[$i + 1],
|
||||
$outerPolygon[$i + 2],
|
||||
$outerPolygon[$i + 3],
|
||||
$innerPolygon[$i + 2],
|
||||
$innerPolygon[$i + 3],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$polygon, $polygonBorderSegments];
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw ModifierException with given message if result is 'false'
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
private function abortUnless(mixed $result, string $message): void
|
||||
{
|
||||
if ($result === false) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', ' . $message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php
vendored
Normal file
82
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawEllipseModifier.php
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ColorDecoderException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\DrawEllipseModifier as GenericDrawEllipseModifier;
|
||||
|
||||
class DrawEllipseModifier extends GenericDrawEllipseModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws ColorDecoderException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
if ($this->drawable->hasBorder()) {
|
||||
imagealphablending($frame->native(), true);
|
||||
|
||||
// slightly smaller ellipse to keep 1px bordered edges clean
|
||||
if ($this->drawable->hasBackgroundColor()) {
|
||||
imagefilledellipse(
|
||||
$frame->native(),
|
||||
$this->drawable()->position()->x(),
|
||||
$this->drawable->position()->y(),
|
||||
$this->drawable->width() - 1,
|
||||
$this->drawable->height() - 1,
|
||||
$this->driver()->colorProcessor($image)->export(
|
||||
$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->drawable()->position()->x(),
|
||||
$this->drawable()->position()->y(),
|
||||
$this->drawable->width(),
|
||||
$this->drawable->height(),
|
||||
0,
|
||||
360,
|
||||
$this->driver()->colorProcessor($image)->export(
|
||||
$this->borderColor()
|
||||
)
|
||||
);
|
||||
} elseif ($this->drawable->hasBackgroundColor()) {
|
||||
imagealphablending($frame->native(), true);
|
||||
imagesetthickness($frame->native(), 0);
|
||||
imagefilledellipse(
|
||||
$frame->native(),
|
||||
$this->drawable()->position()->x(),
|
||||
$this->drawable()->position()->y(),
|
||||
$this->drawable->width(),
|
||||
$this->drawable->height(),
|
||||
$this->driver()->colorProcessor($image)->export(
|
||||
$this->backgroundColor()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
58
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawLineModifier.php
vendored
Normal file
58
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawLineModifier.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ColorDecoderException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\DrawLineModifier as GenericDrawLineModifier;
|
||||
|
||||
class DrawLineModifier extends GenericDrawLineModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws ColorDecoderException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
if (!$this->drawable->hasBackgroundColor()) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
$color = $this->driver()->colorProcessor($image)->export(
|
||||
$this->backgroundColor()
|
||||
);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
$this->modifyFrame($frame, $color);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw current line on given frame
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
private function modifyFrame(FrameInterface $frame, int $color): void
|
||||
{
|
||||
imagealphablending($frame->native(), true);
|
||||
imageantialias($frame->native(), true);
|
||||
imagesetthickness($frame->native(), $this->drawable->width());
|
||||
imageline(
|
||||
$frame->native(),
|
||||
$this->drawable->start()->x(),
|
||||
$this->drawable->start()->y(),
|
||||
$this->drawable->end()->x(),
|
||||
$this->drawable->end()->y(),
|
||||
$color
|
||||
);
|
||||
}
|
||||
}
|
||||
41
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawPixelModifier.php
vendored
Normal file
41
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawPixelModifier.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ColorDecoderException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\DrawPixelModifier as GenericDrawPixelModifier;
|
||||
|
||||
class DrawPixelModifier extends GenericDrawPixelModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws ColorDecoderException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$color = $this->driver()->colorProcessor($image)->export($this->color());
|
||||
|
||||
foreach ($image as $frame) {
|
||||
imagealphablending($frame->native(), true);
|
||||
imagesetpixel(
|
||||
$frame->native(),
|
||||
$this->position->x(),
|
||||
$this->position->y(),
|
||||
$color
|
||||
);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
55
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php
vendored
Normal file
55
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawPolygonModifier.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ColorDecoderException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\DrawPolygonModifier as GenericDrawPolygonModifier;
|
||||
|
||||
class DrawPolygonModifier extends GenericDrawPolygonModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws ColorDecoderException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
if ($this->drawable->hasBackgroundColor()) {
|
||||
imagealphablending($frame->native(), true);
|
||||
imagesetthickness($frame->native(), 0);
|
||||
imagefilledpolygon(
|
||||
$frame->native(),
|
||||
$this->drawable->toArray(),
|
||||
$this->driver()->colorProcessor($image)->export(
|
||||
$this->backgroundColor()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->drawable->hasBorder()) {
|
||||
imagealphablending($frame->native(), true);
|
||||
imagesetthickness($frame->native(), $this->drawable->borderSize());
|
||||
imagepolygon(
|
||||
$frame->native(),
|
||||
$this->drawable->toArray(),
|
||||
$this->driver()->colorProcessor($image)->export(
|
||||
$this->borderColor()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
65
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php
vendored
Normal file
65
vendor/intervention/image/src/Drivers/Gd/Modifiers/DrawRectangleModifier.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ColorDecoderException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\DrawRectangleModifier as GenericDrawRectangleModifier;
|
||||
|
||||
class DrawRectangleModifier extends GenericDrawRectangleModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws ColorDecoderException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$position = $this->drawable->position();
|
||||
|
||||
foreach ($image as $frame) {
|
||||
// draw background
|
||||
if ($this->drawable->hasBackgroundColor()) {
|
||||
imagealphablending($frame->native(), true);
|
||||
imagesetthickness($frame->native(), 0);
|
||||
imagefilledrectangle(
|
||||
$frame->native(),
|
||||
$position->x(),
|
||||
$position->y(),
|
||||
$position->x() + $this->drawable->width(),
|
||||
$position->y() + $this->drawable->height(),
|
||||
$this->driver()->colorProcessor($image)->export(
|
||||
$this->backgroundColor()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// draw border
|
||||
if ($this->drawable->hasBorder()) {
|
||||
imagealphablending($frame->native(), true);
|
||||
imagesetthickness($frame->native(), $this->drawable->borderSize());
|
||||
imagerectangle(
|
||||
$frame->native(),
|
||||
$position->x(),
|
||||
$position->y(),
|
||||
$position->x() + $this->drawable->width(),
|
||||
$position->y() + $this->drawable->height(),
|
||||
$this->driver()->colorProcessor($image)->export(
|
||||
$this->borderColor()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
71
vendor/intervention/image/src/Drivers/Gd/Modifiers/FillModifier.php
vendored
Normal file
71
vendor/intervention/image/src/Drivers/Gd/Modifiers/FillModifier.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ColorDecoderException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\FillModifier as GenericFillModifier;
|
||||
|
||||
class FillModifier extends GenericFillModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws ColorDecoderException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$color = $this->driver()->colorProcessor($image)->export(
|
||||
$this->color()
|
||||
);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
if ($this->hasPosition()) {
|
||||
$this->floodFillWithColor($frame, $color);
|
||||
} else {
|
||||
$this->fillAllWithColor($frame, $color);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ModifierException
|
||||
*/
|
||||
private function floodFillWithColor(FrameInterface $frame, int $color): void
|
||||
{
|
||||
imagefill(
|
||||
$frame->native(),
|
||||
$this->position->x(),
|
||||
$this->position->y(),
|
||||
$color
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ModifierException
|
||||
*/
|
||||
private function fillAllWithColor(FrameInterface $frame, int $color): void
|
||||
{
|
||||
imagealphablending($frame->native(), true);
|
||||
imagefilledrectangle(
|
||||
$frame->native(),
|
||||
0,
|
||||
0,
|
||||
$frame->size()->width() - 1,
|
||||
$frame->size()->height() - 1,
|
||||
$color
|
||||
);
|
||||
}
|
||||
}
|
||||
51
vendor/intervention/image/src/Drivers/Gd/Modifiers/FillTransparentAreasModifier.php
vendored
Normal file
51
vendor/intervention/image/src/Drivers/Gd/Modifiers/FillTransparentAreasModifier.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\FillTransparentAreasModifier as GenericFillTransparentAreasModifier;
|
||||
use Intervention\Image\Colors\Rgb\Color as RgbColor;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
|
||||
class FillTransparentAreasModifier extends GenericFillTransparentAreasModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$backgroundColor = $this->backgroundColor($this->driver())->toColorspace(RgbColorspace::class);
|
||||
|
||||
if (!$backgroundColor instanceof RgbColor) {
|
||||
throw new ModifierException('Failed to normalize background color to RGB color space');
|
||||
}
|
||||
|
||||
foreach ($image as $frame) {
|
||||
// create new canvas with background color as background
|
||||
$modified = Cloner::cloneBlended(
|
||||
$frame->native(),
|
||||
background: $backgroundColor
|
||||
);
|
||||
|
||||
// set new gd image
|
||||
$frame->setNative($modified);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
32
vendor/intervention/image/src/Drivers/Gd/Modifiers/FlipModifier.php
vendored
Normal file
32
vendor/intervention/image/src/Drivers/Gd/Modifiers/FlipModifier.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Direction;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\FlipModifier as GenericFlipModifier;
|
||||
|
||||
class FlipModifier extends GenericFlipModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$direction = $this->direction === Direction::HORIZONTAL ? IMG_FLIP_HORIZONTAL : IMG_FLIP_VERTICAL;
|
||||
|
||||
foreach ($image as $frame) {
|
||||
imageflip($frame->native(), $direction);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
29
vendor/intervention/image/src/Drivers/Gd/Modifiers/GammaModifier.php
vendored
Normal file
29
vendor/intervention/image/src/Drivers/Gd/Modifiers/GammaModifier.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\GammaModifier as GenericGammaModifier;
|
||||
|
||||
class GammaModifier extends GenericGammaModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
imagegammacorrect($frame->native(), 1, $this->gamma);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
34
vendor/intervention/image/src/Drivers/Gd/Modifiers/GrayscaleModifier.php
vendored
Normal file
34
vendor/intervention/image/src/Drivers/Gd/Modifiers/GrayscaleModifier.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\GrayscaleModifier as GenericGrayscaleModifier;
|
||||
|
||||
class GrayscaleModifier extends GenericGrayscaleModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
$result = imagefilter($frame->native(), IMG_FILTER_GRAYSCALE);
|
||||
if ($result === false) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', unable to transform image to grayscale',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
129
vendor/intervention/image/src/Drivers/Gd/Modifiers/InsertModifier.php
vendored
Normal file
129
vendor/intervention/image/src/Drivers/Gd/Modifiers/InsertModifier.php
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\RuntimeException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\PointInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\InsertModifier as GenericInsertModifier;
|
||||
use Intervention\Image\Traits\CanConvertRange;
|
||||
|
||||
class InsertModifier extends GenericInsertModifier implements SpecializedInterface
|
||||
{
|
||||
use CanConvertRange;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$watermark = $this->driver()->decodeImage($this->image);
|
||||
$position = $this->position($image, $watermark);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
imagealphablending($frame->native(), true);
|
||||
|
||||
if ($this->transparency === 1.0) {
|
||||
$this->insertOpaque($frame, $watermark, $position);
|
||||
} else {
|
||||
$this->insertTransparent($frame, $watermark, $position);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert watermark with 100% opacity
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
private function insertOpaque(FrameInterface $frame, ImageInterface $watermark, PointInterface $position): void
|
||||
{
|
||||
imagecopy(
|
||||
$frame->native(),
|
||||
$watermark->core()->native(),
|
||||
$position->x(),
|
||||
$position->y(),
|
||||
0,
|
||||
0,
|
||||
$watermark->width(),
|
||||
$watermark->height()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert watermark transparent with current transparency
|
||||
*
|
||||
* Unfortunately, the original PHP function imagecopymerge does not work reliably.
|
||||
* For example, any transparency of the image to be inserted is not applied correctly.
|
||||
* For this reason, a new GDImage is created into which the original image is inserted
|
||||
* in the first step and the watermark is inserted with 100% opacity in the second
|
||||
* step. This combination is then transferred to the original image again with the
|
||||
* respective opacity.
|
||||
*
|
||||
* Please note: Unfortunately, there is still an edge case, when a transparent image
|
||||
* is inserted on a transparent background, the "double" transparent areas appear opaque!
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
private function insertTransparent(FrameInterface $frame, ImageInterface $watermark, PointInterface $position): void
|
||||
{
|
||||
$cut = imagecreatetruecolor($watermark->width(), $watermark->height());
|
||||
|
||||
if ($cut === false) {
|
||||
throw new ModifierException('Failed to insert image');
|
||||
}
|
||||
|
||||
imagecopy(
|
||||
$cut,
|
||||
$frame->native(),
|
||||
0,
|
||||
0,
|
||||
$position->x(),
|
||||
$position->y(),
|
||||
imagesx($cut),
|
||||
imagesy($cut)
|
||||
);
|
||||
|
||||
imagecopy(
|
||||
$cut,
|
||||
$watermark->core()->native(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
imagesx($cut),
|
||||
imagesy($cut)
|
||||
);
|
||||
|
||||
try {
|
||||
$transparency = (int) round(self::convertRange($this->transparency, 0, 1, 0, 100));
|
||||
} catch (RuntimeException $e) {
|
||||
throw new ModifierException('Failed to convert transparency', previous: $e);
|
||||
}
|
||||
|
||||
imagecopymerge(
|
||||
$frame->native(),
|
||||
$cut,
|
||||
$position->x(),
|
||||
$position->y(),
|
||||
0,
|
||||
0,
|
||||
$watermark->width(),
|
||||
$watermark->height(),
|
||||
$transparency,
|
||||
);
|
||||
}
|
||||
}
|
||||
32
vendor/intervention/image/src/Drivers/Gd/Modifiers/InvertModifier.php
vendored
Normal file
32
vendor/intervention/image/src/Drivers/Gd/Modifiers/InvertModifier.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\InvertModifier as GenericInvertModifier;
|
||||
|
||||
class InvertModifier extends GenericInvertModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
$result = imagefilter($frame->native(), IMG_FILTER_NEGATE);
|
||||
if ($result === false) {
|
||||
throw new ModifierException('Failed to invert image colors');
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
62
vendor/intervention/image/src/Drivers/Gd/Modifiers/OrientModifier.php
vendored
Normal file
62
vendor/intervention/image/src/Drivers/Gd/Modifiers/OrientModifier.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\OrientModifier as GenericOrientModifier;
|
||||
|
||||
class OrientModifier extends GenericOrientModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$image = match ($this->orientation($image)) {
|
||||
2 => $image->flip(),
|
||||
3 => $image->rotate(180),
|
||||
4 => $image->rotate(180)->flip(),
|
||||
5 => $image->rotate(90)->flip(),
|
||||
6 => $image->rotate(90),
|
||||
7 => $image->rotate(270)->flip(),
|
||||
8 => $image->rotate(270),
|
||||
default => $image
|
||||
};
|
||||
|
||||
return $this->markAligned($image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return exif information about image orientation.
|
||||
*/
|
||||
private function orientation(ImageInterface $image): int
|
||||
{
|
||||
$orientation = $image->exif('IFD0.Orientation');
|
||||
|
||||
return is_numeric($orientation) ? (int) $orientation : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set exif data of image to top-left orientation, marking the image as
|
||||
* aligned and making sure the rotation correction process is not
|
||||
* performed again.
|
||||
*/
|
||||
private function markAligned(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$exif = $image->exif()->map(function ($item) {
|
||||
if (is_array($item) && array_key_exists('Orientation', $item)) {
|
||||
$item['Orientation'] = 1;
|
||||
return $item;
|
||||
}
|
||||
|
||||
return $item;
|
||||
});
|
||||
|
||||
return $image->setExif($exif);
|
||||
}
|
||||
}
|
||||
34
vendor/intervention/image/src/Drivers/Gd/Modifiers/PixelateModifier.php
vendored
Normal file
34
vendor/intervention/image/src/Drivers/Gd/Modifiers/PixelateModifier.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\PixelateModifier as GenericPixelateModifier;
|
||||
|
||||
class PixelateModifier extends GenericPixelateModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
foreach ($image as $frame) {
|
||||
$result = imagefilter($frame->native(), IMG_FILTER_PIXELATE, $this->size, true);
|
||||
if ($result === false) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', unable to process pixelation effect',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
27
vendor/intervention/image/src/Drivers/Gd/Modifiers/ProfileModifier.php
vendored
Normal file
27
vendor/intervention/image/src/Drivers/Gd/Modifiers/ProfileModifier.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ProfileModifier as GenericProfileModifier;
|
||||
|
||||
class ProfileModifier extends GenericProfileModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
'Color profiles are not supported by GD driver'
|
||||
);
|
||||
}
|
||||
}
|
||||
75
vendor/intervention/image/src/Drivers/Gd/Modifiers/ReduceColorsModifier.php
vendored
Normal file
75
vendor/intervention/image/src/Drivers/Gd/Modifiers/ReduceColorsModifier.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ReduceColorsModifier as GenericReduceColorsModifier;
|
||||
use Intervention\Image\Colors\Rgb\Color as RgbColor;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
|
||||
class ReduceColorsModifier extends GenericReduceColorsModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StateException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
if ($this->limit <= 0) {
|
||||
throw new InvalidArgumentException('Quantization limit must be greater than 0');
|
||||
}
|
||||
|
||||
// no color reduction if the limit is higher than the colors in the img
|
||||
$colorCount = imagecolorstotal($image->core()->native());
|
||||
if ($colorCount > 0 && $this->limit > $colorCount) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
$width = $image->width();
|
||||
$height = $image->height();
|
||||
$backgroundColor = $this->backgroundColor($image);
|
||||
|
||||
if (!$backgroundColor instanceof RgbColor) {
|
||||
throw new ModifierException('Failed to convert background color to RGB color space');
|
||||
}
|
||||
|
||||
$nativeBackgroundColor = $this->driver()
|
||||
->colorProcessor($image)
|
||||
->export($backgroundColor);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
// create new image for color quantization
|
||||
$reduced = Cloner::cloneEmpty($frame->native(), background: $backgroundColor);
|
||||
|
||||
// fill with background
|
||||
imagefill($reduced, 0, 0, $nativeBackgroundColor);
|
||||
|
||||
// set transparency
|
||||
imagecolortransparent($reduced, $nativeBackgroundColor);
|
||||
|
||||
// copy original image (colors are limited automatically in the copy process)
|
||||
imagecopy($reduced, $frame->native(), 0, 0, 0, 0, $width, $height);
|
||||
|
||||
// gd library does not support color quantization directly therefore the
|
||||
// colors are decrease by transforming the image to a palette version
|
||||
imagetruecolortopalette($reduced, true, $this->limit);
|
||||
|
||||
$frame->setNative($reduced);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
29
vendor/intervention/image/src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php
vendored
Normal file
29
vendor/intervention/image/src/Drivers/Gd/Modifiers/RemoveAnimationModifier.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\RemoveAnimationModifier as GenericRemoveAnimationModifier;
|
||||
|
||||
class RemoveAnimationModifier extends GenericRemoveAnimationModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$image->core()->setNative(
|
||||
$this->selectedFrame($image)->native()
|
||||
);
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
24
vendor/intervention/image/src/Drivers/Gd/Modifiers/RemoveProfileModifier.php
vendored
Normal file
24
vendor/intervention/image/src/Drivers/Gd/Modifiers/RemoveProfileModifier.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\RemoveProfileModifier as GenericRemoveProfileModifier;
|
||||
|
||||
class RemoveProfileModifier extends GenericRemoveProfileModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
// Color profiles are not supported by GD, so the decoded
|
||||
// image is already free of profiles anyway.
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
37
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResizeCanvasModifier.php
vendored
Normal file
37
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResizeCanvasModifier.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ResizeCanvasModifier as GenericResizeCanvasModifier;
|
||||
|
||||
class ResizeCanvasModifier extends GenericResizeCanvasModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StateException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$cropSize = $this->cropSize($image);
|
||||
|
||||
$image->modify(new CropModifier(
|
||||
$cropSize->width(),
|
||||
$cropSize->height(),
|
||||
$cropSize->pivot()->x(),
|
||||
$cropSize->pivot()->y(),
|
||||
$this->backgroundColor(),
|
||||
));
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
16
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResizeCanvasRelativeModifier.php
vendored
Normal file
16
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResizeCanvasRelativeModifier.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
|
||||
class ResizeCanvasRelativeModifier extends ResizeCanvasModifier
|
||||
{
|
||||
protected function cropSize(ImageInterface $image, bool $relative = false): SizeInterface
|
||||
{
|
||||
return parent::cropSize($image, true);
|
||||
}
|
||||
}
|
||||
16
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResizeDownModifier.php
vendored
Normal file
16
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResizeDownModifier.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
|
||||
class ResizeDownModifier extends ResizeModifier
|
||||
{
|
||||
protected function adjustedSize(ImageInterface $image): SizeInterface
|
||||
{
|
||||
return $image->size()->resizeDown($this->width, $this->height);
|
||||
}
|
||||
}
|
||||
73
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResizeModifier.php
vendored
Normal file
73
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResizeModifier.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ResizeModifier as GenericResizeModifier;
|
||||
|
||||
class ResizeModifier extends GenericResizeModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$resizeTo = $this->adjustedSize($image);
|
||||
foreach ($image as $frame) {
|
||||
$this->resizeFrame($frame, $resizeTo);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
*/
|
||||
private function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): void
|
||||
{
|
||||
// create empty canvas in target size
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $resizeTo);
|
||||
|
||||
// copy content from resource
|
||||
imagecopyresampled(
|
||||
$modified,
|
||||
$frame->native(),
|
||||
$resizeTo->pivot()->x(),
|
||||
$resizeTo->pivot()->y(),
|
||||
0,
|
||||
0,
|
||||
$resizeTo->width(),
|
||||
$resizeTo->height(),
|
||||
$frame->size()->width(),
|
||||
$frame->size()->height()
|
||||
);
|
||||
|
||||
// set new content as resource
|
||||
$frame->setNative($modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size the modifier will resize to
|
||||
*/
|
||||
protected function adjustedSize(ImageInterface $image): SizeInterface
|
||||
{
|
||||
return $image->size()->resize($this->width, $this->height);
|
||||
}
|
||||
}
|
||||
42
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResolutionModifier.php
vendored
Normal file
42
vendor/intervention/image/src/Drivers/Gd/Modifiers/ResolutionModifier.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\ResolutionModifier as GenericResolutionModifier;
|
||||
|
||||
class ResolutionModifier extends GenericResolutionModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$x = intval(round($this->x));
|
||||
$y = intval(round($this->y));
|
||||
|
||||
foreach ($image as $frame) {
|
||||
imageresolution($frame->native(), $x, $y);
|
||||
}
|
||||
|
||||
// GD returns 96x96 as resolution by default even if the image has no resolution.
|
||||
// This is problematic because it is impossible to tell whether the image
|
||||
// really has this resolution or whether it just corresponds to the default value.
|
||||
//
|
||||
// If the resolution was change to 96x96 (default resolution of GD) we mark
|
||||
// the resolution as changed to be able to distinguish it
|
||||
if ($x === 96 && $y === 96) {
|
||||
$image->core()->meta()->set('resolutionChanged', true);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
124
vendor/intervention/image/src/Drivers/Gd/Modifiers/RotateModifier.php
vendored
Normal file
124
vendor/intervention/image/src/Drivers/Gd/Modifiers/RotateModifier.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Alignment;
|
||||
use Intervention\Image\Colors\Rgb\Color as RgbColor;
|
||||
use Intervention\Image\Colors\Rgb\Colorspace as Rgb;
|
||||
use Intervention\Image\Drivers\Gd\Cloner;
|
||||
use Intervention\Image\Exceptions\DriverException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Geometry\Polygon;
|
||||
use Intervention\Image\Interfaces\ColorInterface;
|
||||
use Intervention\Image\Interfaces\FrameInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\RotateModifier as GenericRotateModifier;
|
||||
use Intervention\Image\Size;
|
||||
|
||||
class RotateModifier extends GenericRotateModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$background = $this->backgroundColor();
|
||||
|
||||
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
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ModifierException
|
||||
* @throws DriverException
|
||||
*/
|
||||
protected function modifyFrame(FrameInterface $frame, ColorInterface $background): void
|
||||
{
|
||||
// normalize color to rgb colorspace
|
||||
$background = $background->toColorspace(Rgb::class);
|
||||
|
||||
if (!$background instanceof RgbColor) {
|
||||
throw new ModifierException('Failed to normalize background color to RGB color space');
|
||||
}
|
||||
|
||||
// get transparent color from frame core
|
||||
$transparent = match ($transparent = imagecolortransparent($frame->native())) {
|
||||
-1 => imagecolorallocatealpha(
|
||||
$frame->native(),
|
||||
$background->red()->value(),
|
||||
$background->green()->value(),
|
||||
$background->blue()->value(),
|
||||
127
|
||||
),
|
||||
default => $transparent,
|
||||
};
|
||||
|
||||
// rotate original image against transparent background
|
||||
$rotated = imagerotate(
|
||||
$frame->native(),
|
||||
$this->rotationAngle() * -1,
|
||||
$transparent
|
||||
);
|
||||
|
||||
// create size from original after rotation
|
||||
$container = (new Size(
|
||||
imagesx($rotated),
|
||||
imagesy($rotated),
|
||||
))->movePivot(Alignment::CENTER);
|
||||
|
||||
// create size from original and rotate points
|
||||
$cutout = Polygon::fromSize(new Size(
|
||||
imagesx($frame->native()),
|
||||
imagesy($frame->native()),
|
||||
$container->pivot()
|
||||
))->alignHorizontally(Alignment::CENTER)
|
||||
->alignVertically(Alignment::CENTER)
|
||||
->rotate($this->rotationAngle());
|
||||
|
||||
// create new gd image
|
||||
$modified = Cloner::cloneEmpty($frame->native(), $container, $background);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
21
vendor/intervention/image/src/Drivers/Gd/Modifiers/ScaleDownModifier.php
vendored
Normal file
21
vendor/intervention/image/src/Drivers/Gd/Modifiers/ScaleDownModifier.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
|
||||
class ScaleDownModifier extends ResizeModifier
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ResizeModifier::adjustedSize()
|
||||
*/
|
||||
protected function adjustedSize(ImageInterface $image): SizeInterface
|
||||
{
|
||||
return $image->size()->scaleDown($this->width, $this->height);
|
||||
}
|
||||
}
|
||||
16
vendor/intervention/image/src/Drivers/Gd/Modifiers/ScaleModifier.php
vendored
Normal file
16
vendor/intervention/image/src/Drivers/Gd/Modifiers/ScaleModifier.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SizeInterface;
|
||||
|
||||
class ScaleModifier extends ResizeModifier
|
||||
{
|
||||
protected function adjustedSize(ImageInterface $image): SizeInterface
|
||||
{
|
||||
return $image->size()->scale($this->width, $this->height);
|
||||
}
|
||||
}
|
||||
53
vendor/intervention/image/src/Drivers/Gd/Modifiers/SharpenModifier.php
vendored
Normal file
53
vendor/intervention/image/src/Drivers/Gd/Modifiers/SharpenModifier.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\SharpenModifier as GenericSharpenModifier;
|
||||
|
||||
class SharpenModifier extends GenericSharpenModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$matrix = $this->matrix();
|
||||
foreach ($image as $frame) {
|
||||
$result = imageconvolution($frame->native(), $matrix, 1, 0);
|
||||
if ($result === false) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', unable to set convolution matrix',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create matrix to be used by imageconvolution()
|
||||
*
|
||||
* @return array<array<float>>
|
||||
*/
|
||||
private function matrix(): array
|
||||
{
|
||||
$min = $this->level >= 10 ? $this->level * -0.01 : 0;
|
||||
$max = $this->level * -0.025;
|
||||
$abs = ((4 * $min + 4 * $max) * -1) + 1;
|
||||
|
||||
return [
|
||||
[$min, $max, $min],
|
||||
[$max, $abs, $max],
|
||||
[$min, $max, $min]
|
||||
];
|
||||
}
|
||||
}
|
||||
31
vendor/intervention/image/src/Drivers/Gd/Modifiers/SliceAnimationModifier.php
vendored
Normal file
31
vendor/intervention/image/src/Drivers/Gd/Modifiers/SliceAnimationModifier.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\SliceAnimationModifier as GenericSliceAnimationModifier;
|
||||
|
||||
class SliceAnimationModifier extends GenericSliceAnimationModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
if ($this->offset >= $image->count()) {
|
||||
throw new InvalidArgumentException('Offset is not in the range of frames');
|
||||
}
|
||||
|
||||
$image->core()->slice($this->offset, $this->length);
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
135
vendor/intervention/image/src/Drivers/Gd/Modifiers/TextModifier.php
vendored
Normal file
135
vendor/intervention/image/src/Drivers/Gd/Modifiers/TextModifier.php
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\TextModifier as GenericTextModifier;
|
||||
|
||||
class TextModifier extends GenericTextModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws ModifierException
|
||||
* @throws StateException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
$fontProcessor = $this->driver()->fontProcessor();
|
||||
$lines = $fontProcessor->textBlock($this->text, $this->font, $this->position);
|
||||
|
||||
// decode text colors
|
||||
$textColor = $this->gdTextColor($image);
|
||||
$strokeColor = $this->gdStrokeColor($image);
|
||||
|
||||
foreach ($image as $frame) {
|
||||
imagealphablending($frame->native(), true);
|
||||
if ($this->font->hasFile()) {
|
||||
foreach ($lines as $line) {
|
||||
foreach ($this->strokeOffsets($this->font) as $offset) {
|
||||
imagettftext(
|
||||
image: $frame->native(),
|
||||
size: $fontProcessor->nativeFontSize($this->font),
|
||||
angle: $this->font->angle() * -1,
|
||||
x: $line->position()->x() + $offset->x(),
|
||||
y: $line->position()->y() + $offset->y(),
|
||||
color: $strokeColor,
|
||||
font_filename: $this->font->filepath(),
|
||||
text: (string) $line
|
||||
);
|
||||
}
|
||||
|
||||
imagettftext(
|
||||
image: $frame->native(),
|
||||
size: $fontProcessor->nativeFontSize($this->font),
|
||||
angle: $this->font->angle() * -1,
|
||||
x: $line->position()->x(),
|
||||
y: $line->position()->y(),
|
||||
color: $textColor,
|
||||
font_filename: $this->font->filepath(),
|
||||
text: (string) $line
|
||||
);
|
||||
}
|
||||
} else {
|
||||
foreach ($lines as $line) {
|
||||
foreach ($this->strokeOffsets($this->font) as $offset) {
|
||||
imagestring(
|
||||
image: $frame->native(),
|
||||
font: $this->gdFont(),
|
||||
x: $line->position()->x() + $offset->x(),
|
||||
y: $line->position()->y() + $offset->y(),
|
||||
string: (string) $line,
|
||||
color: $strokeColor
|
||||
);
|
||||
}
|
||||
|
||||
imagestring(
|
||||
image: $frame->native(),
|
||||
font: $this->gdFont(),
|
||||
x: $line->position()->x(),
|
||||
y: $line->position()->y(),
|
||||
string: (string) $line,
|
||||
color: $textColor
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode text color in GD compatible format
|
||||
*
|
||||
* @throws StateException
|
||||
*/
|
||||
protected function gdTextColor(ImageInterface $image): int
|
||||
{
|
||||
return $this
|
||||
->driver()
|
||||
->colorProcessor($image)
|
||||
->export(parent::textColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode color for stroke (outline) effect in GD compatible format
|
||||
*
|
||||
* @throws StateException
|
||||
*/
|
||||
protected function gdStrokeColor(ImageInterface $image): int
|
||||
{
|
||||
if (!$this->font->hasStrokeEffect()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$color = parent::strokeColor();
|
||||
|
||||
if ($color->isTransparent()) {
|
||||
throw new StateException('The stroke color must be fully opaque');
|
||||
}
|
||||
|
||||
return $this
|
||||
->driver()
|
||||
->colorProcessor($image)
|
||||
->export($color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return GD's internal font size
|
||||
*/
|
||||
private function gdFont(): int
|
||||
{
|
||||
if (!in_array($this->font->size(), range(1, 5))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (int) $this->font->size();
|
||||
}
|
||||
}
|
||||
111
vendor/intervention/image/src/Drivers/Gd/Modifiers/TrimModifier.php
vendored
Normal file
111
vendor/intervention/image/src/Drivers/Gd/Modifiers/TrimModifier.php
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Exceptions\ModifierException;
|
||||
use Intervention\Image\Exceptions\NotSupportedException;
|
||||
use Intervention\Image\Exceptions\StateException;
|
||||
use Intervention\Image\Geometry\Point;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Modifiers\TrimModifier as GenericTrimModifier;
|
||||
use ValueError;
|
||||
|
||||
class TrimModifier extends GenericTrimModifier implements SpecializedInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ModifierInterface::apply()
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
* @throws StateException
|
||||
* @throws ModifierException
|
||||
*/
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
if ($image->isAnimated()) {
|
||||
throw new NotSupportedException('Trim modifier cannot be applied to animated images');
|
||||
}
|
||||
|
||||
// apply tolerance with a min. value of .5 because the default tolerance of '0' should
|
||||
// already trim away similar colors which is not the case with imagecropauto.
|
||||
$trimmed = imagecropauto(
|
||||
$image->core()->native(),
|
||||
IMG_CROP_THRESHOLD,
|
||||
max([.5, $this->tolerance / 10]),
|
||||
$this->trimColor($image)
|
||||
);
|
||||
|
||||
// if the tolerance is very high, it is possible that no image is left.
|
||||
// imagick returns a 1x1 pixel image in this case. this does the same.
|
||||
if ($trimmed === false) {
|
||||
$trimmed = $this->driver()->createImage(1, 1)->core()->native();
|
||||
}
|
||||
|
||||
$image->core()->setNative($trimmed);
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an average color from the colors of the four corner points of the given image
|
||||
*
|
||||
* @throws ModifierException
|
||||
*/
|
||||
private function trimColor(ImageInterface $image): int
|
||||
{
|
||||
// trim color base
|
||||
$red = 0;
|
||||
$green = 0;
|
||||
$blue = 0;
|
||||
|
||||
// corner coordinates
|
||||
$size = $image->size();
|
||||
$cornerPoints = [
|
||||
new Point(0, 0),
|
||||
new Point($size->width() - 1, 0),
|
||||
new Point(0, $size->height() - 1),
|
||||
new Point($size->width() - 1, $size->height() - 1),
|
||||
];
|
||||
|
||||
// create an average color to be used in trim operation
|
||||
foreach ($cornerPoints as $pos) {
|
||||
$cornerColor = imagecolorat($image->core()->native(), $pos->x(), $pos->y());
|
||||
|
||||
if ($cornerColor === false) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', unable to determine average color for process',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$rgb = imagecolorsforindex($image->core()->native(), $cornerColor);
|
||||
} catch (ValueError) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', unable to read trim color from index',
|
||||
);
|
||||
}
|
||||
|
||||
$red += round(round($rgb['red'] / 51) * 51);
|
||||
$green += round(round($rgb['green'] / 51) * 51);
|
||||
$blue += round(round($rgb['blue'] / 51) * 51);
|
||||
}
|
||||
|
||||
$red = (int) round($red / 4);
|
||||
$green = (int) round($green / 4);
|
||||
$blue = (int) round($blue / 4);
|
||||
|
||||
$color = imagecolorallocate($image->core()->native(), $red, $green, $blue);
|
||||
|
||||
if ($color === false) {
|
||||
throw new ModifierException(
|
||||
'Failed to apply ' . self::class . ', unable to allocate trim color',
|
||||
);
|
||||
}
|
||||
|
||||
return $color;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user