mirror of
https://github.com/Intervention/image.git
synced 2025-08-19 12:11:26 +02:00
Optimize image decoding with Imagick driver
File paths are now read directly via Imagick::readImage() instead of reading everything with file_get_contents() and passing it to BinaryImageDecoder.
This commit is contained in:
@@ -68,21 +68,29 @@ abstract class AbstractDecoder extends DriverSpecialized implements DecoderInter
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and return EXIF data from given image data string
|
||||
* Extract and return EXIF data from given input which can be binary image
|
||||
* data or a file path.
|
||||
*
|
||||
* @param string $image_data
|
||||
* @param string $path_or_data
|
||||
* @return CollectionInterface
|
||||
*/
|
||||
protected function extractExifData(string $image_data): CollectionInterface
|
||||
protected function extractExifData(string $path_or_data): CollectionInterface
|
||||
{
|
||||
if (!function_exists('exif_read_data')) {
|
||||
return new Collection();
|
||||
}
|
||||
|
||||
try {
|
||||
$pointer = $this->buildFilePointer($image_data);
|
||||
$data = @exif_read_data($pointer, null, true);
|
||||
fclose($pointer);
|
||||
$input = match (true) {
|
||||
(strlen($path_or_data) <= PHP_MAXPATHLEN && is_file($path_or_data)) => $path_or_data, // path
|
||||
default => $this->buildFilePointer($path_or_data), // data
|
||||
};
|
||||
|
||||
// extract exif data via file path
|
||||
$data = @exif_read_data($input, null, true);
|
||||
if (is_resource($input)) {
|
||||
fclose($input);
|
||||
}
|
||||
} catch (Exception) {
|
||||
$data = [];
|
||||
}
|
||||
@@ -118,7 +126,7 @@ abstract class AbstractDecoder extends DriverSpecialized implements DecoderInter
|
||||
|
||||
$result = preg_match($pattern, $input, $matches);
|
||||
|
||||
return new class ($matches, $result)
|
||||
return new class($matches, $result)
|
||||
{
|
||||
private $matches;
|
||||
private $result;
|
||||
|
@@ -6,17 +6,12 @@ namespace Intervention\Image\Drivers\Imagick\Decoders;
|
||||
|
||||
use Imagick;
|
||||
use ImagickException;
|
||||
use Intervention\Image\Drivers\AbstractDecoder;
|
||||
use Intervention\Image\Drivers\Imagick\Core;
|
||||
use Intervention\Image\Drivers\Imagick\Driver;
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Image;
|
||||
use Intervention\Image\Interfaces\ColorInterface;
|
||||
use Intervention\Image\Interfaces\DecoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Origin;
|
||||
|
||||
class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface
|
||||
class BinaryImageDecoder extends ImagickImageDecoder implements DecoderInterface
|
||||
{
|
||||
public function decode(mixed $input): ImageInterface|ColorInterface
|
||||
{
|
||||
@@ -31,59 +26,11 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface
|
||||
throw new DecoderException('Unable to decode input');
|
||||
}
|
||||
|
||||
// For some JPEG formats, the "coalesceImages()" call leads to an image
|
||||
// completely filled with background color. The logic behind this is
|
||||
// incomprehensible for me; could be an imagick bug.
|
||||
if ($imagick->getImageFormat() != 'JPEG') {
|
||||
$imagick = $imagick->coalesceImages();
|
||||
}
|
||||
// decode image
|
||||
$image = parent::decode($imagick);
|
||||
|
||||
// fix image orientation
|
||||
switch ($imagick->getImageOrientation()) {
|
||||
case Imagick::ORIENTATION_TOPRIGHT: // 2
|
||||
$imagick->flopImage();
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_BOTTOMRIGHT: // 3
|
||||
$imagick->rotateimage("#000", 180);
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_BOTTOMLEFT: // 4
|
||||
$imagick->rotateimage("#000", 180);
|
||||
$imagick->flopImage();
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_LEFTTOP: // 5
|
||||
$imagick->rotateimage("#000", -270);
|
||||
$imagick->flopImage();
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_RIGHTTOP: // 6
|
||||
$imagick->rotateimage("#000", -270);
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_RIGHTBOTTOM: // 7
|
||||
$imagick->rotateimage("#000", -90);
|
||||
$imagick->flopImage();
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_LEFTBOTTOM: // 8
|
||||
$imagick->rotateimage("#000", -90);
|
||||
break;
|
||||
}
|
||||
|
||||
// set new orientation in image
|
||||
$imagick->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
|
||||
|
||||
$image = new Image(
|
||||
new Driver(),
|
||||
new Core($imagick),
|
||||
$this->extractExifData($input)
|
||||
);
|
||||
|
||||
$image->setOrigin(new Origin(
|
||||
$imagick->getImageMimeType()
|
||||
));
|
||||
// extract exif data
|
||||
$image->setExif($this->extractExifData($input));
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
@@ -5,12 +5,14 @@ declare(strict_types=1);
|
||||
namespace Intervention\Image\Drivers\Imagick\Decoders;
|
||||
|
||||
use Exception;
|
||||
use Imagick;
|
||||
use ImagickException;
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Interfaces\ColorInterface;
|
||||
use Intervention\Image\Interfaces\DecoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
|
||||
class FilePathImageDecoder extends BinaryImageDecoder implements DecoderInterface
|
||||
class FilePathImageDecoder extends ImagickImageDecoder implements DecoderInterface
|
||||
{
|
||||
public function decode(mixed $input): ImageInterface|ColorInterface
|
||||
{
|
||||
@@ -30,12 +32,22 @@ class FilePathImageDecoder extends BinaryImageDecoder implements DecoderInterfac
|
||||
throw new DecoderException('Unable to decode input');
|
||||
}
|
||||
|
||||
try {
|
||||
$imagick = new Imagick();
|
||||
$imagick->readImage($input);
|
||||
} catch (ImagickException) {
|
||||
throw new DecoderException('Unable to decode input');
|
||||
}
|
||||
|
||||
// decode image
|
||||
$image = parent::decode(file_get_contents($input));
|
||||
$image = parent::decode($imagick);
|
||||
|
||||
// set file path on origin
|
||||
$image->origin()->setFilePath($input);
|
||||
|
||||
// extract exif data
|
||||
$image->setExif($this->extractExifData($input));
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
|
85
src/Drivers/Imagick/Decoders/ImagickImageDecoder.php
Normal file
85
src/Drivers/Imagick/Decoders/ImagickImageDecoder.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Imagick\Decoders;
|
||||
|
||||
use Imagick;
|
||||
use Intervention\Image\Drivers\AbstractDecoder;
|
||||
use Intervention\Image\Drivers\Imagick\Core;
|
||||
use Intervention\Image\Drivers\Imagick\Driver;
|
||||
use Intervention\Image\Exceptions\DecoderException;
|
||||
use Intervention\Image\Image;
|
||||
use Intervention\Image\Interfaces\ColorInterface;
|
||||
use Intervention\Image\Interfaces\DecoderInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Origin;
|
||||
|
||||
class ImagickImageDecoder extends AbstractDecoder implements DecoderInterface
|
||||
{
|
||||
public function decode(mixed $input): ImageInterface|ColorInterface
|
||||
{
|
||||
if (!is_object($input)) {
|
||||
throw new DecoderException('Unable to decode input');
|
||||
}
|
||||
|
||||
if (!($input instanceof Imagick)) {
|
||||
throw new DecoderException('Unable to decode input');
|
||||
}
|
||||
|
||||
// For some JPEG formats, the "coalesceImages()" call leads to an image
|
||||
// completely filled with background color. The logic behind this is
|
||||
// incomprehensible for me; could be an imagick bug.
|
||||
if ($input->getImageFormat() != 'JPEG') {
|
||||
$input = $input->coalesceImages();
|
||||
}
|
||||
|
||||
// fix image orientation
|
||||
switch ($input->getImageOrientation()) {
|
||||
case Imagick::ORIENTATION_TOPRIGHT: // 2
|
||||
$input->flopImage();
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_BOTTOMRIGHT: // 3
|
||||
$input->rotateimage("#000", 180);
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_BOTTOMLEFT: // 4
|
||||
$input->rotateimage("#000", 180);
|
||||
$input->flopImage();
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_LEFTTOP: // 5
|
||||
$input->rotateimage("#000", -270);
|
||||
$input->flopImage();
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_RIGHTTOP: // 6
|
||||
$input->rotateimage("#000", -270);
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_RIGHTBOTTOM: // 7
|
||||
$input->rotateimage("#000", -90);
|
||||
$input->flopImage();
|
||||
break;
|
||||
|
||||
case Imagick::ORIENTATION_LEFTBOTTOM: // 8
|
||||
$input->rotateimage("#000", -90);
|
||||
break;
|
||||
}
|
||||
|
||||
// set new orientation in image
|
||||
$input->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
|
||||
|
||||
$image = new Image(
|
||||
new Driver(),
|
||||
new Core($input)
|
||||
);
|
||||
|
||||
$image->setOrigin(new Origin(
|
||||
$input->getImageMimeType()
|
||||
));
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
@@ -249,6 +249,18 @@ final class Image implements ImageInterface
|
||||
return is_null($query) ? $this->exif : $this->exif->get($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see ImgageInterface::setExif()
|
||||
*/
|
||||
public function setExif(CollectionInterface $exif): ImageInterface
|
||||
{
|
||||
$this->exif = $exif;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
|
@@ -144,6 +144,14 @@ interface ImageInterface extends IteratorAggregate, Countable
|
||||
*/
|
||||
public function exif(?string $query = null): mixed;
|
||||
|
||||
/**
|
||||
* Set exif data for the image object
|
||||
*
|
||||
* @param CollectionInterface $exif
|
||||
* @return ImageInterface
|
||||
*/
|
||||
public function setExif(CollectionInterface $exif): ImageInterface;
|
||||
|
||||
/**
|
||||
* Return image resolution/density
|
||||
*
|
||||
|
Reference in New Issue
Block a user