Commit cb5e00ae authored by mondrake's avatar mondrake Committed by mondrake

Issue #3023439 by mondrake: Support 'Smart Crop' and 'Scale and Smart Crop' on Imagemagick

parent a6d9d2ec
......@@ -36,10 +36,10 @@ Mask | Apply a mask to the image.
Mirror | Mirror the image horizontally and/or vertically. | X | X |
Opacity | Change overall image transparency level. | X | IM only |
Resize percentage | Resize the image by percentage of its width/height. | X | X |
Scale and Smart Crop | Similar to Scale and Crop, but preserves the portion of the image with the most entropy. | X | |
Scale and Smart Crop | Similar to Scale and Crop, but preserves the portion of the image with the most entropy. | X | X |
Set canvas | Places the source image over a colored or a transparent background of a defined size. | X | IM only |
Set transparent color | Defines the color to be used for transparency in GIF images. | X | IM only |
Smart Crop | Similar to Crop, but preserves the portion of the image with the most entropy. | X | |
Smart Crop | Similar to Crop, but preserves the portion of the image with the most entropy. | X | X |
Sharpen | Sharpens an image (using convolution). | X | IM only |
Strip metadata | Strips all EXIF metadata from image. | X | X |
Text overlay<sup>2</sup> | Overlays text on an image, defining text font, size and positioning. | X | IM only<sup>3</sup> |
......
......@@ -16,6 +16,11 @@ trait ScaleAndSmartCropTrait {
'description' => 'The calculation algorithm for the crop',
'required' => TRUE,
],
'algorithm_params' => [
'description' => 'The calculation algorithm parameters',
'required' => FALSE,
'default' => [],
],
'simulate' => [
'description' => 'Boolean indicating the crop shall not be executed, but just the crop area highlighted on the source image',
'required' => FALSE,
......
<?php
namespace Drupal\image_effects\Plugin\ImageToolkit\Operation\imagemagick;
use Drupal\imagemagick\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickImageToolkitOperationBase;
use Drupal\image_effects\Plugin\ImageToolkit\Operation\DrawEllipseTrait;
/**
* Defines ImageMagick draw ellipse operation.
*
* @ImageToolkitOperation(
* id = "image_effects_imagemagick_draw_ellipse",
* toolkit = "imagemagick",
* operation = "draw_ellipse",
* label = @Translation("Draw ellipse"),
* description = @Translation("Draws on the image an ellipse of the specified color.")
* )
*/
class DrawEllipse extends ImagemagickImageToolkitOperationBase {
use DrawEllipseTrait;
/**
* {@inheritdoc}
*/
protected function execute(array $arguments) {
$arg = '';
$arg .= '-fill ' . $this->escapeArgument($arguments['color']);
$this->addArgument($arg . ' -draw ' . $this->escapeArgument("ellipse {$arguments['cx']},{$arguments['cy']} {$arguments['width']},{$arguments['height']} 0,360"));
return TRUE;
}
}
......@@ -24,7 +24,13 @@ class ScaleAndSmartCrop extends ImagemagickImageToolkitOperationBase {
* {@inheritdoc}
*/
protected function execute(array $arguments = []) {
// @todo not supported in ImageMagick. See if it could be possible.
// Don't scale if we don't change the dimensions at all.
if ($arguments['width'] !== $this->getToolkit()->getWidth() || $arguments['height'] !== $this->getToolkit()->getHeight()) {
// Don't upscale if the option isn't enabled.
if ($arguments['upscale'] || ($arguments['width'] <= $this->getToolkit()->getWidth() && $arguments['height'] <= $this->getToolkit()->getHeight())) {
return $this->getToolkit()->apply('resize', $arguments['resize']) && $this->getToolkit()->apply('smart_crop', $arguments);
}
}
return TRUE;
}
......
......@@ -4,6 +4,7 @@ namespace Drupal\image_effects\Plugin\ImageToolkit\Operation\imagemagick;
use Drupal\imagemagick\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickImageToolkitOperationBase;
use Drupal\image_effects\Plugin\ImageToolkit\Operation\SmartCropTrait;
use Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait;
/**
* Defines Imagemagick Scale and Smart Crop operation.
......@@ -19,12 +20,69 @@ use Drupal\image_effects\Plugin\ImageToolkit\Operation\SmartCropTrait;
class SmartCrop extends ImagemagickImageToolkitOperationBase {
use SmartCropTrait;
use GDOperationTrait;
/**
* {@inheritdoc}
*/
protected function execute(array $arguments = []) {
// @todo not supported in ImageMagick. See if it could be possible.
$file_system = \Drupal::service('file_system');
$image_factory = \Drupal::service('image.factory');
// Convert the image to disk at the current state, and reopen it using the
// GD toolkit to allow determining the crop rectangle.
$temp_path = $file_system->tempnam('temporary://', 'image_effects_');
$current_destination_format = $this->getToolkit()->arguments()->getDestinationFormat();
$this->getToolkit()->arguments()->setDestinationFormatFromExtension('png');
$this->getToolkit()->save($temp_path);
$this->getToolkit()->arguments()->setDestinationFormat($current_destination_format);
$temp_image = $image_factory->get($temp_path, 'gd');
switch ($arguments['algorithm']) {
case 'entropy_slice':
$rect = $this->getEntropyCropBySlicing($temp_image->getToolkit()->getResource(), $arguments['width'], $arguments['height']);
break;
case 'entropy_grid':
$rect = $this->getEntropyCropByGridding($temp_image->getToolkit()->getResource(), $arguments['width'], $arguments['height'], $arguments['simulate'], $arguments['algorithm_params']['grid_width'], $arguments['algorithm_params']['grid_height'], $arguments['algorithm_params']['grid_rows'], $arguments['algorithm_params']['grid_cols'], $arguments['algorithm_params']['grid_sub_rows'], $arguments['algorithm_params']['grid_sub_cols']);
break;
}
$points = $this->getRectangleCorners($rect);
// Do not need the temporary image file any longer.
$file_system->unlink($temp_path);
// Crop the image using the coordinates found above. If simulating, draw
// a marker on the image instead.
if (!$arguments['simulate']) {
return $this->getToolkit()->apply('crop', [
'x' => $points[6],
'y' => $points[7],
'width' => $rect->getWidth(),
'height' => $rect->getHeight(),
]);
}
else {
$rect->translate([-2, -2]);
for ($i = -2; $i <= 2; $i++) {
$this->getToolkit()->apply('draw_rectangle', [
'rectangle' => $rect,
'border_color' => $i !== 0 ? '#00FF00FF' : '#FF0000FF',
]);
$rect->translate([1, 1]);
}
for ($i = 0; $i < 8; $i += 2) {
$this->getToolkit()->apply('draw_ellipse', [
'cx' => $points[$i],
'cy' => $points[$i + 1],
'width' => 6,
'height' => 6,
'color' => '#FF0000FF',
]);
}
}
return TRUE;
}
......
......@@ -11,17 +11,6 @@ use Drupal\Tests\image_effects\Functional\ImageEffectsTestBase;
*/
class ScaleAndSmartCropTest extends ImageEffectsTestBase {
/**
* {@inheritdoc}
*/
public function providerToolkits() {
$toolkits = parent::providerToolkits();
// @todo This effect does not work with ImageMagick.
unset($toolkits['ImageMagick-imagemagick']);
unset($toolkits['ImageMagick-graphicsmagick']);
return $toolkits;
}
/**
* Test the image_effects_scale_and_smart_crop effect.
*
......@@ -151,7 +140,7 @@ class ScaleAndSmartCropTest extends ImageEffectsTestBase {
$this->testImageStyle->createDerivative($original_uri, $derivative_uri);
$actual_image = $this->imageFactory->get($derivative_uri, 'gd');
$expected_image = $this->imageFactory->get($expected_image_uri, 'gd');
$this->assertImagesAreEqual($expected_image, $actual_image);
$this->assertImagesAreEqual($expected_image, $actual_image, 200);
}
// Remove effect.
......
......@@ -11,17 +11,6 @@ use Drupal\Tests\image_effects\Functional\ImageEffectsTestBase;
*/
class SmartCropTest extends ImageEffectsTestBase {
/**
* {@inheritdoc}
*/
public function providerToolkits() {
$toolkits = parent::providerToolkits();
// @todo This effect does not work with ImageMagick.
unset($toolkits['ImageMagick-imagemagick']);
unset($toolkits['ImageMagick-graphicsmagick']);
return $toolkits;
}
/**
* Test the image_effects_smart_crop effect.
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment