diff --git a/core/core.services.yml b/core/core.services.yml index 608c439..e59f7b9 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -511,6 +511,9 @@ services: image.toolkit.manager: class: Drupal\Core\ImageToolkit\ImageToolkitManager arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@config.factory'] + image.toolkit.operation.manager: + class: Drupal\Core\ImageToolkit\ImageToolkitOperationManager + arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler'] image.toolkit: class: Drupal\Core\ImageToolkit\ImageToolkitInterface factory_method: getDefaultToolkit diff --git a/core/lib/Drupal/Core/Image/Image.php b/core/lib/Drupal/Core/Image/Image.php index 963ed5b..f72bce3 100644 --- a/core/lib/Drupal/Core/Image/Image.php +++ b/core/lib/Drupal/Core/Image/Image.php @@ -107,7 +107,7 @@ public function __construct($source, ImageToolkitInterface $toolkit) { * {@inheritdoc} */ public function isSupported() { - return in_array($this->getType(), $this->toolkit->supportedTypes()); + return in_array($this->getType(), $this->getToolkit()->supportedTypes()); } /** @@ -195,7 +195,7 @@ public function hasResource() { public function getResource() { if (!$this->hasResource()) { $this->processInfo(); - $this->toolkit->load($this); + $this->getToolkit()->load($this); } return $this->resource; } @@ -218,8 +218,8 @@ public function getSource() { /** * {@inheritdoc} */ - public function getToolkitId() { - return $this->toolkit->getPluginId(); + public function getToolkit() { + return $this->toolkit; } /** @@ -229,7 +229,7 @@ public function save($destination = NULL) { if (empty($destination)) { $destination = $this->getSource(); } - if ($return = $this->toolkit->save($this, $destination)) { + if ($return = $this->getToolkit()->save($this, $destination)) { // Clear the cached file size and refresh the image information. clearstatcache(TRUE, $destination); $this->setSource($destination); @@ -247,8 +247,7 @@ public function save($destination = NULL) { * Prepares the image information. * * Drupal supports GIF, JPG and PNG file formats when used with the GD - * toolkit, and may support others, depending on which toolkits are - * installed. + * toolkit, and may support others, depending on which toolkit is active. * * @return bool * FALSE, if the file could not be found or is not an image. Otherwise, the @@ -264,7 +263,7 @@ protected function processInfo() { return FALSE; } - if ($details = $this->toolkit->getInfo($this)) { + if ($details = $this->getToolkit()->getInfo($this)) { $this->height = $details['height']; $this->width = $details['width']; $this->type = $details['type']; @@ -326,7 +325,7 @@ public function crop($x, $y, $width, $height) { $width = (int) round($width); $height = (int) round($height); - return $this->toolkit->crop($this, $x, $y, $width, $height); + return $this->getToolkit()->apply('crop', $this, array('x' => $x, 'y' => $y, 'width' => $width, 'height' => $height)); } /** @@ -336,21 +335,21 @@ public function resize($width, $height) { $width = (int) round($width); $height = (int) round($height); - return $this->toolkit->resize($this, $width, $height); + return $this->getToolkit()->apply('resize', $this, array('width' => $width, 'height' => $height)); } /** * {@inheritdoc} */ public function desaturate() { - return $this->toolkit->desaturate($this); + return $this->getToolkit()->apply('desaturate', $this); } /** * {@inheritdoc} */ public function rotate($degrees, $background = NULL) { - return $this->toolkit->rotate($this, $degrees, $background); + return $this->getToolkit()->apply('rotate', $this, array('degrees' => $degrees, 'background' => $background)); } /** diff --git a/core/lib/Drupal/Core/Image/ImageInterface.php b/core/lib/Drupal/Core/Image/ImageInterface.php index bba3460..0ef4fc2 100644 --- a/core/lib/Drupal/Core/Image/ImageInterface.php +++ b/core/lib/Drupal/Core/Image/ImageInterface.php @@ -6,6 +6,7 @@ */ namespace Drupal\Core\Image; +use Drupal\Core\ImageToolkit\ImageToolkitInterface; /** * Provides an interface for image objects. @@ -136,12 +137,12 @@ public function setSource($source); public function getSource(); /** - * Returns the ID of the image toolkit used for this image file. + * Returns the image toolkit used for this image file. * - * @return string - * The ID of the image toolkit. + * @return ImageToolkitInterface + * The image toolkit. */ - public function getToolkitId(); + public function getToolkit(); /** * Closes the image and saves the changes to a file. diff --git a/core/lib/Drupal/Core/ImageToolkit/Annotation/ImageToolkitOperation.php b/core/lib/Drupal/Core/ImageToolkit/Annotation/ImageToolkitOperation.php new file mode 100644 index 0000000..1b1eb22 --- /dev/null +++ b/core/lib/Drupal/Core/ImageToolkit/Annotation/ImageToolkitOperation.php @@ -0,0 +1,70 @@ +getToolkitOperationPluginId($this->getPluginId(), $operation); + $plugin = $manager->createInstance($plugin_id); + return $plugin->apply($this, $image, $data); + } + +} diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php index 2b86aef..736fe7e 100644 --- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php +++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php @@ -60,78 +60,6 @@ public function settingsForm(); public function settingsFormSubmit($form, &$form_state); /** - * Scales an image to the specified size. - * - * @param \Drupal\Core\Image\ImageInterface $image - * An image object. The $image->resource, $image->info['width'], and - * $image->info['height'] values will be modified by this call. - * @param int $width - * The new width of the resized image, in pixels. - * @param int $height - * The new height of the resized image, in pixels. - * - * @return bool - * TRUE or FALSE, based on success. - */ - public function resize(ImageInterface $image, $width, $height); - - /** - * Rotates an image the given number of degrees. - * - * @param \Drupal\Core\Image\ImageInterface $image - * An image object. The $image->resource, $image->info['width'], and - * $image->info['height'] values will be modified by this call. - * @param int $degrees - * The number of (clockwise) degrees to rotate the image. - * @param string $background - * (optional) An hexadecimal integer specifying the background color to use - * for the uncovered area of the image after the rotation. E.g. 0x000000 for - * black, 0xff00ff for magenta, and 0xffffff for white. For images that - * support transparency, this will default to transparent. Otherwise it will - * be white. - * - * @return bool - * TRUE or FALSE, based on success. - */ - public function rotate(ImageInterface $image, $degrees, $background = NULL); - - /** - * Crops an image. - * - * @param \Drupal\Core\Image\ImageInterface $image - * An image object. The $image->resource, $image->info['width'], and - * $image->info['height'] values will be modified by this call. - * @param int $x - * The starting x offset at which to start the crop, in pixels. - * @param int $y - * The starting y offset at which to start the crop, in pixels. - * @param int $width - * The width of the cropped area, in pixels. - * @param int $height - * The height of the cropped area, in pixels. - * - * @return bool - * TRUE or FALSE, based on success. - * - * @see image_crop() - */ - public function crop(ImageInterface $image, $x, $y, $width, $height); - - /** - * Converts an image resource to grayscale. - * - * Note that transparent GIFs loose transparency when desaturated. - * - * @param \Drupal\Core\Image\ImageInterface $image - * An image object. The $image->resource value will be modified by this - * call. - * - * @return bool - * TRUE or FALSE, based on success. - */ - public function desaturate(ImageInterface $image); - - /** * Creates an image resource from a file. * * @param \Drupal\Core\Image\ImageInterface $image @@ -190,4 +118,18 @@ public static function isAvailable(); */ public static function supportedTypes(); + /** + * Applies a toolkit specific operation to an image. + * + * @param string $operation + * The toolkit operation to be processed. + * @param \Drupal\Core\Image\ImageInterface $image + * An image object. + * @param array $data + * An array of variables to be used by the toolkit operation. + * + * @return mixed + * TRUE if the operation was performed successfully, FALSE otherwise. + */ + public function apply($operation, ImageInterface $image, array $data = array()); } diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationInterface.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationInterface.php new file mode 100644 index 0000000..d6033de --- /dev/null +++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationInterface.php @@ -0,0 +1,36 @@ +alterInfo($module_handler, 'image_toolkit_operation'); + $this->setCacheBackend($cache_backend, $language_manager, 'image_toolkit_operation'); + } + + /** + * Returns the plugin ID for the plugin for a given toolkit and operation. + * + * By convention, the id of an ImageToolkitOperation plugin must look like + * "{toolkit}.{operation}". + * + * @param string $toolkit + * The toolkit ID. + * @param string $operation + * The operation (e.g. "crop"). + * + * @return string + * The plugin ID. + */ + public function getToolkitOperationPluginId($toolkit, $operation) { + return "$toolkit.$operation"; + } + +} diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/CropImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/CropImageEffect.php index 39f58f3..6eacc64 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/CropImageEffect.php +++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/CropImageEffect.php @@ -30,7 +30,7 @@ public function applyEffect(ImageInterface $image) { $x = image_filter_keyword($x, $image->getWidth(), $this->configuration['width']); $y = image_filter_keyword($y, $image->getHeight(), $this->configuration['height']); if (!$image->crop($x, $y, $this->configuration['width'], $this->configuration['height'])) { - watchdog('image', 'Image crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); + watchdog('image', 'Image crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkit()->getPluginId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); return FALSE; } return TRUE; diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/DesaturateImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/DesaturateImageEffect.php index 912bec9..5ce7dd6 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/DesaturateImageEffect.php +++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/DesaturateImageEffect.php @@ -34,7 +34,7 @@ public function transformDimensions(array &$dimensions) { */ public function applyEffect(ImageInterface $image) { if (!$image->desaturate()) { - watchdog('image', 'Image desaturate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); + watchdog('image', 'Image desaturate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkit()->getPluginId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); return FALSE; } return TRUE; diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ResizeImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ResizeImageEffect.php index 27d5538..f9bca8b 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ResizeImageEffect.php +++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ResizeImageEffect.php @@ -29,7 +29,7 @@ class ResizeImageEffect extends ImageEffectBase implements ConfigurableImageEffe */ public function applyEffect(ImageInterface $image) { if (!$image->resize($this->configuration['width'], $this->configuration['height'])) { - watchdog('image', 'Image resize failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); + watchdog('image', 'Image resize failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkit()->getPluginId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); return FALSE; } return TRUE; diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/RotateImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/RotateImageEffect.php index 23c0560..e2aae4b 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/RotateImageEffect.php +++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/RotateImageEffect.php @@ -48,7 +48,7 @@ public function applyEffect(ImageInterface $image) { } if (!$image->rotate($this->configuration['degrees'], $this->configuration['bgcolor'])) { - watchdog('image', 'Image rotate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); + watchdog('image', 'Image rotate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkit()->getPluginId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); return FALSE; } return TRUE; diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleAndCropImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleAndCropImageEffect.php index 39bd2ec..a099879 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleAndCropImageEffect.php +++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleAndCropImageEffect.php @@ -27,7 +27,7 @@ class ScaleAndCropImageEffect extends ResizeImageEffect { */ public function applyEffect(ImageInterface $image) { if (!$image->scaleAndCrop($this->configuration['width'], $this->configuration['height'])) { - watchdog('image', 'Image scale and crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); + watchdog('image', 'Image scale and crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkit()->getPluginId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); return FALSE; } return TRUE; diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleImageEffect.php index 87b0dac..98e0507 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleImageEffect.php +++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleImageEffect.php @@ -28,7 +28,7 @@ class ScaleImageEffect extends ResizeImageEffect { */ public function applyEffect(ImageInterface $image) { if (!$image->scale($this->configuration['width'], $this->configuration['height'], $this->configuration['upscale'])) { - watchdog('image', 'Image scale failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); + watchdog('image', 'Image scale failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkit()->getPluginId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR); return FALSE; } return TRUE; diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php index 737ad27..1005853 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php @@ -2,13 +2,14 @@ /** * @file - * Contains \Drupal\system\Plugin\ImageToolkit\GDToolkit;. + * Contains \Drupal\system\Plugin\ImageToolkit\GDToolkit. */ namespace Drupal\system\Plugin\ImageToolkit; use Drupal\Core\Plugin\PluginBase; use Drupal\Core\Image\ImageInterface; +use Drupal\Core\ImageToolkit\ImageToolkitBase; use Drupal\Core\ImageToolkit\ImageToolkitInterface; /** @@ -19,7 +20,7 @@ * title = @Translation("GD2 image manipulation toolkit") * ) */ -class GDToolkit extends PluginBase implements ImageToolkitInterface { +class GDToolkit extends ImageToolkitBase { /** * {@inheritdoc} @@ -49,109 +50,6 @@ public function settingsFormSubmit($form, &$form_state) { /** * {@inheritdoc} */ - public function resize(ImageInterface $image, $width, $height) { - $res = $this->createTmp($image, $width, $height); - - if (!imagecopyresampled($res, $image->getResource(), 0, 0, 0, 0, $width, $height, $image->getWidth(), $image->getHeight())) { - return FALSE; - } - - imagedestroy($image->getResource()); - // Update image object. - $image - ->setResource($res) - ->setWidth($width) - ->setHeight($height); - return TRUE; - } - - /** - * {@inheritdoc} - */ - public function rotate(ImageInterface $image, $degrees, $background = NULL) { - // PHP installations using non-bundled GD do not have imagerotate. - if (!function_exists('imagerotate')) { - watchdog('image', 'The image %file could not be rotated because the imagerotate() function is not available in this PHP installation.', array('%file' => $image->getSource())); - return FALSE; - } - - // Convert the hexadecimal background value to a color index value. - if (isset($background)) { - $rgb = array(); - for ($i = 16; $i >= 0; $i -= 8) { - $rgb[] = (($background >> $i) & 0xFF); - } - $background = imagecolorallocatealpha($image->getResource(), $rgb[0], $rgb[1], $rgb[2], 0); - } - // Set the background color as transparent if $background is NULL. - else { - // Get the current transparent color. - $background = imagecolortransparent($image->getResource()); - - // If no transparent colors, use white. - if ($background == 0) { - $background = imagecolorallocatealpha($image->getResource(), 255, 255, 255, 0); - } - } - - // Images are assigned a new color palette when rotating, removing any - // transparency flags. For GIF images, keep a record of the transparent color. - if ($image->getType() == IMAGETYPE_GIF) { - $transparent_index = imagecolortransparent($image->getResource()); - if ($transparent_index != 0) { - $transparent_gif_color = imagecolorsforindex($image->getResource(), $transparent_index); - } - } - - $image->setResource(imagerotate($image->getResource(), 360 - $degrees, $background)); - - // GIFs need to reassign the transparent color after performing the rotate. - if (isset($transparent_gif_color)) { - $background = imagecolorexactalpha($image->getResource(), $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']); - imagecolortransparent($image->getResource(), $background); - } - - $image - ->setWidth(imagesx($image->getResource())) - ->setHeight(imagesy($image->getResource())); - return TRUE; - } - - /** - * {@inheritdoc} - */ - public function crop(ImageInterface $image, $x, $y, $width, $height) { - $res = $this->createTmp($image, $width, $height); - - if (!imagecopyresampled($res, $image->getResource(), 0, 0, $x, $y, $width, $height, $width, $height)) { - return FALSE; - } - - // Destroy the original image and return the modified image. - imagedestroy($image->getResource()); - $image - ->setResource($res) - ->setWidth($width) - ->setHeight($height); - return TRUE; - } - - /** - * {@inheritdoc} - */ - public function desaturate(ImageInterface $image) { - // PHP installations using non-bundled GD do not have imagefilter. - if (!function_exists('imagefilter')) { - watchdog('image', 'The image %file could not be desaturated because the imagefilter() function is not available in this PHP installation.', array('%file' => $image->getSource())); - return FALSE; - } - - return imagefilter($image->getResource(), IMG_FILTER_GRAYSCALE); - } - - /** - * {@inheritdoc} - */ public function load(ImageInterface $image) { $function = 'imagecreatefrom' . image_type_to_extension($image->getType(), FALSE); if (function_exists($function) && $resource = $function($image->getSource())) { @@ -291,4 +189,5 @@ public static function isAvailable() { public static function supportedTypes() { return array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF); } + } diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDCrop.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDCrop.php new file mode 100644 index 0000000..95d982f --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDCrop.php @@ -0,0 +1,64 @@ +createTmp($image, $data['width'], $data['height']); + + if (!imagecopyresampled($res, $image->getResource(), 0, 0, $data['x'], $data['y'], $data['width'], $data['height'], $data['width'], $data['height'])) { + return FALSE; + } + + // Destroy the original image and return the modified image. + imagedestroy($image->getResource()); + $image + ->setResource($res) + ->setWidth($data['width']) + ->setHeight($data['height']); + return TRUE; + } + +} diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDDesaturate.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDDesaturate.php new file mode 100644 index 0000000..e5101b7 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDDesaturate.php @@ -0,0 +1,54 @@ + $image->getSource())); + return FALSE; + } + + return imagefilter($image->getResource(), IMG_FILTER_GRAYSCALE); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDResize.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDResize.php new file mode 100644 index 0000000..42f1c08 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDResize.php @@ -0,0 +1,62 @@ +createTmp($image, $data['width'], $data['height']); + + if (!imagecopyresampled($res, $image->getResource(), 0, 0, 0, 0, $data['width'], $data['height'], $image->getWidth(), $image->getHeight())) { + return FALSE; + } + + imagedestroy($image->getResource()); + // Update image object. + $image + ->setResource($res) + ->setWidth($data['width']) + ->setHeight($data['height']); + return TRUE; + } + +} diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDRotate.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDRotate.php new file mode 100644 index 0000000..0f2fea3 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/Operation/GDRotate.php @@ -0,0 +1,102 @@ + $image->getSource())); + return FALSE; + } + + // Convert the hexadecimal background value to a color index value. + if (!empty($data['background'])) { + $rgb = array(); + for ($i = 16; $i >= 0; $i -= 8) { + $rgb[] = (($data['background'] >> $i) & 0xFF); + } + $data['background'] = imagecolorallocatealpha($image->getResource(), $rgb[0], $rgb[1], $rgb[2], 0); + } + // Set the background color as transparent if $data['background'] is NULL. + else { + // Get the current transparent color. + $data['background'] = imagecolortransparent($image->getResource()); + + // If no transparent colors, use white. + if ($data['background'] == 0) { + $data['background'] = imagecolorallocatealpha($image->getResource(), 255, 255, 255, 0); + } + } + + // Images are assigned a new color palette when rotating, removing any + // transparency flags. For GIF images, keep a record of the transparent color. + if ($image->getType() == IMAGETYPE_GIF) { + $transparent_index = imagecolortransparent($image->getResource()); + if ($transparent_index != 0) { + $transparent_gif_color = imagecolorsforindex($image->getResource(), $transparent_index); + } + } + + $image->setResource(imagerotate($image->getResource(), 360 - $data['degrees'], $data['background'])); + + // GIFs need to reassign the transparent color after performing the rotate. + if (isset($transparent_gif_color)) { + $data['background'] = imagecolorexactalpha($image->getResource(), $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']); + imagecolortransparent($image->getResource(), $data['background']); + } + + $image + ->setWidth(imagesx($image->getResource())) + ->setHeight(imagesy($image->getResource())); + return TRUE; + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php index 0158249..fc05907 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php @@ -37,7 +37,7 @@ function testGetAvailableToolkits() { function testLoad() { $image = $this->getImage(); $this->assertTrue(is_object($image), 'Returned an object.'); - $this->assertEqual($this->toolkit->getPluginId(), $image->getToolkitId(), 'Image had toolkit set.'); + $this->assertEqual($this->toolkit->getPluginId(), $image->getToolkit()->getPluginId(), 'Image had toolkit set.'); $this->assertToolkitOperationsCalled(array('load', 'get_info')); } diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestCrop.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestCrop.php new file mode 100644 index 0000000..2c3930d --- /dev/null +++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestCrop.php @@ -0,0 +1,56 @@ +logCall('crop', array($image, $data['x'], $data['y'], $data['width'], $data['height'])); + return TRUE; + } + +} diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestDesaturate.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestDesaturate.php new file mode 100644 index 0000000..4fa60ae --- /dev/null +++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestDesaturate.php @@ -0,0 +1,50 @@ +logCall('desaturate', array($image)); + return TRUE; + } + +} diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestResize.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestResize.php new file mode 100644 index 0000000..07eea38 --- /dev/null +++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestResize.php @@ -0,0 +1,52 @@ +logCall('resize', array($image, $data['width'], $data['height'])); + return TRUE; + } + +} diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestRotate.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestRotate.php new file mode 100644 index 0000000..1cfc6f6 --- /dev/null +++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/Operation/TestRotate.php @@ -0,0 +1,58 @@ +logCall('rotate', array($image, $data['degrees'], $data['background'])); + return TRUE; + } + +} diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php index 43800617..4bf7555 100644 --- a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php +++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php @@ -9,6 +9,7 @@ use Drupal\Core\Plugin\PluginBase; use Drupal\Core\Image\ImageInterface; +use Drupal\Core\ImageToolkit\ImageToolkitBase; use Drupal\Core\ImageToolkit\ImageToolkitInterface; /** @@ -19,7 +20,7 @@ * title = @Translation("A dummy toolkit that works") * ) */ -class TestToolkit extends PluginBase implements ImageToolkitInterface { +class TestToolkit extends ImageToolkitBase implements ImageToolkitInterface { /** * {@inheritdoc} @@ -74,38 +75,6 @@ public function save(ImageInterface $image, $destination) { } /** - * {@inheritdoc} - */ - public function crop(ImageInterface $image, $x, $y, $width, $height) { - $this->logCall('crop', array($image, $x, $y, $width, $height)); - return TRUE; - } - - /** - * {@inheritdoc} - */ - public function resize(ImageInterface $image, $width, $height) { - $this->logCall('resize', array($image, $width, $height)); - return TRUE; - } - - /** - * {@inheritdoc} - */ - public function rotate(ImageInterface $image, $degrees, $background = NULL) { - $this->logCall('rotate', array($image, $degrees, $background)); - return TRUE; - } - - /** - * {@inheritdoc} - */ - public function desaturate(ImageInterface $image) { - $this->logCall('desaturate', array($image)); - return TRUE; - } - - /** * Stores the values passed to a toolkit call. * * @param string $op @@ -117,7 +86,7 @@ public function desaturate(ImageInterface $image) { * @see \Drupal\system\Tests\Image\ToolkitTestBase::imageTestReset() * @see \Drupal\system\Tests\Image\ToolkitTestBase::imageTestGetAllCalls() */ - protected function logCall($op, $args) { + public function logCall($op, $args) { $results = \Drupal::state()->get('image_test.results') ?: array(); $results[$op][] = $args; \Drupal::state()->set('image_test.results', $results); diff --git a/core/tests/Drupal/Tests/Core/Image/ImageTest.php b/core/tests/Drupal/Tests/Core/Image/ImageTest.php index d2a5b61..008d784 100644 --- a/core/tests/Drupal/Tests/Core/Image/ImageTest.php +++ b/core/tests/Drupal/Tests/Core/Image/ImageTest.php @@ -154,10 +154,10 @@ public function testSetSource() { } /** - * Tests \Drupal\Core\Image\Image::getToolkitId(). + * Tests \Drupal\Core\Image\Image::getToolkit(). */ - public function testGetToolkitId() { - $this->assertEquals($this->image->getToolkitId(), 'gd'); + public function testGetToolkit() { + $this->assertEquals($this->image->getToolkit()->getPluginId(), 'gd'); } /** @@ -219,10 +219,11 @@ public function testProcessInfoFails() { */ public function testScaleWidth() { $this->toolkit->expects($this->once()) - ->method('resize') + ->method('apply') + ->with($this->equalTo('resize')) ->will($this->returnArgument(2)); - $height = $this->image->scale(44); - $this->assertEquals($height, 50); + $result = $this->image->scale(44); + $this->assertEquals($result['height'], 50); } /** @@ -230,11 +231,12 @@ public function testScaleWidth() { */ public function testScaleHeight() { $this->toolkit->expects($this->once()) - ->method('resize') - ->will($this->returnArgument(1)); + ->method('apply') + ->with($this->equalTo('resize')) + ->will($this->returnArgument(2)); - $width = $this->image->scale(NULL, 50); - $this->assertEquals($width, 44); + $result = $this->image->scale(NULL, 50); + $this->assertEquals($result['width'], 44); } /** @@ -243,55 +245,52 @@ public function testScaleHeight() { public function testScaleSame() { // Dimensions are the same, resize should not be called. $this->toolkit->expects($this->never()) - ->method('resize') - ->will($this->returnArgument(1)); + ->method('apply'); - $width = $this->image->scale(88, 100); - $this->assertEquals($width, 88); + $this->image->scale(88, 100); } /** * Tests \Drupal\Core\Image\Image::scaleAndCrop(). */ public function testScaleAndCropWidth() { - $this->toolkit->expects($this->once()) - ->method('resize') - ->will($this->returnValue(TRUE)); - - $this->toolkit->expects($this->once()) - ->method('crop') - ->will($this->returnArgument(1)); + $this->toolkit->expects($this->exactly(2)) + ->method('apply') + ->with($this->logicalOr( + $this->equalTo('resize'), + $this->equalTo('crop') + )) + ->will($this->returnArgument(2)); - $x = $this->image->scaleAndCrop(34, 50); - $this->assertEquals($x, 5); + $result = $this->image->scaleAndCrop(34, 50); + $this->assertEquals($result['x'], 5); } /** * Tests \Drupal\Core\Image\Image::scaleAndCrop(). */ public function testScaleAndCropHeight() { - $this->toolkit->expects($this->once()) - ->method('resize') - ->will($this->returnValue(TRUE)); - - $this->toolkit->expects($this->once()) - ->method('crop') + $this->toolkit->expects($this->exactly(2)) + ->method('apply') + ->with($this->logicalOr( + $this->equalTo('resize'), + $this->equalTo('crop') + )) ->will($this->returnArgument(2)); - $y = $this->image->scaleAndCrop(44, 40); - $this->assertEquals($y, 5); + $result = $this->image->scaleAndCrop(44, 40); + $this->assertEquals($result['y'], 5); } /** * Tests \Drupal\Core\Image\Image::scaleAndCrop(). */ public function testScaleAndCropFails() { - $this->toolkit->expects($this->once()) - ->method('resize') + $this->toolkit->expects($this->exactly(1)) + ->method('apply') + ->with($this->equalTo('resize')) ->will($this->returnValue(FALSE)); - $this->toolkit->expects($this->never()) - ->method('crop'); $this->image->scaleAndCrop(44, 40); } @@ -300,11 +299,12 @@ public function testScaleAndCropFails() { */ public function testCropWidth() { $this->toolkit->expects($this->once()) - ->method('crop') - ->will($this->returnArgument(4)); + ->method('apply') + ->with($this->equalTo('crop')) + ->will($this->returnArgument(2)); // Cropping with width only should preserve the aspect ratio. - $height = $this->image->crop(0, 0, 44, NULL); - $this->assertEquals($height, 50); + $result = $this->image->crop(0, 0, 44, NULL); + $this->assertEquals($result['height'], 50); } /** @@ -312,11 +312,12 @@ public function testCropWidth() { */ public function testCropHeight() { $this->toolkit->expects($this->once()) - ->method('crop') - ->will($this->returnArgument(3)); + ->method('apply') + ->with($this->equalTo('crop')) + ->will($this->returnArgument(2)); // Cropping with height only should preserve the aspect ratio. - $width = $this->image->crop(0, 0, NULL, 50); - $this->assertEquals($width, 44); + $result = $this->image->crop(0, 0, NULL, 50); + $this->assertEquals($result['width'], 44); } /** @@ -324,10 +325,11 @@ public function testCropHeight() { */ public function testCrop() { $this->toolkit->expects($this->once()) - ->method('crop') - ->will($this->returnArgument(3)); - $width = $this->image->crop(0, 0, 44, 50); - $this->assertEquals($width, 44); + ->method('apply') + ->with($this->equalTo('crop')) + ->will($this->returnArgument(2)); + $result = $this->image->crop(0, 0, 44, 50); + $this->assertEquals($result['width'], 44); } /** @@ -335,14 +337,15 @@ public function testCrop() { */ public function testResize() { $this->toolkit->expects($this->exactly(2)) - ->method('resize') - ->will($this->returnArgument(1)); + ->method('apply') + ->with($this->equalTo('resize')) + ->will($this->returnArgument(2)); // Resize with integer for width and height. $this->image->resize(30, 40); // Pass a float for width. - $width = $this->image->resize(30.4, 40); + $result = $this->image->resize(30.4, 40); // Ensure that the float was rounded to an integer first. - $this->assertEquals($width, 30); + $this->assertEquals($result['width'], 30); } /** @@ -350,7 +353,8 @@ public function testResize() { */ public function testDesaturate() { $this->toolkit->expects($this->once()) - ->method('desaturate'); + ->method('apply') + ->with($this->equalTo('desaturate')); $this->image->desaturate(); } @@ -359,7 +363,8 @@ public function testDesaturate() { */ public function testRotate() { $this->toolkit->expects($this->once()) - ->method('rotate'); + ->method('apply') + ->with($this->equalTo('rotate')); $this->image->rotate(90); }