mirror of
https://github.com/getgrav/grav.git
synced 2026-05-07 08:57:06 +02:00
Start using the new Image classes in Media
This commit is contained in:
@@ -45,8 +45,6 @@
|
||||
"filp/whoops": "~2.9",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "~1.25",
|
||||
"getgrav/image": "^3.0",
|
||||
"getgrav/cache": "^2.0",
|
||||
"donatj/phpuseragentparser": "~1.1",
|
||||
"pimple/pimple": "~3.5.0",
|
||||
"rockettheme/toolbox": "~1.5",
|
||||
|
||||
147
composer.lock
generated
147
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1067938388862c52927c6450e8a36df0",
|
||||
"content-hash": "ddf2ee1968a5086f59be53180004e557",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/ca-bundle",
|
||||
@@ -617,117 +617,6 @@
|
||||
],
|
||||
"time": "2022-01-07T12:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "getgrav/cache",
|
||||
"version": "v2.0.0",
|
||||
"target-dir": "Gregwar/Cache",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getgrav/Cache.git",
|
||||
"reference": "56fd63f752779928fcd1074ab7d12f406dde8861"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getgrav/Cache/zipball/56fd63f752779928fcd1074ab7d12f406dde8861",
|
||||
"reference": "56fd63f752779928fcd1074ab7d12f406dde8861",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Gregwar\\Cache": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gregwar",
|
||||
"email": "g.passault@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Grav CMS",
|
||||
"email": "hello@getgrav.org",
|
||||
"homepage": "https://getgrav.org"
|
||||
}
|
||||
],
|
||||
"description": "A lightweight file-system cache system",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"caching",
|
||||
"file-system",
|
||||
"system"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/getgrav/Cache/tree/v2.0.0"
|
||||
},
|
||||
"time": "2021-04-20T05:48:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "getgrav/image",
|
||||
"version": "v3.0.0",
|
||||
"target-dir": "Gregwar/Image",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getgrav/Image.git",
|
||||
"reference": "02c1bb2c179dd894c4f6610c9c49da364ee7d264"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getgrav/Image/zipball/02c1bb2c179dd894c4f6610c9c49da364ee7d264",
|
||||
"reference": "02c1bb2c179dd894c4f6610c9c49da364ee7d264",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-gd": "*",
|
||||
"getgrav/cache": "^2.0",
|
||||
"php": "^5.6 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"sllh/php-cs-fixer-styleci-bridge": "~1.0",
|
||||
"symfony/phpunit-bridge": "^2.7.4 || ^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"behat/transliterator": "Transliterator provides ability to set non-latin1 pretty names"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Gregwar\\Image": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Grégoire Passault",
|
||||
"email": "g.passault@gmail.com",
|
||||
"homepage": "http://www.gregwar.com/"
|
||||
},
|
||||
{
|
||||
"name": "Grav CMS",
|
||||
"email": "hello@getgrav.org",
|
||||
"homepage": "https://getgrav.org"
|
||||
}
|
||||
],
|
||||
"description": "Image handling",
|
||||
"homepage": "https://github.com/Gregwar/Image",
|
||||
"keywords": [
|
||||
"gd",
|
||||
"image"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/getgrav/Image/tree/v3.0.0"
|
||||
},
|
||||
"time": "2021-04-20T05:50:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "1.8.3",
|
||||
@@ -4126,12 +4015,12 @@
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -4748,16 +4637,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.11",
|
||||
"version": "9.2.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "665a1ac0a763c51afc30d6d130dac0813092b17f"
|
||||
"reference": "deac8540cb7bd40b2b8cfa679b76202834fd04e8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/665a1ac0a763c51afc30d6d130dac0813092b17f",
|
||||
"reference": "665a1ac0a763c51afc30d6d130dac0813092b17f",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/deac8540cb7bd40b2b8cfa679b76202834fd04e8",
|
||||
"reference": "deac8540cb7bd40b2b8cfa679b76202834fd04e8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4813,7 +4702,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.11"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.13"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4821,7 +4710,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-18T12:46:09+00:00"
|
||||
"time": "2022-02-23T17:02:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@@ -5066,16 +4955,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.14",
|
||||
"version": "9.5.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "1883687169c017d6ae37c58883ca3994cfc34189"
|
||||
"reference": "5ff8c545a50226c569310a35f4fa89d79f1ddfdc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1883687169c017d6ae37c58883ca3994cfc34189",
|
||||
"reference": "1883687169c017d6ae37c58883ca3994cfc34189",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5ff8c545a50226c569310a35f4fa89d79f1ddfdc",
|
||||
"reference": "5ff8c545a50226c569310a35f4fa89d79f1ddfdc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5091,7 +4980,7 @@
|
||||
"phar-io/version": "^3.0.2",
|
||||
"php": ">=7.3",
|
||||
"phpspec/prophecy": "^1.12.1",
|
||||
"phpunit/php-code-coverage": "^9.2.7",
|
||||
"phpunit/php-code-coverage": "^9.2.13",
|
||||
"phpunit/php-file-iterator": "^3.0.5",
|
||||
"phpunit/php-invoker": "^3.1.1",
|
||||
"phpunit/php-text-template": "^2.0.3",
|
||||
@@ -5153,7 +5042,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.14"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.16"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5165,7 +5054,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-18T12:54:07+00:00"
|
||||
"time": "2022-02-23T17:10:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
|
||||
@@ -10,17 +10,15 @@
|
||||
namespace Grav\Common\Media\Traits;
|
||||
|
||||
use BadFunctionCallException;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Media\Interfaces\ImageMediaInterface;
|
||||
use Grav\Common\Page\Medium\ImageFile;
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Medium\MediumFactory;
|
||||
use Grav\Framework\File\Formatter\JsonFormatter;
|
||||
use Grav\Framework\File\JsonFile;
|
||||
use Grav\Framework\Image\Adapter\GdAdapter;
|
||||
use Grav\Framework\Image\Image;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use function array_key_exists;
|
||||
use function extension_loaded;
|
||||
use function func_num_args;
|
||||
use function function_exists;
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
@@ -29,15 +27,13 @@ use function in_array;
|
||||
*/
|
||||
trait ImageMediaTrait
|
||||
{
|
||||
/** @var ImageFile|null */
|
||||
/** @var Image|null */
|
||||
protected $image;
|
||||
/** @var string */
|
||||
protected $format = 'guess';
|
||||
/** @var int */
|
||||
protected $quality;
|
||||
/** @var bool */
|
||||
protected $debug_watermarked = false;
|
||||
/** @var bool */
|
||||
protected $watermark;
|
||||
|
||||
/** @var array */
|
||||
@@ -101,9 +97,6 @@ trait ImageMediaTrait
|
||||
public function setImagePrettyName($name)
|
||||
{
|
||||
$this->set('prettyname', $name);
|
||||
if ($this->image) {
|
||||
$this->image->setPrettyName($name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,8 +104,9 @@ trait ImageMediaTrait
|
||||
*/
|
||||
public function getImagePrettyName()
|
||||
{
|
||||
if ($this->get('prettyname')) {
|
||||
return $this->get('prettyname');
|
||||
$prettyName = $this->get('prettyname');
|
||||
if ($prettyName) {
|
||||
return $prettyName;
|
||||
}
|
||||
|
||||
$basename = $this->get('basename');
|
||||
@@ -149,55 +143,46 @@ trait ImageMediaTrait
|
||||
*/
|
||||
public function derivatives($min_width, $max_width = 2500, $step = 200)
|
||||
{
|
||||
if (!empty($this->alternatives)) {
|
||||
// Get the largest image to be the base.
|
||||
if (empty($this->alternatives)) {
|
||||
$base = $this;
|
||||
} else {
|
||||
$max = max(array_keys($this->alternatives));
|
||||
$base = $this->alternatives[$max];
|
||||
} else {
|
||||
$base = $this;
|
||||
}
|
||||
|
||||
$widths = [];
|
||||
$baseWidth = $base->get('width');
|
||||
$filepath = $base->get('filepath');
|
||||
|
||||
$widths = [];
|
||||
if (func_num_args() === 1) {
|
||||
foreach ((array) func_get_arg(0) as $width) {
|
||||
if ($width < $base->get('width')) {
|
||||
$widths[] = $width;
|
||||
foreach ((array) $min_width as $width) {
|
||||
if ($width < $baseWidth) {
|
||||
$widths[] = (int)$width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$max_width = min($max_width, $base->get('width'));
|
||||
|
||||
$max_width = min($max_width, $baseWidth);
|
||||
for ($width = $min_width; $width < $max_width; $width += $step) {
|
||||
$widths[] = $width;
|
||||
$widths[] = (int)$width;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($widths as $width) {
|
||||
// Only generate image alternatives that don't already exist
|
||||
if (array_key_exists((int) $width, $this->alternatives)) {
|
||||
if (isset($this->alternatives[$width])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$derivative = MediumFactory::fromFile($base->get('filepath'));
|
||||
|
||||
// It's possible that MediumFactory::fromFile returns null if the
|
||||
// original image file no longer exists and this class instance was
|
||||
// retrieved from the page cache
|
||||
$derivative = $this->getMedia()->createFromFile($filepath);
|
||||
if (null !== $derivative) {
|
||||
$index = 2;
|
||||
$alt_widths = array_keys($this->alternatives);
|
||||
sort($alt_widths);
|
||||
|
||||
foreach ($alt_widths as $i => $key) {
|
||||
if ($width > $key) {
|
||||
$index += max($i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$basename = preg_replace('/(@\d+x)?$/', "@{$width}w", $base->get('basename'), 1);
|
||||
$derivative->setImagePrettyName($basename);
|
||||
|
||||
$ratio = $base->get('width') / $width;
|
||||
$ratio = $baseWidth / $width;
|
||||
$height = $derivative->get('height') / $ratio;
|
||||
|
||||
$derivative->resize($width, $height);
|
||||
@@ -263,7 +248,7 @@ trait ImageMediaTrait
|
||||
* Set or get sizes parameter for srcset media action
|
||||
*
|
||||
* @param string|null $sizes
|
||||
* @return string
|
||||
* @return string|$this
|
||||
*/
|
||||
public function sizes($sizes = null)
|
||||
{
|
||||
@@ -330,9 +315,9 @@ trait ImageMediaTrait
|
||||
*/
|
||||
public function filter($filter = 'image.filters.default')
|
||||
{
|
||||
$filters = (array) $this->get($filter, []);
|
||||
$filters = (array)$this->get($filter, []);
|
||||
foreach ($filters as $params) {
|
||||
$params = (array) $params;
|
||||
$params = (array)$params;
|
||||
$method = array_shift($params);
|
||||
$this->__call($method, $params);
|
||||
}
|
||||
@@ -368,15 +353,13 @@ trait ImageMediaTrait
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function cropZoom()
|
||||
public function cropZoom(...$args)
|
||||
{
|
||||
$this->__call('zoomCrop', func_get_args());
|
||||
$this->__call('zoomCrop', $args);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string|null $image
|
||||
* @param string|null $position
|
||||
@@ -385,6 +368,8 @@ trait ImageMediaTrait
|
||||
*/
|
||||
public function watermark($image = null, $position = null, $scale = null)
|
||||
{
|
||||
// TODO:
|
||||
/*
|
||||
$grav = $this->getGrav();
|
||||
|
||||
$locator = $grav['locator'];
|
||||
@@ -440,6 +425,7 @@ trait ImageMediaTrait
|
||||
}
|
||||
|
||||
$this->__call('merge', [$watermark,$positionX, $positionY]);
|
||||
*/
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -451,7 +437,9 @@ trait ImageMediaTrait
|
||||
*/
|
||||
public function addFrame(int $border = 10, string $color = '0x000000')
|
||||
{
|
||||
if($border > 0 && preg_match('/^0x[a-f0-9]{6}$/i', $color)) { // $border must be an integer and bigger than 0; $color must be formatted as an HEX value (0x??????).
|
||||
// TODO:
|
||||
/*
|
||||
if( $border > 0 && preg_match('/^0x[a-f0-9]{6}$/i', $color)) { // $border must be an integer and bigger than 0; $color must be formatted as an HEX value (0x??????).
|
||||
$image = ImageFile::fromData($this->readFile());
|
||||
}
|
||||
else {
|
||||
@@ -470,6 +458,7 @@ trait ImageMediaTrait
|
||||
$this->__call('merge', [$image, $border, $border]);
|
||||
|
||||
$this->saveImage();
|
||||
*/
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -525,30 +514,14 @@ trait ImageMediaTrait
|
||||
*/
|
||||
protected function image()
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$filepath = $this->filepath;
|
||||
$webroot = preg_quote(GRAV_WEBROOT, '`');
|
||||
$root = preg_quote(GRAV_WEBROOT, '`');
|
||||
$filepath = preg_replace(['`^' . $webroot . '/`u', '`^' . $root . '/`u'], ['GRAV_WEBROOT/', 'GRAV_ROOT/'], $filepath);
|
||||
|
||||
// Use existing cache folder or if it doesn't exist, create it.
|
||||
$cacheDir = $locator->findResource('cache://images', true) ?: $locator->findResource('cache://images', true, true);
|
||||
|
||||
// Make sure we free previous image.
|
||||
unset($this->image);
|
||||
|
||||
$this->image = ImageFile::fromData($this->readFile());
|
||||
$this->image
|
||||
->setCacheDir($cacheDir)
|
||||
->setActualCacheDir($cacheDir)
|
||||
->setPrettyName($this->getImagePrettyName());
|
||||
|
||||
// Fix orientation if enabled
|
||||
/** @var Config $config */
|
||||
$config = Grav::instance()['config'];
|
||||
if ($config->get('system.images.auto_fix_orientation', false) &&
|
||||
extension_loaded('exif') && function_exists('exif_read_data')) {
|
||||
$this->image->fixOrientation();
|
||||
}
|
||||
|
||||
$this->watermark = $config->get('system.images.watermark.watermark_all', false);
|
||||
// Create a new image.
|
||||
$this->image = new Image($filepath, $this->getItems());
|
||||
$this->image->fixOrientation();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -570,27 +543,102 @@ trait ImageMediaTrait
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
if ($this->format === 'guess') {
|
||||
$extension = strtolower($this->get('extension'));
|
||||
$this->format($extension);
|
||||
}
|
||||
|
||||
if (!$this->debug_watermarked && $this->get('debug')) {
|
||||
$ratio = $this->get('ratio');
|
||||
if (!$ratio) {
|
||||
$ratio = 1;
|
||||
}
|
||||
if ($this->get('debug')) {
|
||||
$ratio = min(1, (int)$this->get('ratio'));
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$overlay = $locator->findResource("system://assets/responsive-overlays/{$ratio}x.png") ?: $locator->findResource('system://assets/responsive-overlays/unknown.png');
|
||||
$this->image->merge(ImageFile::open($overlay));
|
||||
|
||||
// FIXME
|
||||
$info = [
|
||||
'modified' => 0,
|
||||
'size' => 0,
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
];
|
||||
|
||||
$overlayImage = new Image($overlay, $info);
|
||||
|
||||
$this->image->merge($overlayImage);
|
||||
}
|
||||
|
||||
if ($this->watermark) {
|
||||
$this->watermark();
|
||||
}
|
||||
|
||||
return $this->image->cacheFile($this->format, $this->quality, false, [$this->get('width'), $this->get('height'), $this->get('modified')]);
|
||||
return $this->generateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCache(): string
|
||||
{
|
||||
$quality = $this->quality;
|
||||
$format = $this->format;
|
||||
if ($format === 'guess') {
|
||||
$extension = strtolower($this->get('extension'));
|
||||
$format = $extension;
|
||||
}
|
||||
|
||||
$image = $this->image;
|
||||
$image->extra['format'] = $format;
|
||||
$image->extra['quality'] = $quality;
|
||||
|
||||
$data = $image->jsonSerialize();
|
||||
$hash = $data['hash'];
|
||||
$d1 = substr($hash, 0, 2);
|
||||
$d2 = substr($hash, 2, 2);
|
||||
$d3 = substr($hash, 4);
|
||||
$prettyName = $this->getImagePrettyName();
|
||||
|
||||
$imageFile = "cache://images/{$d1}/{$d2}/{$d3}/{$prettyName}.{$format}";
|
||||
$cacheFile = "{$imageFile}.json";
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->getGrav()['locator'];
|
||||
$imageFile = '/' . $locator->getResource($imageFile, false);
|
||||
$cacheFile = $locator->getResource($cacheFile, true);
|
||||
|
||||
$file = $this->getCacheMetaFile($cacheFile);
|
||||
if (!$file->exists()) {
|
||||
$file->save($data);
|
||||
} else {
|
||||
$file->touch();
|
||||
}
|
||||
|
||||
return $this->generateCacheImage(GRAV_WEBROOT . '/' . $imageFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filepath
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCacheImage(string $filepath): string
|
||||
{
|
||||
if (file_exists($filepath)) {
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
$adapter = GdAdapter::createFromString($this->readFile());
|
||||
|
||||
$image = $this->image;
|
||||
$image->setAdapter($adapter);
|
||||
$image->save($filepath, 'jpg', $this->quality);
|
||||
$image->freeAdapter();
|
||||
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filepath
|
||||
* @return JsonFile
|
||||
*/
|
||||
protected function getCacheMetaFile(string $filepath): JsonFile
|
||||
{
|
||||
$formatter = new JsonFormatter(['encode_options' => JSON_PRETTY_PRINT]);
|
||||
|
||||
return new JsonFile($filepath, $formatter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use Grav\Common\Data\Data;
|
||||
use Grav\Common\Media\Interfaces\MediaFileInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaLinkInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaObjectInterface;
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Medium\ThumbnailImageMedium;
|
||||
use Grav\Common\Utils;
|
||||
use RuntimeException;
|
||||
@@ -553,9 +554,9 @@ trait MediaObjectTrait
|
||||
/**
|
||||
* Get the thumbnail Medium object
|
||||
*
|
||||
* @return ThumbnailImageMedium
|
||||
* @return ThumbnailImageMedium|ImageMedium
|
||||
*/
|
||||
protected function getThumbnail(): ThumbnailImageMedium
|
||||
protected function getThumbnail(): ImageMedium
|
||||
{
|
||||
if (null === $this->_thumbnail) {
|
||||
$thumbnails = (array)$this->get('thumbnails') + ['system' => 'system://images/media/thumb.png'];
|
||||
|
||||
@@ -583,6 +583,7 @@ abstract class AbstractMedia implements ExportInterface, MediaCollectionInterfac
|
||||
}
|
||||
|
||||
$pathInfo = Utils::pathinfo($filename);
|
||||
$info['basename'] = $pathInfo['filename'];
|
||||
$info['filename'] = $pathInfo['basename'];
|
||||
if (!isset($info['path'])) {
|
||||
$info['path'] = $pathInfo['dirname'] === '.' ? $this->getPath() : $pathInfo['dirname'];
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Page
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Exception;
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Gregwar\Image\Exceptions\GenerationError;
|
||||
use Gregwar\Image\Image;
|
||||
use Gregwar\Image\Source;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
use function extension_loaded;
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* Class ImageFile
|
||||
* @package Grav\Common\Page\Medium
|
||||
*
|
||||
* @method Image applyExifOrientation($exif_orienation)
|
||||
*/
|
||||
class ImageFile extends Image
|
||||
{
|
||||
/**
|
||||
* Destruct also image object.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$adapter = $this->adapter;
|
||||
if ($adapter) {
|
||||
$adapter->deinit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear previously applied operations
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearOperations()
|
||||
{
|
||||
$this->operations = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the same as the Gregwar Image class except this one fires a Grav Event on creation of new cached file
|
||||
*
|
||||
* @param string $type the image type
|
||||
* @param int $quality the quality (for JPEG)
|
||||
* @param bool $actual
|
||||
* @param array $extras
|
||||
* @return string
|
||||
*/
|
||||
public function cacheFile($type = 'jpg', $quality = 80, $actual = false, $extras = [])
|
||||
{
|
||||
if ($type === 'guess') {
|
||||
$type = $this->guessType();
|
||||
}
|
||||
|
||||
if (!$this->forceCache && !count($this->operations) && $type === $this->guessType()) {
|
||||
return $this->getFilename($this->getFilePath());
|
||||
}
|
||||
|
||||
// Computes the hash
|
||||
$this->hash = $this->getHash($type, $quality, $extras);
|
||||
|
||||
/** @var Config $config */
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
// Seo friendly image names
|
||||
$seofriendly = $config->get('system.images.seofriendly', false);
|
||||
|
||||
if ($seofriendly) {
|
||||
$mini_hash = substr($this->hash, 0, 4) . substr($this->hash, -4);
|
||||
$cacheFile = "{$this->prettyName}-{$mini_hash}";
|
||||
} else {
|
||||
$cacheFile = "{$this->hash}-{$this->prettyName}";
|
||||
}
|
||||
|
||||
$cacheFile .= '.' . $type;
|
||||
|
||||
// If the files does not exists, save it
|
||||
$image = $this;
|
||||
|
||||
// Target file should be younger than all the current image
|
||||
// dependencies
|
||||
$conditions = array(
|
||||
'younger-than' => $this->getDependencies()
|
||||
);
|
||||
|
||||
// The generating function
|
||||
$generate = function ($target) use ($image, $type, $quality) {
|
||||
$result = $image->save($target, $type, $quality);
|
||||
|
||||
if ($result !== $target) {
|
||||
throw new GenerationError($result);
|
||||
}
|
||||
|
||||
Grav::instance()->fireEvent('onImageMediumSaved', new Event(['image' => $target]));
|
||||
};
|
||||
|
||||
// Asking the cache for the cacheFile
|
||||
try {
|
||||
$perms = $config->get('system.images.cache_perms', '0755');
|
||||
$perms = octdec($perms);
|
||||
$file = $this->getCacheSystem()->setDirectoryMode($perms)->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
|
||||
} catch (GenerationError $e) {
|
||||
$file = $e->getNewFile();
|
||||
}
|
||||
|
||||
// Nulling the resource
|
||||
$adapter = $this->getAdapter();
|
||||
$adapter->setSource(new Source\File($file));
|
||||
$adapter->deinit();
|
||||
|
||||
if ($actual) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return $this->getFilename($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hash.
|
||||
*
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @param array $extras
|
||||
* @return string
|
||||
*/
|
||||
public function getHash($type = 'guess', $quality = 80, $extras = [])
|
||||
{
|
||||
if (null === $this->hash) {
|
||||
$this->generateHash($type, $quality, $extras);
|
||||
}
|
||||
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the hash.
|
||||
*
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @param array $extras
|
||||
*/
|
||||
public function generateHash($type = 'guess', $quality = 80, $extras = [])
|
||||
{
|
||||
$inputInfos = $this->source->getInfos();
|
||||
|
||||
$data = [
|
||||
$inputInfos,
|
||||
$this->serializeOperations(),
|
||||
$type,
|
||||
$quality,
|
||||
$extras
|
||||
];
|
||||
|
||||
$this->hash = sha1(serialize($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read exif rotation from file and apply it.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fixOrientation()
|
||||
{
|
||||
$info = $this->source->getInfos();
|
||||
if (!\is_string($info) || !file_exists($info)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (!extension_loaded('exif')) {
|
||||
throw new RuntimeException('You need to EXIF PHP Extension to use this function');
|
||||
}
|
||||
|
||||
if (!in_array(exif_imagetype($info), [IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM], true)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// resolve any streams
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$filepath = $this->source->getInfos();
|
||||
if ($locator->isStream($filepath)) {
|
||||
$filepath = $locator->findResource($info, true, true);
|
||||
}
|
||||
|
||||
// Make sure file exists
|
||||
if (!file_exists($filepath)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
try {
|
||||
$exif = @exif_read_data($filepath);
|
||||
} catch (Exception $e) {
|
||||
Grav::instance()['log']->error($filepath . ' - ' . $e->getMessage());
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($exif === false || !array_key_exists('Orientation', $exif)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->applyExifOrientation($exif['Orientation']);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
/** @var array */
|
||||
protected $defaults = [];
|
||||
/** @var array */
|
||||
protected $options = [];
|
||||
protected $imageSettings = [];
|
||||
/** @var string|null */
|
||||
private $saved_image_path;
|
||||
|
||||
@@ -47,6 +47,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
/** @var Config $config */
|
||||
$config = $this->getGrav()['config'];
|
||||
|
||||
$this->watermark = $config->get('system.images.watermark.watermark_all', false);
|
||||
$this->thumbnailTypes = ['page', 'media', 'default'];
|
||||
$this->defaults = [
|
||||
'quality' => (int)$config->get('system.images.default_image_quality', 85),
|
||||
@@ -54,7 +55,6 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
'auto_sizes' => (bool)$config->get('system.images.cls.auto_sizes', false),
|
||||
'aspect_ratio' => (bool)$config->get('system.images.cls.aspect_ratio', false),
|
||||
'retina_scale' => (int)$config->get('system.images.cls.retina_scale', 1)
|
||||
|
||||
];
|
||||
|
||||
parent::__construct($items, $blueprint);
|
||||
@@ -64,7 +64,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
$path = $this->get('filepath');
|
||||
$this->set('thumbnails.media', $path);
|
||||
|
||||
// TODO:
|
||||
// TODO: We shouldn't have this here
|
||||
$exists = $path && file_exists($path) && filesize($path);
|
||||
if ($exists && !($this->offsetExists('width') && $this->offsetExists('height') && $this->offsetExists('mime'))) {
|
||||
$image_info = getimagesize($path);
|
||||
@@ -101,9 +101,8 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
parent::reset();
|
||||
|
||||
$this->format = 'guess';
|
||||
$this->options = $this->defaults;
|
||||
$this->imageSettings = $this->defaults;
|
||||
$this->quality = $this->defaults['quality'];
|
||||
$this->debug_watermarked = false;
|
||||
|
||||
$this->resetImage();
|
||||
|
||||
@@ -135,13 +134,13 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
$grav = $this->getGrav();
|
||||
|
||||
// FIXME: update this code
|
||||
$saved_image_path = $this->saved_image_path = $this->saveImage();
|
||||
|
||||
$output = preg_replace('|^' . preg_quote(GRAV_WEBROOT, '|') . '|', '', $saved_image_path) ?: $saved_image_path;
|
||||
|
||||
$grav = $this->getGrav();
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
|
||||
@@ -208,18 +207,18 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
$attributes['sizes'] = $this->sizes();
|
||||
}
|
||||
|
||||
if ($this->saved_image_path && $this->options['auto_sizes']) {
|
||||
if ($this->saved_image_path && $this->imageSettings['auto_sizes']) {
|
||||
// FIXME: we can calculate this from the new image object..?
|
||||
if (!array_key_exists('height', $this->attributes) && !array_key_exists('width', $this->attributes)) {
|
||||
$info = getimagesize($this->saved_image_path);
|
||||
$width = (int)$info[0];
|
||||
$height = (int)$info[1];
|
||||
|
||||
$scaling_factor = min(1, $this->options['retina_scale']);
|
||||
$scaling_factor = min(1, $this->imageSettings['retina_scale']);
|
||||
$attributes['width'] = (int)($width / $scaling_factor);
|
||||
$attributes['height'] = (int)($height / $scaling_factor);
|
||||
|
||||
if ($this->options['aspect_ratio']) {
|
||||
if ($this->imageSettings['aspect_ratio']) {
|
||||
$style = ($attributes['style'] ?? ' ') . "--aspect-ratio: $width/$height;";
|
||||
$attributes['style'] = trim($style);
|
||||
}
|
||||
@@ -276,7 +275,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
{
|
||||
$enabled = \is_bool($enabled) ? $enabled : $enabled === 'true';
|
||||
|
||||
$this->options['auto_sizes'] = $enabled;
|
||||
$this->imageSettings['auto_sizes'] = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -289,18 +288,18 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate
|
||||
{
|
||||
$enabled = \is_bool($enabled) ? $enabled : $enabled === 'true';
|
||||
|
||||
$this->options['aspect_ratio'] = $enabled;
|
||||
$this->imageSettings['aspect_ratio'] = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $scale
|
||||
* @param string|int $scale
|
||||
* @return $this
|
||||
*/
|
||||
public function retinaScale($scale = 1)
|
||||
{
|
||||
$this->options['retina_scale'] = (int)$scale;
|
||||
$this->imageSettings['retina_scale'] = (int)$scale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -182,11 +182,7 @@ class MediumFactory
|
||||
*/
|
||||
public static function scaledFromMedium($medium, $from, $to)
|
||||
{
|
||||
if (!$medium instanceof ImageMedium) {
|
||||
return $medium;
|
||||
}
|
||||
|
||||
if ($to > $from) {
|
||||
if (!$medium instanceof ImageMedium || $to > $from) {
|
||||
return $medium;
|
||||
}
|
||||
|
||||
@@ -206,13 +202,15 @@ class MediumFactory
|
||||
$medium->set('debug', $debug);
|
||||
$medium->setImagePrettyName($prev_basename);
|
||||
|
||||
$size = filesize($file);
|
||||
|
||||
$medium = self::fromFile($file);
|
||||
if ($medium) {
|
||||
$size = filesize($file);
|
||||
|
||||
$medium->set('basename', $basename);
|
||||
$medium->set('filename', $basename . '.' . $medium->extension);
|
||||
$medium->set('size', $size);
|
||||
} else {
|
||||
$size = 0;
|
||||
}
|
||||
|
||||
return ['file' => $medium, 'size' => $size];
|
||||
|
||||
@@ -116,6 +116,12 @@ interface ImageOperationsInterface extends ImageResizeInterface, ImageInfoInterf
|
||||
*/
|
||||
public function sepia();
|
||||
|
||||
/**
|
||||
* @param int $blurFactor
|
||||
* @return $this
|
||||
*/
|
||||
public function gaussianBlur(int $blurFactor = 1);
|
||||
|
||||
/**
|
||||
* Merge with another image.
|
||||
*
|
||||
|
||||
@@ -10,34 +10,34 @@ interface ImageSaveInterface
|
||||
/**
|
||||
* Save the image as a gif.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string|null $filepath
|
||||
* @return $this
|
||||
*/
|
||||
public function saveGif(string $file);
|
||||
public function saveGif(?string $filepath);
|
||||
|
||||
/**
|
||||
* Save the image as a png.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string|null $filepath
|
||||
* @return $this
|
||||
*/
|
||||
public function savePng(string $file);
|
||||
public function savePng(?string $filepath);
|
||||
|
||||
/**
|
||||
* Save the image as a Webp.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string|null $filepath
|
||||
* @param int $quality
|
||||
* @return $this
|
||||
*/
|
||||
public function saveWebp(string $file, int $quality);
|
||||
public function saveWebp(?string $filepath, int $quality);
|
||||
|
||||
/**
|
||||
* Save the image as a jpeg.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string|null $filepath
|
||||
* @param int $quality
|
||||
* @return $this
|
||||
*/
|
||||
public function saveJpeg(string $file, int $quality);
|
||||
public function saveJpeg(?string $filepath, int $quality);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use Grav\Common\Media\Factories\MediaFactory;
|
||||
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaUploadInterface;
|
||||
use Grav\Common\Media\Traits\MediaTrait;
|
||||
use Grav\Common\Page\Media;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Page\Medium\MediumFactory;
|
||||
use Grav\Common\Utils;
|
||||
@@ -205,10 +206,12 @@ trait FlexMediaTrait
|
||||
}
|
||||
|
||||
$media = $this->getMedia();
|
||||
/** @var Media|null $originalMedia */
|
||||
$originalMedia = is_callable([$this, 'getOriginalMedia']) ? $this->getOriginalMedia() : null;
|
||||
|
||||
$list = [];
|
||||
foreach ($value as $filename => $info) {
|
||||
$filename = (string)$filename;
|
||||
if (!is_array($info)) {
|
||||
$list[$filename] = $info;
|
||||
continue;
|
||||
|
||||
@@ -2,10 +2,31 @@
|
||||
|
||||
namespace Grav\Framework\Image\Adapter;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
use Grav\Framework\Contracts\Image\ImageAdapterInterface;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use UnexpectedValueException;
|
||||
use function count;
|
||||
use function define;
|
||||
use function defined;
|
||||
use function extension_loaded;
|
||||
use function function_exists;
|
||||
use function is_resource;
|
||||
|
||||
// Make sure DG defines have been set!
|
||||
if (!defined('IMG_GIF')) {
|
||||
define('IMG_GIF', 1);
|
||||
}
|
||||
if (!defined('IMG_JPG')) {
|
||||
define('IMG_JPG', 2);
|
||||
}
|
||||
if (!defined('IMG_PNG')) {
|
||||
define('IMG_PNG', 4);
|
||||
}
|
||||
if (!defined('IMG_WEBP')) {
|
||||
define('IMG_WEBP', 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* GD Image adapter.
|
||||
@@ -42,6 +63,58 @@ class GdAdapter extends Adapter
|
||||
return (bool)(imagetypes() & $test);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new image.
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return static
|
||||
*/
|
||||
public static function create(int $width, int $height): GdAdapter
|
||||
{
|
||||
$resource = static::createResource($width, $height);
|
||||
|
||||
return new static($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates image from a file.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromFile(string $filepath): GdAdapter
|
||||
{
|
||||
$extension = strtolower(Utils::pathinfo($filepath, PATHINFO_EXTENSION));
|
||||
$resource = static::createResourceFromFile($filepath, $extension);
|
||||
|
||||
return new static($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates image from string of image data.
|
||||
*
|
||||
* @param string $data
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromString(string $data): GdAdapter
|
||||
{
|
||||
$resource = static::createResourceFromString($data);
|
||||
|
||||
return new static($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of image from resource.
|
||||
*
|
||||
* @param \GdImage|resource $resource
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromImage($resource): GdAdapter
|
||||
{
|
||||
return new static($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GdImage|resource $resource
|
||||
*/
|
||||
@@ -56,6 +129,15 @@ class GdAdapter extends Adapter
|
||||
}
|
||||
|
||||
$this->resource = $resource;
|
||||
|
||||
$this->convertToTrueColor();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->resource) {
|
||||
imagedestroy($this->resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +159,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fillBackground(?int $background = 0xffffff)
|
||||
public function fillBackground(?int $background = 0xffffff): GdAdapter
|
||||
{
|
||||
$w = $this->width();
|
||||
$h = $this->height();
|
||||
@@ -96,10 +178,13 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resize(?int $background, int $target_width, int $target_height, int $new_width, int $new_height)
|
||||
public function resize(?int $background, int $target_width, int $target_height, int $new_width, int $new_height): GdAdapter
|
||||
{
|
||||
$width = $this->width();
|
||||
$height = $this->height();
|
||||
$dst_x = (int)(($target_width - $new_width) / 2);
|
||||
$dst_y = (int)(($target_height - $new_height) / 2);
|
||||
|
||||
$n = imagecreatetruecolor($target_width, $target_height);
|
||||
if (!$n) {
|
||||
throw new RuntimeException('Failed to resize image: image creation failed');
|
||||
@@ -115,7 +200,7 @@ class GdAdapter extends Adapter
|
||||
imagesavealpha($n, true);
|
||||
}
|
||||
|
||||
imagecopyresampled($n, $this->resource, ($target_width - $new_width) / 2, ($target_height - $new_height) / 2, 0, 0, $new_width, $new_height, $width, $height);
|
||||
imagecopyresampled($n, $this->resource, $dst_x, $dst_y, 0, 0, $new_width, $new_height, $width, $height);
|
||||
imagedestroy($this->resource);
|
||||
|
||||
$this->resource = $n;
|
||||
@@ -126,7 +211,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function crop(int $x, int $y, int $width, int $height)
|
||||
public function crop(int $x, int $y, int $width, int $height): GdAdapter
|
||||
{
|
||||
$destination = imagecreatetruecolor($width, $height);
|
||||
if (!$destination) {
|
||||
@@ -146,7 +231,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function negate()
|
||||
public function negate(): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_NEGATE);
|
||||
|
||||
@@ -156,7 +241,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function brightness($brightness)
|
||||
public function brightness($brightness): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_BRIGHTNESS, $brightness);
|
||||
|
||||
@@ -166,7 +251,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function contrast($contrast)
|
||||
public function contrast($contrast): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_CONTRAST, $contrast);
|
||||
|
||||
@@ -176,7 +261,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function grayscale()
|
||||
public function grayscale(): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_GRAYSCALE);
|
||||
|
||||
@@ -186,7 +271,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function emboss()
|
||||
public function emboss(): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_EMBOSS);
|
||||
|
||||
@@ -196,7 +281,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function smooth(int $p)
|
||||
public function smooth(int $p): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_SMOOTH, $p);
|
||||
|
||||
@@ -206,7 +291,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sharp()
|
||||
public function sharp(): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_MEAN_REMOVAL);
|
||||
|
||||
@@ -216,7 +301,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function edge()
|
||||
public function edge(): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_EDGEDETECT);
|
||||
|
||||
@@ -226,7 +311,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function colorize(int $red, int $green, int $blue)
|
||||
public function colorize(int $red, int $green, int $blue): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_COLORIZE, $red, $green, $blue);
|
||||
|
||||
@@ -236,7 +321,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sepia()
|
||||
public function sepia(): GdAdapter
|
||||
{
|
||||
imagefilter($this->resource, IMG_FILTER_GRAYSCALE);
|
||||
imagefilter($this->resource, IMG_FILTER_COLORIZE, 100, 50, 0);
|
||||
@@ -247,7 +332,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gaussianBlur(int $blurFactor = 1)
|
||||
public function gaussianBlur(int $blurFactor = 1): GdAdapter
|
||||
{
|
||||
if ($blurFactor < 1) {
|
||||
return $this;
|
||||
@@ -267,8 +352,8 @@ class GdAdapter extends Adapter
|
||||
// scale way down and gradually scale back up, blurring all the way
|
||||
for ($i = 0; $i < $blurFactor; ++$i) {
|
||||
// determine dimensions of next image
|
||||
$nextWidth = (int)($smallestWidth * (2 ** $i));
|
||||
$nextHeight = (int)($smallestHeight * (2 ** $i));
|
||||
$nextWidth = $smallestWidth * (2 ** $i);
|
||||
$nextHeight = $smallestHeight * (2 ** $i);
|
||||
|
||||
// resize previous image to next size
|
||||
$nextImage = imagecreatetruecolor($nextWidth, $nextHeight);
|
||||
@@ -288,8 +373,7 @@ class GdAdapter extends Adapter
|
||||
}
|
||||
|
||||
// scale back to original size and blur one more time
|
||||
imagecopyresized($this->resource, $nextImage,
|
||||
0, 0, 0, 0, $originalWidth, $originalHeight, $nextWidth, $nextHeight);
|
||||
imagecopyresized($this->resource, $nextImage, 0, 0, 0, 0, $originalWidth, $originalHeight, $nextWidth, $nextHeight);
|
||||
imagefilter($this->resource, IMG_FILTER_GAUSSIAN_BLUR);
|
||||
|
||||
// clean up
|
||||
@@ -303,7 +387,7 @@ class GdAdapter extends Adapter
|
||||
*
|
||||
* @param GdAdapter $other
|
||||
*/
|
||||
public function merge(ImageAdapterInterface $other, int $x = 0, int $y = 0, int $width = null, int $height = null)
|
||||
public function merge(ImageAdapterInterface $other, int $x = 0, int $y = 0, int $width = null, int $height = null): GdAdapter
|
||||
{
|
||||
if (!$other instanceof self) {
|
||||
throw new InvalidArgumentException('Image to be merged needs to be instance of GdAdapter');
|
||||
@@ -327,7 +411,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rotate(float $angle, ?int $background = 0xffffff)
|
||||
public function rotate(float $angle, ?int $background = 0xffffff): GdAdapter
|
||||
{
|
||||
$resource = imagerotate($this->resource, $angle, $this->allocateColor($background));
|
||||
if (!$resource) {
|
||||
@@ -344,7 +428,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fill(int $color = 0xffffff, int $x = 0, int $y = 0)
|
||||
public function fill(int $color = 0xffffff, int $x = 0, int $y = 0): GdAdapter
|
||||
{
|
||||
imagealphablending($this->resource, false);
|
||||
imagefill($this->resource, $x, $y, $this->allocateColor($color));
|
||||
@@ -355,7 +439,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(string $font, string $text, int $x = 0, int $y = 0, float $size = 12.0, float $angle = 0.0, int $color = 0x000000, string $align = 'left')
|
||||
public function write(string $font, string $text, int $x = 0, int $y = 0, float $size = 12.0, float $angle = 0.0, int $color = 0x000000, string $align = 'left'): GdAdapter
|
||||
{
|
||||
imagealphablending($this->resource, true);
|
||||
|
||||
@@ -363,7 +447,7 @@ class GdAdapter extends Adapter
|
||||
$sim_size = $this->getTTFBox($font, $text, $size, $angle);
|
||||
|
||||
if ($align === 'center') {
|
||||
$x -= $sim_size['width'] / 2;
|
||||
$x -= (int)($sim_size['width'] / 2);
|
||||
}
|
||||
|
||||
if ($align === 'right') {
|
||||
@@ -379,7 +463,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rectangle(int $x1, int $y1, int $x2, int $y2, int $color, bool $filled = false)
|
||||
public function rectangle(int $x1, int $y1, int $x2, int $y2, int $color, bool $filled = false): GdAdapter
|
||||
{
|
||||
$c = $this->allocateColor($color);
|
||||
if ($filled) {
|
||||
@@ -394,7 +478,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function roundedRectangle(int $x1, int $y1, int $x2, int $y2, int $radius, int $color, bool $filled = false)
|
||||
public function roundedRectangle(int $x1, int $y1, int $x2, int $y2, int $radius, int $color, bool $filled = false): GdAdapter
|
||||
{
|
||||
$c = $this->allocateColor($color);
|
||||
|
||||
@@ -425,7 +509,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function line(int $x1, int $y1, int $x2, int $y2, $color = 0x000000)
|
||||
public function line(int $x1, int $y1, int $x2, int $y2, $color = 0x000000): GdAdapter
|
||||
{
|
||||
imageline($this->resource, $x1, $y1, $x2, $y2, $this->allocateColor($color));
|
||||
|
||||
@@ -435,7 +519,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function ellipse(int $cx, int $cy, int $width, int $height, $color = 0x000000, bool $filled = false)
|
||||
public function ellipse(int $cx, int $cy, int $width, int $height, $color = 0x000000, bool $filled = false): GdAdapter
|
||||
{
|
||||
$c = $this->allocateColor($color);
|
||||
if ($filled) {
|
||||
@@ -450,7 +534,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function circle(int $cx, int $cy, int $r, $color = 0x000000, bool $filled = false)
|
||||
public function circle(int $cx, int $cy, int $r, $color = 0x000000, bool $filled = false): GdAdapter
|
||||
{
|
||||
return $this->ellipse($cx, $cy, $r, $r, $this->allocateColor($color), $filled);
|
||||
}
|
||||
@@ -458,7 +542,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function polygon(array $points, $color, bool $filled = false)
|
||||
public function polygon(array $points, $color, bool $filled = false): GdAdapter
|
||||
{
|
||||
$num = (int)(count($points) / 2);
|
||||
$c = $this->allocateColor($color);
|
||||
@@ -475,7 +559,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function flip(bool $flipVertical, bool $flipHorizontal)
|
||||
public function flip(bool $flipVertical, bool $flipHorizontal): GdAdapter
|
||||
{
|
||||
if (!$flipVertical && !$flipHorizontal) {
|
||||
return $this;
|
||||
@@ -548,7 +632,7 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveGif(string $filepath)
|
||||
public function saveGif(?string $filepath): GdAdapter
|
||||
{
|
||||
$transColor = imagecolorallocatealpha($this->resource, 255, 255, 255, 127);
|
||||
if (!$transColor) {
|
||||
@@ -556,7 +640,10 @@ class GdAdapter extends Adapter
|
||||
}
|
||||
|
||||
imagecolortransparent($this->resource, $transColor);
|
||||
imagegif($this->resource, $filepath);
|
||||
$result = imagegif($this->resource, $filepath);
|
||||
if (false === $result) {
|
||||
throw new RuntimeException('Failed to save image as gif');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -564,9 +651,13 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function savePng(string $filepath)
|
||||
public function savePng(?string $filepath): GdAdapter
|
||||
{
|
||||
imagepng($this->resource, $filepath);
|
||||
$result = imagepng($this->resource, $filepath);
|
||||
if (false === $result) {
|
||||
throw new RuntimeException('Failed to save image as png');
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -574,9 +665,12 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveWebp(string $filepath, int $quality)
|
||||
public function saveWebp(?string $filepath, int $quality): GdAdapter
|
||||
{
|
||||
imagewebp($this->resource, $filepath, $quality);
|
||||
$result = imagewebp($this->resource, $filepath, $quality);
|
||||
if (false === $result) {
|
||||
throw new RuntimeException('Failed to save image as webp');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -584,9 +678,12 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveJpeg(string $filepath, int $quality)
|
||||
public function saveJpeg(?string $filepath, int $quality): GdAdapter
|
||||
{
|
||||
imagejpeg($this->resource, $filepath, $quality);
|
||||
$result = imagejpeg($this->resource, $filepath, $quality);
|
||||
if (false === $result) {
|
||||
throw new RuntimeException('Failed to save image as jpeg');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -594,108 +691,27 @@ class GdAdapter extends Adapter
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function enableProgressive()
|
||||
public function enableProgressive(): GdAdapter
|
||||
{
|
||||
imageinterlace($this->resource, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create empty image.
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return void
|
||||
*/
|
||||
protected function createImage(int $width, int $height): void
|
||||
{
|
||||
$this->resource = imagecreatetruecolor($width, $height) ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create image from a string.
|
||||
*
|
||||
* @param string $data
|
||||
* @return void
|
||||
*/
|
||||
protected function createImageFromString(string $data): void
|
||||
{
|
||||
$this->resource = @imagecreatefromstring($data) ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the image to true color.
|
||||
*
|
||||
* @return void
|
||||
* @return static
|
||||
*/
|
||||
protected function convertToTrueColor(): void
|
||||
protected function convertToTrueColor(): GdAdapter
|
||||
{
|
||||
if (!imageistruecolor($this->resource)) {
|
||||
imagepalettetotruecolor($this->resource);
|
||||
}
|
||||
|
||||
imagesavealpha($this->resource, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to open the file using jpeg.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return void
|
||||
*/
|
||||
protected function openJpeg(string $filepath): void
|
||||
{
|
||||
if (file_exists($filepath) && filesize($filepath)) {
|
||||
$this->resource = @imagecreatefromjpeg($filepath) ?: null;
|
||||
} else {
|
||||
$this->resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to open the file using gif.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return void
|
||||
*/
|
||||
protected function openGif(string $filepath): void
|
||||
{
|
||||
if (file_exists($filepath) && filesize($filepath)) {
|
||||
$this->resource = @imagecreatefromgif($filepath) ?: null;
|
||||
} else {
|
||||
$this->resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to open the file using PNG.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return void
|
||||
*/
|
||||
protected function openPng(string $filepath): void
|
||||
{
|
||||
if (file_exists($filepath) && filesize($filepath)) {
|
||||
$this->resource = @imagecreatefrompng($filepath) ?: null;
|
||||
} else {
|
||||
$this->resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to open the file using WEBP.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return void
|
||||
*/
|
||||
protected function openWebp(string $filepath): void
|
||||
{
|
||||
if (file_exists($filepath) && filesize($filepath)) {
|
||||
$this->resource = @imagecreatefromwebp($filepath) ?: null;
|
||||
} else {
|
||||
$this->resource = null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -710,56 +726,6 @@ class GdAdapter extends Adapter
|
||||
return imagecolorat($this->resource, $x, $y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load image resource.
|
||||
*
|
||||
* @param \GdImage|resource $resource
|
||||
*/
|
||||
protected function loadResource($resource): void
|
||||
{
|
||||
$this->resource = $resource;
|
||||
|
||||
imagesavealpha($this->resource, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load file.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @param string $type
|
||||
* @return void
|
||||
* @throws UnexpectedValueException
|
||||
*/
|
||||
protected function loadFile(string $filepath, string $type): void
|
||||
{
|
||||
if (!static::isSupported($type)) {
|
||||
throw new UnexpectedValueException('Type ' . $type . ' is not supported by GD');
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'jpeg':
|
||||
$this->openJpeg($filepath);
|
||||
break;
|
||||
case 'gif':
|
||||
$this->openGif($filepath);
|
||||
break;
|
||||
case 'png':
|
||||
$this->openPng($filepath);
|
||||
break;
|
||||
case 'webp':
|
||||
$this->openWebp($filepath);
|
||||
break;
|
||||
default:
|
||||
throw new UnexpectedValueException('Unable to open file (' . $filepath . ')');
|
||||
}
|
||||
|
||||
if (null === $this->getResource()) {
|
||||
throw new UnexpectedValueException('Unable to open file (' . $filepath . ')');
|
||||
}
|
||||
|
||||
$this->convertToTrueColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the bounding box of a text using TrueType fonts.
|
||||
*
|
||||
@@ -813,4 +779,70 @@ class GdAdapter extends Adapter
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load file.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @param string $type
|
||||
* @return \GdImage|resource|null
|
||||
* @throws UnexpectedValueException
|
||||
*/
|
||||
protected static function createResourceFromFile(string $filepath, string $type)
|
||||
{
|
||||
if (!static::isSupported($type)) {
|
||||
throw new UnexpectedValueException(sprintf('Type %s is not supported by GD', $type));
|
||||
}
|
||||
|
||||
// Check if file exists.
|
||||
if (!file_exists($filepath) || !filesize($filepath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$test = self::$types[$type] ?? 0;
|
||||
$resource = null;
|
||||
switch ($test) {
|
||||
case \IMG_JPG:
|
||||
$resource = @imagecreatefromjpeg($filepath) ?: null;
|
||||
break;
|
||||
case \IMG_GIF:
|
||||
$resource = @imagecreatefromgif($filepath) ?: null;
|
||||
break;
|
||||
case \IMG_PNG:
|
||||
$resource = @imagecreatefrompng($filepath) ?: null;
|
||||
break;
|
||||
case \IMG_WEBP:
|
||||
$resource = @imagecreatefromwebp($filepath) ?: null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (null === $resource) {
|
||||
throw new UnexpectedValueException(sprintf('Unable to open file (%s)', $filepath));
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create image from a string.
|
||||
*
|
||||
* @param string $data
|
||||
* @return \GdImage|resource|null
|
||||
*/
|
||||
protected static function createResourceFromString(string $data)
|
||||
{
|
||||
return @imagecreatefromstring($data) ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create empty image.
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return \GdImage|resource|null
|
||||
*/
|
||||
protected static function createResource(int $width, int $height)
|
||||
{
|
||||
return imagecreatetruecolor($width, $height) ?: null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,19 @@
|
||||
|
||||
namespace Grav\Framework\Image;
|
||||
|
||||
use Exception;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Framework\Compat\Serializable;
|
||||
use Grav\Framework\Contracts\Image\ImageAdapterInterface;
|
||||
use Grav\Framework\Contracts\Image\ImageOperationsInterface;
|
||||
use Grav\Framework\Image\Traits\ImageOperationsTrait;
|
||||
use InvalidArgumentException;
|
||||
use JsonSerializable;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use function array_slice;
|
||||
use function dirname;
|
||||
use function is_int;
|
||||
|
||||
/**
|
||||
* Image class.
|
||||
@@ -16,6 +24,23 @@ class Image implements ImageOperationsInterface, JsonSerializable
|
||||
use ImageOperationsTrait;
|
||||
use Serializable;
|
||||
|
||||
/**
|
||||
* Supported types.
|
||||
* @var array
|
||||
*/
|
||||
public static $types = [
|
||||
'jpg' => 'jpeg',
|
||||
'jpeg' => 'jpeg',
|
||||
'webp' => 'webp',
|
||||
'png' => 'png',
|
||||
'gif' => 'gif',
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
public $extra = [];
|
||||
|
||||
/** @var ImageAdapterInterface */
|
||||
protected $adapter;
|
||||
/** @var int */
|
||||
protected $origWidth;
|
||||
/** @var int */
|
||||
@@ -26,8 +51,10 @@ class Image implements ImageOperationsInterface, JsonSerializable
|
||||
protected $modified;
|
||||
/** @var int */
|
||||
protected $size;
|
||||
/** @var array */
|
||||
public $extra = [];
|
||||
/** @var int */
|
||||
protected $operationsCursor = 0;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string $filepath
|
||||
@@ -36,10 +63,10 @@ class Image implements ImageOperationsInterface, JsonSerializable
|
||||
public function __construct(string $filepath, array $info)
|
||||
{
|
||||
$this->filepath = $filepath;
|
||||
$this->modified = $info['modified'] ?? 0;
|
||||
$this->size = $info['size'] ?? 0;
|
||||
$this->origWidth = $this->width = $info['width'] ?? 0;
|
||||
$this->origHeight = $this->height = $info['height'] ?? 0;
|
||||
$this->modified = (int)($info['modified'] ?? 0);
|
||||
$this->size = (int)($info['size'] ?? 0);
|
||||
$this->origWidth = $this->width = (int)($info['width'] ?? 0);
|
||||
$this->origHeight = $this->height = (int)($info['height'] ?? 0);
|
||||
$this->orientation = isset($info['exif']['Orientation']) ? (int)$info['exif']['Orientation'] : null;
|
||||
}
|
||||
|
||||
@@ -105,4 +132,218 @@ class Image implements ImageOperationsInterface, JsonSerializable
|
||||
{
|
||||
return sha1(serialize($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image adapter.
|
||||
*
|
||||
* @return ImageAdapterInterface|null
|
||||
*/
|
||||
public function getAdapter(): ?ImageAdapterInterface
|
||||
{
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set image adapter.
|
||||
*
|
||||
* Note: You should always call $this->freeAdapter() as soon as you have generated the image!
|
||||
*
|
||||
* @param ImageAdapterInterface|string $adapter
|
||||
* @return $this
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function setAdapter(ImageAdapterInterface $adapter): Image
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free image adapter to free some memory.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function freeAdapter(): void
|
||||
{
|
||||
$this->adapter = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param int $quality
|
||||
* @param bool $actual
|
||||
* @return string
|
||||
* @throws IOException|InvalidArgumentException|RuntimeException
|
||||
*/
|
||||
public function cacheFile(string $type = 'jpg', int $quality = 80, bool $actual = false): string
|
||||
{
|
||||
$filepath = $actual ? null : $this->filepath;
|
||||
if (file_exists($filepath)) {
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
return $this->save($filepath, $type, $quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $quality
|
||||
* @return string
|
||||
* @throws IOException|InvalidArgumentException|RuntimeException
|
||||
*/
|
||||
public function jpeg(int $quality = 80): string
|
||||
{
|
||||
return $this->cacheFile('jpg', $quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws IOException|InvalidArgumentException|RuntimeException
|
||||
*/
|
||||
public function gif(): string
|
||||
{
|
||||
return $this->cacheFile('gif');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws IOException|InvalidArgumentException|RuntimeException
|
||||
*/
|
||||
public function png(): string
|
||||
{
|
||||
return $this->cacheFile('png');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $quality
|
||||
* @return string
|
||||
* @throws IOException|InvalidArgumentException|RuntimeException
|
||||
*/
|
||||
public function webp(int $quality = 80): string
|
||||
{
|
||||
return $this->cacheFile('webp', $quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $quality
|
||||
* @return string
|
||||
* @throws IOException|InvalidArgumentException|RuntimeException
|
||||
*/
|
||||
public function guess(int $quality = 80): string
|
||||
{
|
||||
return $this->cacheFile('guess', $quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function guessType(): string
|
||||
{
|
||||
return pathinfo($this->filepath, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the file to a given output.
|
||||
*
|
||||
* Note: to use this method, you need to call `setAdapter()` first.
|
||||
*
|
||||
* @param string|null $filepath
|
||||
* @param string|int $type
|
||||
* @param int $quality
|
||||
* @return string
|
||||
* @throws IOException|InvalidArgumentException|RuntimeException
|
||||
*/
|
||||
public function save(?string $filepath, $type = 'guess', int $quality = 80): string
|
||||
{
|
||||
if (is_int($type)) {
|
||||
$quality = $type;
|
||||
$type = 'jpeg';
|
||||
}
|
||||
|
||||
if ($type === 'guess') {
|
||||
$type = $this->guessType();
|
||||
}
|
||||
|
||||
$type = self::$types[$type] ?? '';
|
||||
if ('' === $type) {
|
||||
throw new InvalidArgumentException(sprintf("Given image type '%s' is not valid", $type));
|
||||
}
|
||||
|
||||
$adapter = $this->getAdapter();
|
||||
if (null === $adapter) {
|
||||
throw new RuntimeException('You need to set image adapter first!');
|
||||
}
|
||||
|
||||
if ($filepath) {
|
||||
$this->mkdir(dirname($filepath));
|
||||
}
|
||||
|
||||
try {
|
||||
$this->applyOperations();
|
||||
|
||||
if (null === $filepath) {
|
||||
ob_start();
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'jpeg';
|
||||
$adapter->saveJpeg($filepath, $quality);
|
||||
break;
|
||||
case 'gif';
|
||||
$adapter->saveGif($filepath);
|
||||
break;
|
||||
case 'png':
|
||||
$adapter->savePng($filepath);
|
||||
break;
|
||||
case 'webp':
|
||||
$adapter->saveWebP($filepath, $quality);
|
||||
break;
|
||||
}
|
||||
|
||||
return $filepath ?? ob_get_clean();
|
||||
} catch (Exception $e) {
|
||||
throw new RuntimeException('', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply image operations.
|
||||
*
|
||||
* Note: to use this method, you need to call `setAdapter()` first.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function applyOperations(): Image
|
||||
{
|
||||
$adapter = $this->adapter;
|
||||
if (!$adapter) {
|
||||
throw new RuntimeException('You need to set image adapter first!');
|
||||
}
|
||||
|
||||
// Only get the remaining operations.
|
||||
$operations = $this->operations;
|
||||
$cursor = $this->operationsCursor;
|
||||
if ($cursor) {
|
||||
$operations = array_slice($operations, $cursor, null, true);
|
||||
}
|
||||
|
||||
foreach ($operations as $cursor => $operation) {
|
||||
[$method, $params] = $operation;
|
||||
|
||||
$adapter->{$method}(...$params);
|
||||
}
|
||||
|
||||
$this->operationsCursor = $cursor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @return void
|
||||
*/
|
||||
private function mkdir(string $directory): void
|
||||
{
|
||||
Folder::mkdir($directory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ trait ImageOperationsTrait
|
||||
$height = $new_height;
|
||||
}
|
||||
|
||||
if ($width === $new_width && $height === $new_height) {
|
||||
if ($width === $new_width && $height === $new_height && $width === $this->width() && $height === $this->height()) {
|
||||
// Nothing to resize.
|
||||
return $this;
|
||||
}
|
||||
@@ -465,6 +465,17 @@ trait ImageOperationsTrait
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $blurFactor
|
||||
* @return $this
|
||||
*/
|
||||
public function gaussianBlur(int $blurFactor = 1)
|
||||
{
|
||||
$this->operations[] = ['gaussianBlur', [$blurFactor]];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge with another image.
|
||||
*
|
||||
@@ -478,8 +489,10 @@ trait ImageOperationsTrait
|
||||
public function merge(Image $other, int $x = 0, int $y = 0, int $width = 0, int $height = 0)
|
||||
{
|
||||
$serialized = $other->jsonSerialize();
|
||||
$deps = $other->dependencies;
|
||||
$deps[] = $serialized;
|
||||
|
||||
$dependencies[] = $serialized;
|
||||
$this->dependencies = array_merge($this->dependencies, $deps);
|
||||
$this->operations[] = ['merge', [$serialized, $x, $y, $width, $height]];
|
||||
|
||||
return $this;
|
||||
|
||||
Reference in New Issue
Block a user