diff --git a/core/includes/image.inc b/core/includes/image.inc
index f0b91bf..6deea69 100644
--- a/core/includes/image.inc
+++ b/core/includes/image.inc
@@ -5,6 +5,8 @@
* API for manipulating images.
*/
+use Drupal\system\Plugin\ImageToolkitManager;
+
/**
* @defgroup image Image toolkits
* @{
@@ -39,15 +41,16 @@
* @return
* An array with the toolkit names as keys and the descriptions as values.
*/
-function image_get_available_toolkits() {
- // hook_image_toolkits returns an array of toolkit names.
- $toolkits = module_invoke_all('image_toolkits');
+function image_get_available_toolkits($key = NULL) {
+ // Use plugin system to get list of available toolkits.
+ $manager = new ImageToolkitManager();
+ $toolkits = $manager->getDefinitions();
$output = array();
- foreach ($toolkits as $name => $info) {
+ foreach ($toolkits as $id => $definition) {
// Only allow modules that aren't marked as unavailable.
- if ($info['available']) {
- $output[$name] = $info['title'];
+ if (call_user_func($definition['class'] . '::isAvailable')) {
+ $output[$id] = $definition;
}
}
@@ -58,46 +61,32 @@ function image_get_available_toolkits() {
* Gets the name of the currently used toolkit.
*
* @return
- * String containing the name of the selected toolkit, or FALSE on error.
+ * Object of the default toolkit, or FALSE on error.
*/
-function image_get_toolkit() {
- static $toolkit;
+function image_get_toolkit($toolkit_id = NULL) {
+ static $toolkit = array();
+
+ if (empty($toolkit_id)) {
+ $toolkit_id = config('system.image')->get('toolkit');
+ }
- if (!isset($toolkit)) {
+ if (!isset($toolkit[$toolkit_id])) {
$toolkits = image_get_available_toolkits();
- $toolkit = variable_get('image_toolkit', 'gd');
- if (!isset($toolkits[$toolkit]) || !function_exists('image_' . $toolkit . '_load')) {
+
+ if (!isset($toolkits[$toolkit_id]) || !class_exists($toolkits[$toolkit_id]['class'])) {
// The selected toolkit isn't available so return the first one found. If
// none are available this will return FALSE.
reset($toolkits);
- $toolkit = key($toolkits);
+ $toolkit_id = key($toolkits);
}
- }
- return $toolkit;
-}
-
-/**
- * Invokes the given method using the currently selected toolkit.
- *
- * @param $method
- * A string containing the method to invoke.
- * @param $image
- * An image object returned by image_load().
- * @param $params
- * An optional array of parameters to pass to the toolkit method.
- *
- * @return
- * Mixed values (typically Boolean indicating successful operation).
- */
-function image_toolkit_invoke($method, stdClass $image, array $params = array()) {
- $function = 'image_' . $image->toolkit . '_' . $method;
- if (function_exists($function)) {
- array_unshift($params, $image);
- return call_user_func_array($function, $params);
+ if ($toolkit_id) {
+ $manager = new ImageToolkitManager();
+ $toolkit[$toolkit_id] = $manager->createInstance($toolkit_id);
+ }
}
- watchdog('image', 'The selected image handling toolkit %toolkit can not correctly process %function.', array('%toolkit' => $image->toolkit, '%function' => $function), WATCHDOG_ERROR);
- return FALSE;
+
+ return $toolkit[$toolkit_id];
}
/**
@@ -110,7 +99,7 @@ function image_toolkit_invoke($method, stdClass $image, array $params = array())
* @param $filepath
* String specifying the path of the image file.
* @param $toolkit
- * An optional image toolkit name to override the default.
+ * An optional image toolkit object to override the default.
*
* @return
* FALSE, if the file could not be found or is not an image. Otherwise, a
@@ -134,7 +123,7 @@ function image_get_info($filepath, $toolkit = FALSE) {
$image = new stdClass();
$image->source = $filepath;
$image->toolkit = $toolkit;
- $details = image_toolkit_invoke('get_info', $image);
+ $details = $toolkit->getInfo($image);
if (isset($details) && is_array($details)) {
$details['file_size'] = filesize($filepath);
}
@@ -280,7 +269,7 @@ function image_resize(stdClass $image, $width, $height) {
$width = (int) round($width);
$height = (int) round($height);
- return image_toolkit_invoke('resize', $image, array($width, $height));
+ return $image->toolkit->resize($image, $width, $height);
}
/**
@@ -304,7 +293,7 @@ function image_resize(stdClass $image, $width, $height) {
* @see image_gd_rotate()
*/
function image_rotate(stdClass $image, $degrees, $background = NULL) {
- return image_toolkit_invoke('rotate', $image, array($degrees, $background));
+ return $image->toolkit->rotate($image, $degrees, $background);
}
/**
@@ -336,7 +325,7 @@ function image_crop(stdClass $image, $x, $y, $width, $height) {
$width = (int) round($width);
$height = (int) round($height);
- return image_toolkit_invoke('crop', $image, array($x, $y, $width, $height));
+ return $image->toolkit->crop($image, $x, $y, $width, $height);
}
/**
@@ -352,7 +341,7 @@ function image_crop(stdClass $image, $x, $y, $width, $height) {
* @see image_gd_desaturate()
*/
function image_desaturate(stdClass $image) {
- return image_toolkit_invoke('desaturate', $image);
+ return $image->toolkit->desaturate($image);
}
/**
@@ -363,7 +352,7 @@ function image_desaturate(stdClass $image) {
* @param $file
* Path to an image file.
* @param $toolkit
- * An optional, image toolkit name to override the default.
+ * An optional, image toolkit object to override the default.
*
* @return
* An image object or FALSE if there was a problem loading the file. The
@@ -390,7 +379,7 @@ function image_load($file, $toolkit = FALSE) {
$image->info = image_get_info($file, $toolkit);
if (isset($image->info) && is_array($image->info)) {
$image->toolkit = $toolkit;
- if (image_toolkit_invoke('load', $image)) {
+ if ($toolkit->load($image)) {
return $image;
}
}
@@ -418,7 +407,7 @@ function image_save(stdClass $image, $destination = NULL) {
if (empty($destination)) {
$destination = $image->source;
}
- if ($return = image_toolkit_invoke('save', $image, array($destination))) {
+ if ($return = $image->toolkit->save($image, $destination)) {
// Clear the cached file size and refresh the image information.
clearstatcache(TRUE, $destination);
$image->info = image_get_info($destination, $image->toolkit);
diff --git a/core/modules/image/image.effects.inc b/core/modules/image/image.effects.inc
index 35a6a74..4f7379b 100644
--- a/core/modules/image/image.effects.inc
+++ b/core/modules/image/image.effects.inc
@@ -79,7 +79,7 @@ function image_image_effect_info() {
*/
function image_resize_effect(&$image, $data) {
if (!image_resize($image, $data['width'], $data['height'])) {
- watchdog('image', 'Image resize failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit, '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+ watchdog('image', 'Image resize failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => get_class($image->toolkit), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -130,7 +130,7 @@ function image_scale_effect(&$image, $data) {
);
if (!image_scale($image, $data['width'], $data['height'], $data['upscale'])) {
- watchdog('image', 'Image scale failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit, '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+ watchdog('image', 'Image scale failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => get_class($image->toolkit), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -184,7 +184,7 @@ function image_crop_effect(&$image, $data) {
$x = image_filter_keyword($x, $image->info['width'], $data['width']);
$y = image_filter_keyword($y, $image->info['height'], $data['height']);
if (!image_crop($image, $x, $y, $data['width'], $data['height'])) {
- watchdog('image', 'Image crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit, '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+ watchdog('image', 'Image crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => get_class($image->toolkit), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -206,7 +206,7 @@ function image_crop_effect(&$image, $data) {
*/
function image_scale_and_crop_effect(&$image, $data) {
if (!image_scale_and_crop($image, $data['width'], $data['height'])) {
- watchdog('image', 'Image scale and crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit, '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+ watchdog('image', 'Image scale and crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => get_class($image->toolkit), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -225,7 +225,7 @@ function image_scale_and_crop_effect(&$image, $data) {
*/
function image_desaturate_effect(&$image, $data) {
if (!image_desaturate($image)) {
- watchdog('image', 'Image desaturate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit, '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+ watchdog('image', 'Image desaturate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => get_class($image->toolkit), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -278,7 +278,7 @@ function image_rotate_effect(&$image, $data) {
}
if (!image_rotate($image, $data['degrees'], $data['bgcolor'])) {
- watchdog('image', 'Image rotate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit, '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+ watchdog('image', 'Image rotate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => get_class($image->toolkit), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
index fb3269c..b7a5b67 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
@@ -20,7 +20,7 @@ class ImageEffectsTest extends ToolkitTestBase {
*
* @var array
*/
- public static $modules = array('image_test');
+ public static $modules = array('image_test', 'image');
public static function getInfo() {
return array(
diff --git a/core/modules/system/config/system.image.gd.yml b/core/modules/system/config/system.image.gd.yml
new file mode 100644
index 0000000..fbc379f
--- /dev/null
+++ b/core/modules/system/config/system.image.gd.yml
@@ -0,0 +1 @@
+jpeg_quality: '75'
diff --git a/core/modules/system/config/system.image.yml b/core/modules/system/config/system.image.yml
new file mode 100644
index 0000000..9a1688f
--- /dev/null
+++ b/core/modules/system/config/system.image.yml
@@ -0,0 +1 @@
+toolkit: gd
diff --git a/core/modules/system/image.gd.inc b/core/modules/system/image.gd.inc
deleted file mode 100644
index f6f12ae..0000000
--- a/core/modules/system/image.gd.inc
+++ /dev/null
@@ -1,367 +0,0 @@
- t('The GD toolkit is installed and working properly.')
- );
-
- $form['image_jpeg_quality'] = array(
- '#type' => 'number',
- '#title' => t('JPEG quality'),
- '#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
- '#min' => 0,
- '#max' => 100,
- '#default_value' => variable_get('image_jpeg_quality', 75),
- '#field_suffix' => t('%'),
- );
-
- return $form;
- }
- else {
- form_set_error('image_toolkit', t('The GD image toolkit requires that the GD module for PHP be installed and configured properly. For more information see PHP\'s image documentation.', array('@url' => 'http://php.net/image')));
- return FALSE;
- }
-}
-
-/**
- * Verify GD2 settings (that the right version is actually installed).
- *
- * @return
- * A boolean indicating if the GD toolkit is available on this machine.
- */
-function image_gd_check_settings() {
- if ($check = get_extension_funcs('gd')) {
- if (in_array('imagegd2', $check)) {
- // GD2 support is available.
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/**
- * Scale an image to the specified size using GD.
- *
- * @param $image
- * An image object. The $image->resource, $image->info['width'], and
- * $image->info['height'] values will be modified by this call.
- * @param $width
- * The new width of the resized image, in pixels.
- * @param $height
- * The new height of the resized image, in pixels.
- * @return
- * TRUE or FALSE, based on success.
- *
- * @see image_resize()
- */
-function image_gd_resize(stdClass $image, $width, $height) {
- $res = image_gd_create_tmp($image, $width, $height);
-
- if (!imagecopyresampled($res, $image->resource, 0, 0, 0, 0, $width, $height, $image->info['width'], $image->info['height'])) {
- return FALSE;
- }
-
- imagedestroy($image->resource);
- // Update image object.
- $image->resource = $res;
- $image->info['width'] = $width;
- $image->info['height'] = $height;
- return TRUE;
-}
-
-/**
- * Rotate an image the given number of degrees.
- *
- * @param $image
- * An image object. The $image->resource, $image->info['width'], and
- * $image->info['height'] values will be modified by this call.
- * @param $degrees
- * The number of (clockwise) degrees to rotate the image.
- * @param $background
- * 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
- * TRUE or FALSE, based on success.
- *
- * @see image_rotate()
- */
-function image_gd_rotate(stdClass $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->source));
- return FALSE;
- }
-
- $width = $image->info['width'];
- $height = $image->info['height'];
-
- // 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->resource, $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->resource);
-
- // If no transparent colors, use white.
- if ($background == 0) {
- $background = imagecolorallocatealpha($image->resource, 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->info['extension'] == 'gif') {
- $transparent_index = imagecolortransparent($image->resource);
- if ($transparent_index != 0) {
- $transparent_gif_color = imagecolorsforindex($image->resource, $transparent_index);
- }
- }
-
- $image->resource = imagerotate($image->resource, 360 - $degrees, $background);
-
- // GIFs need to reassign the transparent color after performing the rotate.
- if (isset($transparent_gif_color)) {
- $background = imagecolorexactalpha($image->resource, $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']);
- imagecolortransparent($image->resource, $background);
- }
-
- $image->info['width'] = imagesx($image->resource);
- $image->info['height'] = imagesy($image->resource);
- return TRUE;
-}
-
-/**
- * Crop an image using the GD toolkit.
- *
- * @param $image
- * An image object. The $image->resource, $image->info['width'], and
- * $image->info['height'] values will be modified by this call.
- * @param $x
- * The starting x offset at which to start the crop, in pixels.
- * @param $y
- * The starting y offset at which to start the crop, in pixels.
- * @param $width
- * The width of the cropped area, in pixels.
- * @param $height
- * The height of the cropped area, in pixels.
- * @return
- * TRUE or FALSE, based on success.
- *
- * @see image_crop()
- */
-function image_gd_crop(stdClass $image, $x, $y, $width, $height) {
- $res = image_gd_create_tmp($image, $width, $height);
-
- if (!imagecopyresampled($res, $image->resource, 0, 0, $x, $y, $width, $height, $width, $height)) {
- return FALSE;
- }
-
- // Destroy the original image and return the modified image.
- imagedestroy($image->resource);
- $image->resource = $res;
- $image->info['width'] = $width;
- $image->info['height'] = $height;
- return TRUE;
-}
-
-/**
- * Convert an image resource to grayscale.
- *
- * Note that transparent GIFs loose transparency when desaturated.
- *
- * @param $image
- * An image object. The $image->resource value will be modified by this call.
- * @return
- * TRUE or FALSE, based on success.
- *
- * @see image_desaturate()
- */
-function image_gd_desaturate(stdClass $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->source));
- return FALSE;
- }
-
- return imagefilter($image->resource, IMG_FILTER_GRAYSCALE);
-}
-
-/**
- * GD helper function to create an image resource from a file.
- *
- * @param $image
- * An image object. The $image->resource value will populated by this call.
- * @return
- * TRUE or FALSE, based on success.
- *
- * @see image_load()
- */
-function image_gd_load(stdClass $image) {
- $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
- $function = 'imagecreatefrom' . $extension;
- if (function_exists($function) && $image->resource = $function($image->source)) {
- if (!imageistruecolor($image->resource)) {
- // Convert indexed images to true color, so that filters work
- // correctly and don't result in unnecessary dither.
- $new_image = image_gd_create_tmp($image, $image->info['width'], $image->info['height']);
- imagecopy($new_image, $image->resource, 0, 0, 0, 0, $image->info['width'], $image->info['height']);
- imagedestroy($image->resource);
- $image->resource = $new_image;
- }
- return (bool) $image->resource;
- }
-
- return FALSE;
-}
-
-/**
- * GD helper to write an image resource to a destination file.
- *
- * @param $image
- * An image object.
- * @param $destination
- * A string file URI or path where the image should be saved.
- * @return
- * TRUE or FALSE, based on success.
- *
- * @see image_save()
- */
-function image_gd_save(stdClass $image, $destination) {
- $scheme = file_uri_scheme($destination);
- // Work around lack of stream wrapper support in imagejpeg() and imagepng().
- if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
- // If destination is not local, save image to temporary local file.
- $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
- if (!isset($local_wrappers[$scheme])) {
- $permanent_destination = $destination;
- $destination = drupal_tempnam('temporary://', 'gd_');
- }
- // Convert stream wrapper URI to normal path.
- $destination = drupal_realpath($destination);
- }
-
- $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
- $function = 'image' . $extension;
- if (!function_exists($function)) {
- return FALSE;
- }
- if ($extension == 'jpeg') {
- $success = $function($image->resource, $destination, variable_get('image_jpeg_quality', 75));
- }
- else {
- // Always save PNG images with full transparency.
- if ($extension == 'png') {
- imagealphablending($image->resource, FALSE);
- imagesavealpha($image->resource, TRUE);
- }
- $success = $function($image->resource, $destination);
- }
- // Move temporary local file to remote destination.
- if (isset($permanent_destination) && $success) {
- return (bool) file_unmanaged_move($destination, $permanent_destination, FILE_EXISTS_REPLACE);
- }
- return $success;
-}
-
-/**
- * Create a truecolor image preserving transparency from a provided image.
- *
- * @param $image
- * An image object.
- * @param $width
- * The new width of the new image, in pixels.
- * @param $height
- * The new height of the new image, in pixels.
- * @return
- * A GD image handle.
- */
-function image_gd_create_tmp(stdClass $image, $width, $height) {
- $res = imagecreatetruecolor($width, $height);
-
- if ($image->info['extension'] == 'gif') {
- // Grab transparent color index from image resource.
- $transparent = imagecolortransparent($image->resource);
-
- if ($transparent >= 0) {
- // The original must have a transparent color, allocate to the new image.
- $transparent_color = imagecolorsforindex($image->resource, $transparent);
- $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
-
- // Flood with our new transparent color.
- imagefill($res, 0, 0, $transparent);
- imagecolortransparent($res, $transparent);
- }
- }
- elseif ($image->info['extension'] == 'png') {
- imagealphablending($res, FALSE);
- $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
- imagefill($res, 0, 0, $transparency);
- imagealphablending($res, TRUE);
- imagesavealpha($res, TRUE);
- }
- else {
- imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
- }
-
- return $res;
-}
-
-/**
- * Get details about an image.
- *
- * @param $image
- * An image object.
- * @return
- * FALSE, if the file could not be found or is not an image. Otherwise, a
- * keyed array containing information about the image:
- * - "width": Width, in pixels.
- * - "height": Height, in pixels.
- * - "extension": Commonly used file extension for the image.
- * - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png').
- *
- * @see image_get_info()
- */
-function image_gd_get_info(stdClass $image) {
- $details = FALSE;
- $data = getimagesize($image->source);
-
- if (isset($data) && is_array($data)) {
- $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
- $extension = isset($extensions[$data[2]]) ? $extensions[$data[2]] : '';
- $details = array(
- 'width' => $data[0],
- 'height' => $data[1],
- 'extension' => $extension,
- 'mime_type' => $data['mime'],
- );
- }
-
- return $details;
-}
-
-/**
- * @} End of "addtogroup image".
- */
diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
new file mode 100644
index 0000000..21a1d07
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
@@ -0,0 +1,156 @@
+resource, $image->info['width'], and
+ * $image->info['height'] values will be modified by this call.
+ * @param $width
+ * The new width of the resized image, in pixels.
+ * @param $height
+ * The new height of the resized image, in pixels.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_resize()
+ */
+ function resize(stdClass $image, $width, $height);
+
+ /**
+ * Rotate an image the given number of degrees.
+ *
+ * @param $image
+ * An image object. The $image->resource, $image->info['width'], and
+ * $image->info['height'] values will be modified by this call.
+ * @param $degrees
+ * The number of (clockwise) degrees to rotate the image.
+ * @param $background
+ * 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
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_rotate()
+ */
+ function rotate(stdClass $image, $degrees, $background = NULL);
+
+ /**
+ * Crop an image.
+ *
+ * @param $image
+ * An image object. The $image->resource, $image->info['width'], and
+ * $image->info['height'] values will be modified by this call.
+ * @param $x
+ * The starting x offset at which to start the crop, in pixels.
+ * @param $y
+ * The starting y offset at which to start the crop, in pixels.
+ * @param $width
+ * The width of the cropped area, in pixels.
+ * @param $height
+ * The height of the cropped area, in pixels.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_crop()
+ */
+ function crop(stdClass $image, $x, $y, $width, $height);
+
+ /**
+ * Convert an image resource to grayscale.
+ *
+ * Note that transparent GIFs loose transparency when desaturated.
+ *
+ * @param $image
+ * An image object. The $image->resource value will be modified by this call.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_desaturate()
+ */
+ function desaturate(stdClass $image);
+
+ /**
+ * Create an image resource from a file.
+ *
+ * @param $image
+ * An image object. The $image->resource value will populated by this call.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_load()
+ */
+ function load(stdClass $image);
+
+ /**
+ * Write an image resource to a destination file.
+ *
+ * @param $image
+ * An image object.
+ * @param $destination
+ * A string file URI or path where the image should be saved.
+ * @return
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_save()
+ */
+ function save(stdClass $image, $destination);
+
+ /**
+ * Get details about an image.
+ *
+ * @param $image
+ * An image object.
+ * @return
+ * FALSE, if the file could not be found or is not an image. Otherwise, a
+ * keyed array containing information about the image:
+ * - "width": Width, in pixels.
+ * - "height": Height, in pixels.
+ * - "extension": Commonly used file extension for the image.
+ * - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png').
+ *
+ * @see image_get_info()
+ */
+ function getInfo(stdClass $image);
+
+ /**
+ * Verify Image Toolkit is set up correctly.
+ *
+ * @return
+ * A boolean indicating if the GD toolkit is available on this machine.
+ */
+ static function isAvailable();
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
new file mode 100644
index 0000000..d801de0
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
@@ -0,0 +1,24 @@
+discovery = new HookDiscovery('image_toolkits');
+ $this->discovery = new AnnotatedClassDiscovery('system', 'imagetoolkit');
+ $this->factory = new DefaultFactory($this->discovery);
+ }
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/system/imagetoolkit/GDToolkit.php b/core/modules/system/lib/Drupal/system/Plugin/system/imagetoolkit/GDToolkit.php
new file mode 100644
index 0000000..313b9df
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/system/imagetoolkit/GDToolkit.php
@@ -0,0 +1,295 @@
+ t('The GD toolkit is installed and working properly.'),
+ );
+
+ $form['image_jpeg_quality'] = array(
+ '#type' => 'number',
+ '#title' => t('JPEG quality'),
+ '#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
+ '#min' => 0,
+ '#max' => 100,
+ '#default_value' => config('system.image.gd')->get('jpeg_quality'),
+ '#field_suffix' => t('%'),
+ );
+ return $form;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::settingsFormSubmit().
+ */
+ function settingsFormSubmit($form, &$form_state) {
+ config('system.image.gd')
+ ->set('jpeg_quality', $form_state['values']['gd']['image_jpeg_quality'])
+ ->save();
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::resize().
+ */
+ function resize(stdClass $image, $width, $height) {
+ $res = $this->createTmp($image, $width, $height);
+
+ if (!imagecopyresampled($res, $image->resource, 0, 0, 0, 0, $width, $height, $image->info['width'], $image->info['height'])) {
+ return FALSE;
+ }
+
+ imagedestroy($image->resource);
+ // Update image object.
+ $image->resource = $res;
+ $image->info['width'] = $width;
+ $image->info['height'] = $height;
+ return TRUE;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::rotate().
+ */
+ function rotate(stdClass $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->source));
+ return FALSE;
+ }
+
+ $width = $image->info['width'];
+ $height = $image->info['height'];
+
+ // 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->resource, $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->resource);
+
+ // If no transparent colors, use white.
+ if ($background == 0) {
+ $background = imagecolorallocatealpha($image->resource, 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->info['extension'] == 'gif') {
+ $transparent_index = imagecolortransparent($image->resource);
+ if ($transparent_index != 0) {
+ $transparent_gif_color = imagecolorsforindex($image->resource, $transparent_index);
+ }
+ }
+
+ $image->resource = imagerotate($image->resource, 360 - $degrees, $background);
+
+ // GIFs need to reassign the transparent color after performing the rotate.
+ if (isset($transparent_gif_color)) {
+ $background = imagecolorexactalpha($image->resource, $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']);
+ imagecolortransparent($image->resource, $background);
+ }
+
+ $image->info['width'] = imagesx($image->resource);
+ $image->info['height'] = imagesy($image->resource);
+ return TRUE;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::crop().
+ */
+ function crop(stdClass $image, $x, $y, $width, $height) {
+ $res = $this->createTmp($image, $width, $height);
+
+ if (!imagecopyresampled($res, $image->resource, 0, 0, $x, $y, $width, $height, $width, $height)) {
+ return FALSE;
+ }
+
+ // Destroy the original image and return the modified image.
+ imagedestroy($image->resource);
+ $image->resource = $res;
+ $image->info['width'] = $width;
+ $image->info['height'] = $height;
+ return TRUE;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::desaturate().
+ */
+ function desaturate(stdClass $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->source));
+ return FALSE;
+ }
+
+ return imagefilter($image->resource, IMG_FILTER_GRAYSCALE);
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::load().
+ */
+ function load(stdClass $image) {
+ $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
+ $function = 'imagecreatefrom' . $extension;
+ if (function_exists($function) && $image->resource = $function($image->source)) {
+ if (!imageistruecolor($image->resource)) {
+ // Convert indexed images to true color, so that filters work
+ // correctly and don't result in unnecessary dither.
+ $new_image = $this->createTmp($image, $image->info['width'], $image->info['height']);
+ imagecopy($new_image, $image->resource, 0, 0, 0, 0, $image->info['width'], $image->info['height']);
+ imagedestroy($image->resource);
+ $image->resource = $new_image;
+ }
+ return (bool) $image->resource;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::save().
+ */
+ function save(stdClass $image, $destination) {
+ $scheme = file_uri_scheme($destination);
+ // Work around lack of stream wrapper support in imagejpeg() and imagepng().
+ if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
+ // If destination is not local, save image to temporary local file.
+ $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
+ if (!isset($local_wrappers[$scheme])) {
+ $permanent_destination = $destination;
+ $destination = drupal_tempnam('temporary://', 'gd_');
+ }
+ // Convert stream wrapper URI to normal path.
+ $destination = drupal_realpath($destination);
+ }
+
+ $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
+ $function = 'image' . $extension;
+ if (!function_exists($function)) {
+ return FALSE;
+ }
+ if ($extension == 'jpeg') {
+ $success = $function($image->resource, $destination, config('system.image.gd')->get('jpeg_quality'));
+ }
+ else {
+ // Always save PNG images with full transparency.
+ if ($extension == 'png') {
+ imagealphablending($image->resource, FALSE);
+ imagesavealpha($image->resource, TRUE);
+ }
+ $success = $function($image->resource, $destination);
+ }
+ // Move temporary local file to remote destination.
+ if (isset($permanent_destination) && $success) {
+ return (bool) file_unmanaged_move($destination, $permanent_destination, FILE_EXISTS_REPLACE);
+ }
+ return $success;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::getInfo().
+ */
+ function getInfo(stdClass $image) {
+ $details = FALSE;
+ $data = getimagesize($image->source);
+
+ if (isset($data) && is_array($data)) {
+ $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
+ $extension = isset($extensions[$data[2]]) ? $extensions[$data[2]] : '';
+ $details = array(
+ 'width' => $data[0],
+ 'height' => $data[1],
+ 'extension' => $extension,
+ 'mime_type' => $data['mime'],
+ );
+ }
+
+ return $details;
+ }
+
+ /**
+ * Create a truecolor image preserving transparency from a provided image.
+ *
+ * @param $image
+ * An image object.
+ * @param $width
+ * The new width of the new image, in pixels.
+ * @param $height
+ * The new height of the new image, in pixels.
+ * @return
+ * A GD image handle.
+ */
+ function createTmp(stdClass $image, $width, $height) {
+ $res = imagecreatetruecolor($width, $height);
+
+ if ($image->info['extension'] == 'gif') {
+ // Grab transparent color index from image resource.
+ $transparent = imagecolortransparent($image->resource);
+
+ if ($transparent >= 0) {
+ // The original must have a transparent color, allocate to the new image.
+ $transparent_color = imagecolorsforindex($image->resource, $transparent);
+ $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
+
+ // Flood with our new transparent color.
+ imagefill($res, 0, 0, $transparent);
+ imagecolortransparent($res, $transparent);
+ }
+ }
+ elseif ($image->info['extension'] == 'png') {
+ imagealphablending($res, FALSE);
+ $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
+ imagefill($res, 0, 0, $transparency);
+ imagealphablending($res, TRUE);
+ imagesavealpha($res, TRUE);
+ }
+ else {
+ imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
+ }
+
+ return $res;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::isAvailable().
+ */
+ public static function isAvailable() {
+ if ($check = get_extension_funcs('gd')) {
+ if (in_array('imagegd2', $check)) {
+ // GD2 support is available.
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
index 611126d..92db417 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
@@ -8,6 +8,7 @@
namespace Drupal\system\Tests\Image;
use Drupal\simpletest\WebTestBase;
+use Drupal\system\Plugin\ImageToolkitManager;
/**
* Test the core GD image manipulation functions.
@@ -35,8 +36,9 @@ class ToolkitGdTest extends WebTestBase {
}
protected function checkRequirements() {
- image_get_available_toolkits();
- if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) {
+ $manager = new ImageToolkitManager();
+ $definition = $manager->getDefinition('gd');
+ if (!call_user_func($definition['class'] . '::isAvailable')) {
return array(
'Image manipulations for the GD toolkit cannot run because the GD toolkit is not available.',
);
@@ -204,7 +206,7 @@ class ToolkitGdTest extends WebTestBase {
foreach ($files as $file) {
foreach ($operations as $op => $values) {
// Load up a fresh image.
- $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, 'gd');
+ $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, image_get_toolkit('gd'));
if (!$image) {
$this->fail(t('Could not load image %file.', array('%file' => $file)));
continue 2;
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 091fc5a..c70719a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
@@ -36,7 +36,7 @@ class ToolkitTest extends ToolkitTestBase {
function testLoad() {
$image = image_load($this->file, $this->toolkit);
$this->assertTrue(is_object($image), t('Returned an object.'));
- $this->assertEqual($this->toolkit, $image->toolkit, t('Image had toolkit set.'));
+ $this->assertEqual($this->toolkit, $image->toolkit);
$this->assertToolkitOperationsCalled(array('load', 'get_info'));
}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
index ac47080..d06938a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
@@ -30,7 +30,7 @@ abstract class ToolkitTestBase extends WebTestBase {
parent::setUp();
// Use the image_test.module's test toolkit.
- $this->toolkit = 'test';
+ $this->toolkit = image_get_toolkit('test');
// Pick a file for testing.
$file = current($this->drupalGetTestFiles('image'));
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 2bf4343..34f5180 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -5,6 +5,7 @@
* Admin page callbacks for the system module.
*/
+use Drupal\system\Plugin\ImageToolkitManager;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
@@ -1871,39 +1872,68 @@ function system_file_system_settings() {
* Form builder; Configure site image toolkit usage.
*
* @ingroup forms
- * @see system_settings_form()
+ * @see system_image_toolkit_settings_submit()
*/
-function system_image_toolkit_settings() {
+function system_image_toolkit_settings($form, &$form_state) {
+ $config = config('system.image');
+
$toolkits_available = image_get_available_toolkits();
- $current_toolkit = image_get_toolkit();
+ $current_toolkit = config('system.image')->get('toolkit');
- if (count($toolkits_available) == 0) {
- variable_del('image_toolkit');
- $form['image_toolkit_help'] = array(
- '#markup' => t("No image toolkits were detected. Drupal includes support for PHP's built-in image processing functions but they were not detected on this system. You should consult your system administrator to have them enabled, or try using a third party toolkit.", array('gd-link' => url('http://php.net/gd'))),
- );
- return $form;
- }
+ // If we have avialable toolkits allow the user to select the image toolkit to
+ // use and load their settings forms.
+ if (count($toolkits_available)) {
+ $options = array();
+ foreach($toolkits_available as $id => $definition) {
+ $options[$id] = $definition['title'];
+ }
- if (count($toolkits_available) > 1) {
$form['image_toolkit'] = array(
'#type' => 'radios',
'#title' => t('Select an image processing toolkit'),
- '#default_value' => variable_get('image_toolkit', $current_toolkit),
- '#options' => $toolkits_available
+ '#default_value' => $current_toolkit,
+ '#options' => $options,
);
+
+ // Get the toolkit settings forms.
+ $manager = new ImageToolkitManager();
+ foreach ($toolkits_available as $id => $definition) {
+ $toolkit = $manager->createInstance($id);
+ $form['image_toolkit_settings'][$id] = array(
+ '#type' => 'fieldset',
+ '#title' => t('@toolkit settings', array('@toolkit' => $definition['title'])),
+ '#collapsible' => TRUE,
+ '#collapsed' => ($id == $current_toolkit) ? FALSE : TRUE,
+ '#tree' => TRUE,
+ );
+ $form['image_toolkit_settings'][$id] += $toolkit->settingsForm();
+ }
+
+ $form = system_config_form($form, $form_state);
}
else {
- variable_set('image_toolkit', key($toolkits_available));
+ form_set_error('image_toolkit', t('There are no image toolkits available. Drupal comes with support for PHP\'s GD image toolkit. This requires that the GD module for PHP be installed and configured properly. For more information see PHP\'s image documentation.', array('@url' => 'http://php.net/image')));
}
- // Get the toolkit's settings form.
- $function = 'image_' . $current_toolkit . '_settings';
- if (function_exists($function)) {
- $form['image_toolkit_settings'] = $function();
- }
+ return $form;
+}
- return system_settings_form($form);
+/**
+ * Form submission handler for system_image_toolkit_settings().
+ */
+function system_image_toolkit_settings_submit($form, &$form_state) {
+ config('system.image')
+ ->set('toolkit', $form_state['values']['image_toolkit'])
+ ->save();
+
+ // Call the form submit handler for each of the toolkits.
+ // Get the toolkit settings forms.
+ $manager = new ImageToolkitManager();
+ $toolkits_available = image_get_available_toolkits();
+ foreach ($toolkits_available as $id => $definition) {
+ $toolkit = $manager->createInstance($id);
+ $toolkit->settingsFormSubmit($form, $form_state);
+ }
}
/**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 486afba..4021e52 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1884,6 +1884,19 @@ function system_update_8019() {
}
/**
+ * Moves image toolkit settings from variable to config.
+ *
+ * @ingroup config_upgrade
+ */
+function system_update_8020() {
+ update_variables_to_config('system.image', array(
+ 'image_toolkit' => 'toolkit',
+ ));
+ update_variables_to_config('system.image.gd', array(
+ 'image_jpeg_quality' => 'jpeg_quality',
+ ));
+}
+/**
* @} End of "defgroup updates-7.x-to-8.x".
* The next series of updates should start at 9000.
*/
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index ad4c5dd..6410f27 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -3839,19 +3839,6 @@ function theme_system_compact_link() {
}
/**
- * Implements hook_image_toolkits().
- */
-function system_image_toolkits() {
- include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'image.gd.inc';
- return array(
- 'gd' => array(
- 'title' => t('GD2 image manipulation toolkit'),
- 'available' => function_exists('image_gd_check_settings') && image_gd_check_settings(),
- ),
- );
-}
-
-/**
* Attempts to get a file using drupal_http_request and to store it locally.
*
* @param $url
diff --git a/core/modules/system/tests/modules/image_test/image_test.module b/core/modules/system/tests/modules/image_test/image_test.module
index de640f0..c6291f2 100644
--- a/core/modules/system/tests/modules/image_test/image_test.module
+++ b/core/modules/system/tests/modules/image_test/image_test.module
@@ -6,22 +6,6 @@
*/
/**
- * Implements hook_image_toolkits().
- */
-function image_test_image_toolkits() {
- return array(
- 'test' => array(
- 'title' => t('A dummy toolkit that works'),
- 'available' => TRUE,
- ),
- 'broken' => array(
- 'title' => t('A dummy toolkit that is "broken"'),
- 'available' => FALSE,
- ),
- );
-}
-
-/**
* Reset/initialize the history of calls to the toolkit functions.
*
* @see image_test_get_all_calls()
diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/BrokenToolkit.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/BrokenToolkit.php
new file mode 100644
index 0000000..90ce5e1
--- /dev/null
+++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/BrokenToolkit.php
@@ -0,0 +1,29 @@
+_logCall('settings', array());
+ return array();
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::settingsFormSubmit().
+ */
+ function settingsFormSubmit($form, &$form_state) {}
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::getInfo().
+ */
+ function getInfo(stdClass $image) {
+ $this->_logCall('get_info', array($image));
+ return array();
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::load().
+ */
+ function load(stdClass $image) {
+ $this->_logCall('load', array($image));
+ return $image;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::save().
+ */
+ function save(stdClass $image, $destination) {
+ $this->_logCall('save', array($image, $destination));
+ // Return false so that image_save() doesn't try to chmod the destination
+ // file that we didn't bother to create.
+ return FALSE;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::crop().
+ */
+ function crop(stdClass $image, $x, $y, $width, $height) {
+ $this->_logCall('crop', array($image, $x, $y, $width, $height));
+ return TRUE;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::resize().
+ */
+ function resize(stdClass $image, $width, $height) {
+ $this->_logCall('resize', array($image, $width, $height));
+ return TRUE;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::rotate().
+ */
+ function rotate(stdClass $image, $degrees, $background = NULL) {
+ $this->_logCall('rotate', array($image, $degrees, $background));
+ return TRUE;
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::desaturate().
+ */
+ function desaturate(stdClass $image) {
+ $this->_logCall('desaturate', array($image));
+ return TRUE;
+ }
+
+ /**
+ * Store the values passed to a toolkit call.
+ *
+ * @param $op
+ * One of the image toolkit operations: 'get_info', 'load', 'save',
+ * 'settings', 'resize', 'rotate', 'crop', 'desaturate'.
+ * @param $args
+ * Values passed to hook.
+ *
+ * @see image_test_get_all_calls()
+ * @see image_test_reset()
+ */
+ function _logCall($op, $args) {
+ $results = variable_get('image_test_results', array());
+ $results[$op][] = $args;
+ variable_set('image_test_results', $results);
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::isAvailable().
+ */
+ static function isAvailable() {
+ return TRUE;
+ }
+}