Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Imagick class #6754

Draft
wants to merge 9 commits into
base: v5/develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"ext-apcu": "Support for the Apcu cache driver",
"ext-exif": "Support for exif information from images",
"ext-fileinfo": "Improved mime type detection for files",
"ext-imagick": "Improved thumbnail generation",
"ext-intl": "Improved i18n number formatting",
"ext-memcached": "Support for the Memcached cache driver",
"ext-redis": "Support for the Redis cache driver",
Expand Down
6 changes: 4 additions & 2 deletions src/Image/Darkroom.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Exception;
use Kirby\Image\Darkroom\GdLib;
use Kirby\Image\Darkroom\ImageMagick;
use Kirby\Image\Darkroom\Imagick;

/**
* A wrapper around resizing and cropping
Expand All @@ -19,8 +20,9 @@
class Darkroom
{
public static array $types = [
'gd' => GdLib::class,
'im' => ImageMagick::class
'gd' => GdLib::class,
'imagick' => Imagick::class,
'im' => ImageMagick::class
];

public function __construct(
Expand Down
5 changes: 4 additions & 1 deletion src/Image/Darkroom/ImageMagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
use Kirby\Image\Focus;

/**
* ImageMagick
* Legacy ImageMagick driver using the convert CLI
*
* @package Kirby Image
* @author Bastian Allgeier <[email protected]>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://opensource.org/licenses/MIT
*
* @deprecated 5.0.0 Use `imagick` driver instead
* @todo Remove in v7
*/
class ImageMagick extends Darkroom
{
Expand Down
277 changes: 277 additions & 0 deletions src/Image/Darkroom/Imagick.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
<?php

namespace Kirby\Image\Darkroom;

use Exception;
use Imagick as Image;
use Kirby\Image\Darkroom;
use Kirby\Image\Focus;

/**
* Imagick
*
* @package Kirby Image
* @author Nico Hoffmann <[email protected]>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://opensource.org/licenses/MIT
*/
class Imagick extends Darkroom
{
protected function autoOrient(Image $image): Image
{
switch ($image->getImageOrientation()) {
case Image::ORIENTATION_TOPLEFT:
break;
case Image::ORIENTATION_TOPRIGHT:
$image->flopImage();
break;
case Image::ORIENTATION_BOTTOMRIGHT:
$image->rotateImage('#000', 180);
break;
case Image::ORIENTATION_BOTTOMLEFT:
$image->flopImage();
$image->rotateImage('#000', 180);
break;
case Image::ORIENTATION_LEFTTOP:
$image->flopImage();
$image->rotateImage('#000', -90);
break;
case Image::ORIENTATION_RIGHTTOP:
$image->rotateImage('#000', 90);
break;
case Image::ORIENTATION_RIGHTBOTTOM:
$image->flopImage();
$image->rotateImage('#000', 90);
break;
case Image::ORIENTATION_LEFTBOTTOM:
$image->rotateImage('#000', -90);
break;
default: // Invalid orientation
break;
}

$image->setImageOrientation(Image::ORIENTATION_TOPLEFT);
return $image;
}

/**
* Applies the blur settings
*/
protected function blur(Image $image, array $options): Image
{
if ($options['blur'] !== false) {
return $image->blurImage(0.0, $options['blur']);
}

return $image;
}

/**
* Keep animated gifs
*/
protected function coalesce(Image $image): Image
{
if ($image->getImageMimeType() === 'image/gif') {
return $image->coalesceImages();
}

return $image;
}

/**
* Returns additional default parameters for imagemagick
*/
protected function defaults(): array
{
return parent::defaults() + [
'interlace' => false,
'threads' => 1,
];
}

/**
* Applies the correct settings for grayscale images
*/
protected function grayscale(Image $image, array $options): Image
{
if ($options['grayscale'] === true) {
$image->setImageColorspace(Image::COLORSPACE_GRAY);
}

return $image;
}

/**
* Applies sharpening if activated in the options.
*/
protected function sharpen(Image $image, array $options): Image
{
if (is_int($options['sharpen']) === false) {
return $image;
}

$amount = max(1, min(100, $options['sharpen'])) / 100;
$image->sharpenImage(0.0, $amount);

return $image;
}

/**
* Applies the correct settings for interlaced JPEGs if
* activated via options
*/
protected function interlace(Image $image, array $options): Image
{
if ($options['interlace'] === true) {
$image->setInterlaceScheme(Image::INTERLACE_LINE);
}

return $image;
}

/**
* Creates and runs the full imagemagick command
* to process the image
*
* @throws \Exception
*/
public function process(string $file, array $options = []): array
{
$options = $this->preprocess($file, $options);

$image = new Image($file);
$image = $this->threads($image, $options);
$image = $this->interlace($image, $options);
$image = $this->coalesce($image);
$image = $this->grayscale($image, $options);
$image = $this->autoOrient($image);
$image = $this->resize($image, $options);
$image = $this->quality($image, $options);
$image = $this->blur($image, $options);
$image = $this->sharpen($image, $options);
$image = $this->strip($image, $options);

if ($this->save($image, $file, $options) === false) {
throw new Exception(message: 'The imagemagick result could not be generated');
}

return $options;
}

/**
* Applies the correct JPEG compression quality settings
*/
protected function quality(Image $image, array $options): Image
{
$image->setImageCompressionQuality($options['quality']);
return $image;
}

/**
* Creates the correct options to crop or resize the image
* and translates the crop positions for imagemagick
*/
protected function resize(Image $image, array $options): Image
{
// simple resize
if ($options['crop'] === false) {
$image->thumbnailImage(
$options['width'],
$options['height']
);
}

// crop based on focus point
if (Focus::isFocalPoint($options['crop']) === true) {
if ($focus = Focus::coords(
$options['crop'],
$options['sourceWidth'],
$options['sourceHeight'],
$options['width'],
$options['height']
)) {
$image->cropImage(
$options['width'],
$options['height'],
$focus['x1'],
$focus['y1']
);

$image->thumbnailImage(
$options['width'],
$options['height']
);
}
}

// translate the gravity option into something imagemagick understands
$gravity = match ($options['crop'] ?? null) {
'top left' => Image::GRAVITY_NORTHWEST,
'top' => Image::GRAVITY_NORTH,
'top right' => Image::GRAVITY_NORTHEAST,
'left' => Image::GRAVITY_WEST,
'right' => Image::GRAVITY_EAST,
'bottom left' => Image::GRAVITY_SOUTHWEST,
'bottom' => Image::GRAVITY_SOUTH,
'bottom right' => Image::GRAVITY_SOUTHEAST,
default => Image::GRAVITY_CENTER
};

$image->thumbnailImage($options['width'], $options['height']);
$image->setGravity($gravity);
$image->cropImage($options['width'], $options['height'], 0, 0);

return $image;
}

/**
* Creates the option for the output file
*/
protected function save(Image $image, string $file, array $options): bool
{
if ($options['format'] !== null) {
$file = pathinfo($file, PATHINFO_DIRNAME) . '/' . pathinfo($file, PATHINFO_FILENAME) . '.' . $options['format'];
}

return $image->writeImage($file);
}

/**
* Removes all metadata but ICC profiles from the image
*/
protected function strip(Image $image, array $options): Image
{
// get the ICC profile before stripping
$profiles = $image->getImageProfiles('icc', true);

// strip all metadata
$image->stripImage();

// re-apply the ICC profile, if it exists
if ($icc = $profiles['icc'] ?? null) {
// temporarily save in different format for PNG files
if (strtolower($image->getImageFormat()) === 'png') {
$blob = $image->getImageBlob();
$image = new Image();
$image->readImageBlob($blob);
}

$image->profileImage('icc', $icc);
}

return $image;
}

/**
* Sets thread limit
*/
protected function threads(Image $image, array $options): Image
{
$image->setResourceLimit(
Image::RESOURCETYPE_THREAD,
$options['threads']
);
return $image;
}
}
Loading
Loading