summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcatch2013-03-25 11:55:05 (GMT)
committercatch2013-03-25 11:55:05 (GMT)
commitc07e727d8fd636846cca6975acd50b2725c502c5 (patch)
tree1472ea99b3748fa251e36fe71a9c0dc56af69e68
parent54d4992fc8ddf9ab5194db38134c43fbaf2671c2 (diff)
Issue #1664844 by alexpott, cweagans, heyrocker, eojthebrave, ACF, Berdir: Convert image toolkits into plugins.
-rw-r--r--core/includes/image.inc247
-rw-r--r--core/lib/Drupal/Core/CoreBundle.php10
-rw-r--r--core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php2
-rw-r--r--core/modules/image/image.effects.inc76
-rw-r--r--core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php14
-rw-r--r--core/modules/system/config/system.image.gd.yml1
-rw-r--r--core/modules/system/config/system.image.yml1
-rw-r--r--core/modules/system/image.gd.inc398
-rw-r--r--core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php163
-rw-r--r--core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php78
-rw-r--r--core/modules/system/lib/Drupal/system/Plugin/system/imagetoolkit/GDToolkit.php291
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php27
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php23
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php37
-rw-r--r--core/modules/system/system.admin.inc68
-rw-r--r--core/modules/system/system.api.php38
-rw-r--r--core/modules/system/system.install16
-rw-r--r--core/modules/system/system.module13
-rw-r--r--core/modules/system/tests/modules/image_test/image_test.module132
-rw-r--r--core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/BrokenToolkit.php29
-rw-r--r--core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/TestToolkit.php120
21 files changed, 963 insertions, 821 deletions
diff --git a/core/includes/image.inc b/core/includes/image.inc
index ccc36a4..17a73e8 100644
--- a/core/includes/image.inc
+++ b/core/includes/image.inc
@@ -5,6 +5,8 @@
* API for manipulating images.
*/
+use Drupal\system\Plugin\ImageToolkitInterface;
+
/**
* @defgroup image Image toolkits
* @{
@@ -22,97 +24,29 @@
* from this problem, but it requires the ISP to have installed additional
* software.
*
- * Image toolkits are discovered based on the associated module's
- * hook_image_toolkits. Additionally the image toolkit include file
- * must be identified in the files array in the module.info.yml file. The
- * toolkit must then be enabled using the admin/config/media/image-toolkit
- * form.
+ * Image toolkits are discovered using the Plugin system using
+ * \Drupal\system\Plugin\ImageToolkitManager. The toolkit must then be enabled
+ * using the admin/config/media/image-toolkit form.
*
* Only one toolkit may be selected at a time. If a module author wishes to call
* a specific toolkit they can check that it is installed by calling
- * image_get_available_toolkits(), and then calling its functions directly.
+ * \Drupal\system\Plugin\ImageToolkitManager::getAvailableToolkits(), and then
+ * calling its functions directly.
*/
/**
- * Gets a list of available toolkits.
- *
- * @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');
-
- $output = array();
- foreach ($toolkits as $name => $info) {
- // Only allow modules that aren't marked as unavailable.
- if ($info['available']) {
- $output[$name] = $info['title'];
- }
- }
-
- return $output;
-}
-
-/**
- * Gets the name of the currently used toolkit.
- *
- * @return
- * String containing the name of the selected toolkit, or FALSE on error.
- */
-function image_get_toolkit() {
- static $toolkit;
-
- if (!isset($toolkit)) {
- $toolkits = image_get_available_toolkits();
- $toolkit = variable_get('image_toolkit', 'gd');
- if (!isset($toolkits[$toolkit]) || !function_exists('image_' . $toolkit . '_load')) {
- // 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);
- }
- }
-
- 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);
- }
- watchdog('image', 'The selected image handling toolkit %toolkit can not correctly process %function.', array('%toolkit' => $image->toolkit, '%function' => $function), WATCHDOG_ERROR);
- return FALSE;
-}
-
-/**
* Gets details about an image.
*
* Drupal supports GIF, JPG and PNG file formats when used with the GD
* toolkit, and may support others, depending on which toolkits are
* installed.
*
- * @param $filepath
+ * @param string $filepath
* String specifying the path of the image file.
- * @param $toolkit
- * An optional image toolkit name to override the default.
+ * @param \Drupal\system\Plugin\ImageToolkitInterface $toolkit
+ * (optional) An image toolkit object to override the default.
*
- * @return
+ * @return array
* 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.
@@ -121,20 +55,20 @@ function image_toolkit_invoke($method, stdClass $image, array $params = array())
* - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png').
* - "file_size": File size in bytes.
*/
-function image_get_info($filepath, $toolkit = FALSE) {
+function image_get_info($filepath, ImageToolkitInterface $toolkit = NULL) {
$details = FALSE;
if (!is_file($filepath) && !is_uploaded_file($filepath)) {
return $details;
}
- if (!$toolkit) {
- $toolkit = image_get_toolkit();
+ if ($toolkit === NULL) {
+ $toolkit = Drupal::service('image.toolkit');
}
if ($toolkit) {
$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);
}
@@ -152,21 +86,21 @@ function image_get_info($filepath, $toolkit = FALSE) {
*
* The resulting image always has the exact target dimensions.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $width
+ * @param int $width
* The target width, in pixels.
- * @param $height
+ * @param int $height
* The target height, in pixels.
*
- * @return
+ * @return bool
* TRUE on success, FALSE on failure.
*
* @see image_load()
* @see image_resize()
* @see image_crop()
*/
-function image_scale_and_crop(stdClass $image, $width, $height) {
+function image_scale_and_crop($image, $width, $height) {
$scale = max($width / $image->info['width'], $height / $image->info['height']);
$x = ($image->info['width'] * $scale - $width) / 2;
$y = ($image->info['height'] * $scale - $height) / 2;
@@ -182,20 +116,21 @@ function image_scale_and_crop(stdClass $image, $width, $height) {
*
* The resulting dimensions can be smaller for one or both target dimensions.
*
- * @param $dimensions
+ * @param array $dimensions
* Dimensions to be modified - an array with components width and height, in
* pixels.
- * @param $width
- * The target width, in pixels. If this value is NULL then the scaling will be
- * based only on the height value.
- * @param $height
- * The target height, in pixels. If this value is NULL then the scaling will
- * be based only on the width value.
- * @param $upscale
- * Boolean indicating that images smaller than the target dimensions will be
- * scaled up. This generally results in a low quality image.
- *
- * @return
+ * @param int $width
+ * (optional) The target width, in pixels. If this value is NULL then the
+ * scaling will be based only on the height value.
+ * @param int $height
+ * (optional) The target height, in pixels. If this value is NULL then the
+ * scaling will be based only on the width value.
+ * @param bool $upscale
+ * (optional) Boolean indicating that images smaller than the target
+ * dimensions will be scaled up. This generally results in a low quality
+ * image.
+ *
+ * @return bool
* TRUE if $dimensions was modified, FALSE otherwise.
*
* @see image_scale()
@@ -230,26 +165,26 @@ function image_dimensions_scale(array &$dimensions, $width = NULL, $height = NUL
*
* The resulting image can be smaller for one or both target dimensions.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $width
- * The target width, in pixels. If this value is NULL then the scaling will
- * be based only on the height value.
- * @param $height
- * The target height, in pixels. If this value is NULL then the scaling will
- * be based only on the width value.
- * @param $upscale
- * Boolean indicating that files smaller than the dimensions will be scaled
- * up. This generally results in a low quality image.
- *
- * @return
+ * @param int $width
+ * (optional) The target width, in pixels. This value is omitted then the
+ * scaling will based only on the height value.
+ * @param int $height
+ * (optional) The target height, in pixels. This value is omitted then the
+ * scaling will based only on the width value.
+ * @param bool $upscale
+ * (optional) Boolean indicating that files smaller than the dimensions will
+ * be scaled up. This generally results in a low quality image.
+ *
+ * @return bool
* TRUE on success, FALSE on failure.
*
* @see image_dimensions_scale()
* @see image_load()
* @see image_scale_and_crop()
*/
-function image_scale(stdClass $image, $width = NULL, $height = NULL, $upscale = FALSE) {
+function image_scale($image, $width = NULL, $height = NULL, $upscale = FALSE) {
$dimensions = $image->info;
// Scale the dimensions - if they don't change then just return success.
@@ -263,24 +198,24 @@ function image_scale(stdClass $image, $width = NULL, $height = NULL, $upscale =
/**
* Resizes an image to the given dimensions (ignoring aspect ratio).
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $width
+ * @param int $width
* The target width, in pixels.
- * @param $height
+ * @param int $height
* The target height, in pixels.
*
- * @return
+ * @return bool
* TRUE on success, FALSE on failure.
*
* @see image_load()
- * @see image_gd_resize()
+ * @see \Drupal\system\Plugin\ImageToolkitInterface::resize()
*/
-function image_resize(stdClass $image, $width, $height) {
+function image_resize($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);
}
/**
@@ -288,23 +223,23 @@ function image_resize(stdClass $image, $width, $height) {
*
* @param $image
* An image object returned by image_load().
- * @param $degrees
+ * @param int $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
+ * @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
+ * @return bool
* TRUE on success, FALSE on failure.
*
* @see image_load()
- * @see image_gd_rotate()
+ * @see \Drupal\system\Plugin\ImageToolkitInterface::rotate()
*/
-function image_rotate(stdClass $image, $degrees, $background = NULL) {
- return image_toolkit_invoke('rotate', $image, array($degrees, $background));
+function image_rotate($image, $degrees, $background = NULL) {
+ return $image->toolkit->rotate($image, $degrees, $background);
}
/**
@@ -312,23 +247,23 @@ function image_rotate(stdClass $image, $degrees, $background = NULL) {
*
* @param $image
* An image object returned by image_load().
- * @param $x
+ * @param int $x
* The top left coordinate, in pixels, of the crop area (x axis value).
- * @param $y
+ * @param int $y
* The top left coordinate, in pixels, of the crop area (y axis value).
- * @param $width
+ * @param int $width
* The target width, in pixels.
- * @param $height
+ * @param int $height
* The target height, in pixels.
*
- * @return
+ * @return bool
* TRUE on success, FALSE on failure.
*
* @see image_load()
* @see image_scale_and_crop()
- * @see image_gd_crop()
+ * @see \Drupal\system\Plugin\ImageToolkitInterface::crop()
*/
-function image_crop(stdClass $image, $x, $y, $width, $height) {
+function image_crop($image, $x, $y, $width, $height) {
$aspect = $image->info['height'] / $image->info['width'];
if (empty($height)) $height = $width / $aspect;
if (empty($width)) $width = $height * $aspect;
@@ -336,7 +271,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);
}
/**
@@ -345,14 +280,14 @@ function image_crop(stdClass $image, $x, $y, $width, $height) {
* @param $image
* An image object returned by image_load().
*
- * @return
+ * @return bool
* TRUE on success, FALSE on failure.
*
* @see image_load()
- * @see image_gd_desaturate()
+ * @see \Drupal\system\Plugin\ImageToolkitInterface::desaturate()
*/
-function image_desaturate(stdClass $image) {
- return image_toolkit_invoke('desaturate', $image);
+function image_desaturate($image) {
+ return $image->toolkit->desaturate($image);
}
/**
@@ -360,12 +295,12 @@ function image_desaturate(stdClass $image) {
*
* Any changes to the file are not saved until image_save() is called.
*
- * @param $file
+ * @param string $file
* Path to an image file.
- * @param $toolkit
- * An optional, image toolkit name to override the default.
+ * @param \Drupal\system\Plugin\ImageToolkitInterface $toolkit
+ * (optional) Image toolkit object to override the default.
*
- * @return
+ * @return object
* An image object or FALSE if there was a problem loading the file. The
* image object has the following properties:
* - 'source' - The original file path.
@@ -377,12 +312,10 @@ function image_desaturate(stdClass $image) {
*
* @see image_save()
* @see image_get_info()
- * @see image_get_available_toolkits()
- * @see image_gd_load()
*/
-function image_load($file, $toolkit = FALSE) {
- if (!$toolkit) {
- $toolkit = image_get_toolkit();
+function image_load($file, ImageToolkitInterface $toolkit = NULL) {
+ if ($toolkit === NULL) {
+ $toolkit = Drupal::service('image.toolkit');
}
if ($toolkit) {
$image = new stdClass();
@@ -390,7 +323,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;
}
}
@@ -401,24 +334,24 @@ function image_load($file, $toolkit = FALSE) {
/**
* Closes the image and saves the changes to a file.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load(). The object's 'info' property
* will be updated if the file is saved successfully.
* @param $destination
- * Destination path where the image should be saved. If it is empty the
- * original image file will be overwritten.
+ * (optional) Destination path where the image should be saved. If it is empty
+ * the original image file will be overwritten.
*
- * @return
+ * @return bool
* TRUE on success, FALSE on failure.
*
* @see image_load()
- * @see image_gd_save()
+ * @see \Drupal\system\Plugin\ImageToolkitInterface::save()
*/
-function image_save(stdClass $image, $destination = NULL) {
+function image_save($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/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 09fe145..35b1b6d 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -303,6 +303,16 @@ class CoreBundle extends Bundle {
$container->register('ajax.subscriber', 'Drupal\Core\Ajax\AjaxSubscriber')
->addTag('event_subscriber');
+ // Register image toolkit manager.
+ $container
+ ->register('image.toolkit.manager', 'Drupal\system\Plugin\ImageToolkitManager')
+ ->addArgument('%container.namespaces%');
+ // Register image toolkit.
+ $container
+ ->register('image.toolkit', 'Drupal\system\Plugin\ImageToolkitInterface')
+ ->setFactoryService('image.toolkit.manager')
+ ->setFactoryMethod('getDefaultToolkit');
+
$container->addCompilerPass(new RegisterMatchersPass());
$container->addCompilerPass(new RegisterRouteFiltersPass());
// Add a compiler pass for registering event subscribers.
diff --git a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
index eeaa47b..d8164c1 100644
--- a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
@@ -79,7 +79,7 @@ class ValidatorTest extends FileManagedTestBase {
$this->assertEqual(count($errors), 1, t('Small images report an error.'), 'File');
// Maximum size.
- if (image_get_toolkit()) {
+ if ($this->container->has('image.toolkit')) {
// Copy the image so that the original doesn't get resized.
copy('core/misc/druplicon.png', 'temporary://druplicon.png');
$this->image->uri = 'temporary://druplicon.png';
diff --git a/core/modules/image/image.effects.inc b/core/modules/image/image.effects.inc
index a56a429..619b09b 100644
--- a/core/modules/image/image.effects.inc
+++ b/core/modules/image/image.effects.inc
@@ -64,22 +64,22 @@ function image_image_effect_info() {
/**
* Image effect callback; Resize an image resource.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the resize effect with the
* following items:
* - "width": An integer representing the desired width in pixels.
* - "height": An integer representing the desired height in pixels.
*
- * @return
+ * @return bool
* TRUE on success. FALSE on failure to resize image.
*
* @see image_resize()
*/
-function image_resize_effect($image, $data) {
+function image_resize_effect($image, array $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' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -88,10 +88,10 @@ function image_resize_effect($image, $data) {
/**
* Image dimensions callback; Resize.
*
- * @param $dimensions
+ * @param array $dimensions
* Dimensions to be modified - an array with components width and height, in
* pixels.
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the resize effect with the
* following items:
* - "width": An integer representing the desired width in pixels.
@@ -106,9 +106,9 @@ function image_resize_dimensions(array &$dimensions, array $data) {
/**
* Image effect callback; Scale an image resource.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the scale effect with the
* following items:
* - "width": An integer representing the desired width in pixels.
@@ -116,12 +116,12 @@ function image_resize_dimensions(array &$dimensions, array $data) {
* - "upscale": A boolean indicating that the image should be upscaled if the
* dimensions are larger than the original image.
*
- * @return
+ * @return bool
* TRUE on success. FALSE on failure to scale image.
*
* @see image_scale()
*/
-function image_scale_effect($image, $data) {
+function image_scale_effect($image, array $data) {
// Set sane default values.
$data += array(
'width' => NULL,
@@ -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' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -139,10 +139,10 @@ function image_scale_effect($image, $data) {
/**
* Image dimensions callback; Scale.
*
- * @param $dimensions
+ * @param array $dimensions
* Dimensions to be modified - an array with components width and height, in
* pixels.
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the scale effect with the
* following items:
* - "width": An integer representing the desired width in pixels.
@@ -159,9 +159,9 @@ function image_scale_dimensions(array &$dimensions, array $data) {
/**
* Image effect callback; Crop an image resource.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the crop effect with the
* following items:
* - "width": An integer representing the desired width in pixels.
@@ -170,11 +170,13 @@ function image_scale_dimensions(array &$dimensions, array $data) {
* of "XOFFSET-YOFFSET". XOFFSET is either a number of pixels or
* "left", "center", "right" and YOFFSET is either a number of pixels or
* "top", "center", "bottom".
- * @return
+ *
+ * @return bool
* TRUE on success. FALSE on failure to crop image.
+ *
* @see image_crop()
*/
-function image_crop_effect($image, $data) {
+function image_crop_effect($image, array $data) {
// Set sane default values.
$data += array(
'anchor' => 'center-center',
@@ -184,7 +186,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' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -193,20 +195,22 @@ function image_crop_effect($image, $data) {
/**
* Image effect callback; Scale and crop an image resource.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the scale and crop effect
* with the following items:
* - "width": An integer representing the desired width in pixels.
* - "height": An integer representing the desired height in pixels.
- * @return
+ *
+ * @return bool
* TRUE on success. FALSE on failure to scale and crop image.
+ *
* @see image_scale_and_crop()
*/
-function image_scale_and_crop_effect($image, $data) {
+function image_scale_and_crop_effect($image, array $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' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -215,17 +219,19 @@ function image_scale_and_crop_effect($image, $data) {
/**
* Image effect callback; Desaturate (grayscale) an image resource.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the desaturate effect.
- * @return
+ *
+ * @return bool
* TRUE on success. FALSE on failure to desaturate image.
+ *
* @see image_desaturate()
*/
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' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -234,9 +240,9 @@ function image_desaturate_effect($image, $data) {
/**
* Image effect callback; Rotate an image resource.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the rotate effect containing
* the following items:
* - "degrees": The number of (clockwise) degrees to rotate the image.
@@ -246,8 +252,10 @@ function image_desaturate_effect($image, $data) {
* - "bgcolor": The background color to use for exposed areas of the image.
* Use web-style hex colors (#FFFFFF for white, #000000 for black). Leave
* blank for transparency on image types that support it.
- * @return
+ *
+ * @return bool
* TRUE on success. FALSE on failure to rotate image.
+ *
* @see image_rotate().
*/
function image_rotate_effect($image, $data) {
@@ -278,7 +286,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' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
return FALSE;
}
return TRUE;
@@ -287,10 +295,10 @@ function image_rotate_effect($image, $data) {
/**
* Image dimensions callback; Rotate.
*
- * @param $dimensions
+ * @param array $dimensions
* Dimensions to be modified - an array with components width and height, in
* pixels.
- * @param $data
+ * @param array $data
* An array of attributes to use when performing the rotate effect containing
* the following items:
* - "degrees": The number of (clockwise) degrees to rotate the image.
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
index 825fd38..7f24796 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', 'image_module_test');
+ public static $modules = array('image', 'image_test', 'image_module_test');
public static function getInfo() {
return array(
@@ -44,7 +44,7 @@ class ImageEffectsTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('resize'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['resize'][0][1], 1, 'Width was passed correctly');
$this->assertEqual($calls['resize'][0][2], 2, 'Height was passed correctly');
}
@@ -58,7 +58,7 @@ class ImageEffectsTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('resize'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['resize'][0][1], 10, 'Width was passed correctly');
$this->assertEqual($calls['resize'][0][2], 5, 'Height was based off aspect ratio and passed correctly');
}
@@ -72,7 +72,7 @@ class ImageEffectsTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('crop'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['crop'][0][1], 0, 'X was passed correctly');
$this->assertEqual($calls['crop'][0][2], 1, 'Y was passed correctly');
$this->assertEqual($calls['crop'][0][3], 3, 'Width was passed correctly');
@@ -87,7 +87,7 @@ class ImageEffectsTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('resize', 'crop'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['crop'][0][1], 7.5, 'X was computed and passed correctly');
$this->assertEqual($calls['crop'][0][2], 0, 'Y was computed and passed correctly');
$this->assertEqual($calls['crop'][0][3], 5, 'Width was computed and passed correctly');
@@ -102,7 +102,7 @@ class ImageEffectsTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('desaturate'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual(count($calls['desaturate'][0]), 1, 'Only the image was passed.');
}
@@ -115,7 +115,7 @@ class ImageEffectsTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('rotate'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['rotate'][0][1], 90, 'Degrees were passed correctly');
$this->assertEqual($calls['rotate'][0][2], 0xffffff, 'Background color was passed correctly');
}
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 0d2f228..0000000
--- a/core/modules/system/image.gd.inc
+++ /dev/null
@@ -1,398 +0,0 @@
-<?php
-
-/**
- * @file
- * GD2 toolkit for image manipulation within Drupal.
- */
-
-/**
- * @addtogroup image
- * @{
- */
-
-/**
- * Image toolkit callback: Returns GD-specific image toolkit settings.
- *
- * This function verifies that the GD PHP extension is installed. If it is not,
- * a form error message is set, informing the user about the missing extension.
- *
- * The form elements returned by this function are integrated into the form
- * built by system_image_toolkit_settings().
- *
- * @return
- * An array of Form API elements to be added to the form.
- *
- * @see hook_image_toolkits()
- * @see system_image_toolkit_settings()
- */
-function image_gd_settings() {
- if (image_gd_check_settings()) {
- $form['status'] = array(
- '#markup' => 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 <a href="@url">PHP\'s image documentation</a>.', array('@url' => 'http://php.net/image')));
- return FALSE;
- }
-}
-
-/**
- * Verifies GD2 settings (that the right version is actually installed).
- *
- * @return
- * A Boolean indicating whether the correct version of the GD toolkit is
- * available on this machine.
- *
- * @see image_gd_settings()
- * @see system_image_toolkits()
- */
-function image_gd_check_settings() {
- if ($check = get_extension_funcs('gd')) {
- if (in_array('imagegd2', $check)) {
- // GD2 support is available.
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/**
- * Image toolkit callback: Scales 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 hook_image_toolkits()
- * @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;
-}
-
-/**
- * Image toolkit callback: Rotates an image a specified 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
- * (optional) A 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;
-}
-
-/**
- * Image toolkit callback: Crops 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 hook_image_toolkits()
- * @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;
-}
-
-/**
- * Image toolkit callback: Converts an image to grayscale using the GD toolkit.
- *
- * 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 hook_image_toolkits()
- * @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);
-}
-
-/**
- * Image toolkit callback: Creates a GD 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 hook_image_toolkits()
- * @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;
-}
-
-/**
- * Image toolkit callback: Writes 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 hook_image_toolkits()
- * @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 (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;
-}
-
-/**
- * Creates 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.
- * Image toolkit callback: Retrieves 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 hook_image_toolkits()
- * @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..0b22f3c
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Plugin\ImageToolkitInterface.
+ */
+
+namespace Drupal\system\Plugin;
+
+/**
+ * Defines an interface for image toolkits.
+ *
+ * An image toolkit provides common image file manipulations like scaling,
+ * cropping, and rotating.
+ */
+interface ImageToolkitInterface {
+
+ /**
+ * Retrieves toolkit's settings form.
+ *
+ * @see system_image_toolkit_settings()
+ */
+ function settingsForm();
+
+ /**
+ * Handles submissions for toolkit's settings form.
+ *
+ * @see system_image_toolkit_settings_submit()
+ */
+ function settingsFormSubmit($form, &$form_state);
+
+ /**
+ * Scales an image to the specified size.
+ *
+ * @param object $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.
+ *
+ * @see image_resize()
+ */
+ function resize($image, $width, $height);
+
+ /**
+ * Rotates an image the given number of degrees.
+ *
+ * @param object $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.
+ *
+ * @see image_rotate()
+ */
+ function rotate($image, $degrees, $background = NULL);
+
+ /**
+ * Crops an image.
+ *
+ * @param object $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()
+ */
+ function crop($image, $x, $y, $width, $height);
+
+ /**
+ * Converts an image resource to grayscale.
+ *
+ * Note that transparent GIFs loose transparency when desaturated.
+ *
+ * @param object $image
+ * An image object. The $image->resource value will be modified by this
+ * call.
+ *
+ * @return bool
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_desaturate()
+ */
+ function desaturate($image);
+
+ /**
+ * Creates an image resource from a file.
+ *
+ * @param object $image
+ * An image object. The $image->resource value will populated by this call.
+ *
+ * @return bool
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_load()
+ */
+ function load($image);
+
+ /**
+ * Writes an image resource to a destination file.
+ *
+ * @param object $image
+ * An image object.
+ * @param string $destination
+ * A string file URI or path where the image should be saved.
+ *
+ * @return bool
+ * TRUE or FALSE, based on success.
+ *
+ * @see image_save()
+ */
+ function save($image, $destination);
+
+ /**
+ * Gets details about an image.
+ *
+ * @param object $image
+ * An image object.
+ *
+ * @return array
+ * 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($image);
+
+ /**
+ * Verifies Image Toolkit is set up correctly.
+ *
+ * @return bool
+ * True 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..158b6d4
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Plugin\ImageToolkitManager.
+ */
+
+namespace Drupal\system\Plugin;
+
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+
+/**
+ * Manages toolkit plugins.
+ */
+class ImageToolkitManager extends PluginManagerBase {
+
+ /**
+ * Constructs the ImageToolkitManager object.
+ */
+ public function __construct(array $namespaces) {
+ $this->discovery = new AnnotatedClassDiscovery('system', 'imagetoolkit', $namespaces);
+ $this->factory = new DefaultFactory($this->discovery);
+ }
+
+ /**
+ * Gets the default image toolkit.
+ *
+ * @param string $toolkit_id
+ * (optional) String specifying toolkit to load. NULL will load the default
+ * toolkit.
+ *
+ * @return \Drupal\system\Plugin\ImageToolkitInterface
+ * Object of the default toolkit, or FALSE on error.
+ */
+ public function getDefaultToolkit() {
+ $toolkit_id = config('system.image')->get('toolkit');
+ $toolkits = $this->getAvailableToolkits();
+
+ 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_id = key($toolkits);
+ }
+
+ if ($toolkit_id) {
+ $toolkit = $this->createInstance($toolkit_id);
+ }
+ else {
+ $toolkit = FALSE;
+ }
+
+ return $toolkit;
+ }
+
+ /**
+ * Gets a list of available toolkits.
+ *
+ * @return array
+ * An array with the toolkit names as keys and the descriptions as values.
+ */
+ public function getAvailableToolkits() {
+ // Use plugin system to get list of available toolkits.
+ $toolkits = $this->getDefinitions();
+
+ $output = array();
+ foreach ($toolkits as $id => $definition) {
+ // Only allow modules that aren't marked as unavailable.
+ if (call_user_func($definition['class'] . '::isAvailable')) {
+ $output[$id] = $definition;
+ }
+ }
+
+ return $output;
+ }
+}
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..1c513cb
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/system/imagetoolkit/GDToolkit.php
@@ -0,0 +1,291 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Plugin\system\imagetoolkit\GDToolkit;.
+ */
+
+namespace Drupal\system\Plugin\system\imagetoolkit;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\system\Plugin\ImageToolkitInterface;
+
+/**
+ * Defines the GD2 toolkit for image manipulation within Drupal.
+ *
+ * @Plugin(
+ * id = "gd",
+ * title = @Translation("GD2 image manipulation toolkit")
+ * )
+ */
+class GDToolkit extends PluginBase implements ImageToolkitInterface {
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::settingsForm().
+ */
+ public function settingsForm() {
+ $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().
+ */
+ public 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().
+ */
+ public function resize($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().
+ */
+ public function rotate($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().
+ */
+ public function crop($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().
+ */
+ public function desaturate($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().
+ */
+ public function load($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().
+ */
+ public function save($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().
+ */
+ public function getInfo($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;
+ }
+
+ /**
+ * Creates a truecolor image preserving transparency from a provided image.
+ *
+ * @param object $image
+ * An image object.
+ * @param int $width
+ * The new width of the new image, in pixels.
+ * @param int $height
+ * The new height of the new image, in pixels.
+ *
+ * @return resource
+ * A GD image handle.
+ */
+ public function createTmp($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 22f6c13..1dac11c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
@@ -7,12 +7,13 @@
namespace Drupal\system\Tests\Image;
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\DrupalUnitTestBase;
+use Drupal\system\Plugin\ImageToolkitManager;
/**
* Test the core GD image manipulation functions.
*/
-class ToolkitGdTest extends WebTestBase {
+class ToolkitGdTest extends DrupalUnitTestBase {
// Colors that are used in testing.
protected $black = array(0, 0, 0, 0);
protected $red = array(255, 0, 0, 0);
@@ -26,6 +27,13 @@ class ToolkitGdTest extends WebTestBase {
protected $width = 40;
protected $height = 20;
+ /**
+ * Modules to enable.
+ *
+ * @var array
+ */
+ public static $modules = array('system', 'simpletest');
+
public static function getInfo() {
return array(
'name' => 'Image GD manipulation tests',
@@ -35,8 +43,14 @@ class ToolkitGdTest extends WebTestBase {
}
protected function checkRequirements() {
- image_get_available_toolkits();
- if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) {
+ $gd_available = FALSE;
+ if ($check = get_extension_funcs('gd')) {
+ if (in_array('imagegd2', $check)) {
+ // GD2 support is available.
+ $gd_available = TRUE;
+ }
+ }
+ if (!$gd_available) {
return array(
'Image manipulations for the GD toolkit cannot run because the GD toolkit is not available.',
);
@@ -201,10 +215,11 @@ class ToolkitGdTest extends WebTestBase {
);
}
+ $manager = new ImageToolkitManager($this->container->getParameter('container.namespaces'));
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, $manager->createInstance('gd'));
if (!$image) {
$this->fail(t('Could not load image %file.', array('%file' => $file)));
continue 2;
@@ -244,7 +259,7 @@ class ToolkitGdTest extends WebTestBase {
$correct_dimensions_object = FALSE;
}
- $directory = file_default_scheme() . '://imagetests';
+ $directory = $this->public_files_directory .'/imagetest';
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
image_save($image, $directory . '/' . $op . '.' . $image->info['extension']);
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 3e5aadd..7077023 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
@@ -7,6 +7,8 @@
namespace Drupal\system\Tests\Image;
+use Drupal\system\Plugin\ImageToolkitManager;
+
/**
* Test that the functions in image.inc correctly pass data to the toolkit.
*/
@@ -20,11 +22,12 @@ class ToolkitTest extends ToolkitTestBase {
}
/**
- * Check that hook_image_toolkits() is called and only available toolkits are
- * returned.
+ * Check that ImageToolkitManager::getAvailableToolkits() only returns
+ * available toolkits.
*/
function testGetAvailableToolkits() {
- $toolkits = image_get_available_toolkits();
+ $manager = new ImageToolkitManager($this->container->getParameter('container.namespaces'));
+ $toolkits = $manager->getAvailableToolkits();
$this->assertTrue(isset($toolkits['test']), 'The working toolkit was returned.');
$this->assertFalse(isset($toolkits['broken']), 'The toolkit marked unavailable was not returned');
$this->assertToolkitOperationsCalled(array());
@@ -36,7 +39,7 @@ class ToolkitTest extends ToolkitTestBase {
function testLoad() {
$image = image_load($this->file, $this->toolkit);
$this->assertTrue(is_object($image), 'Returned an object.');
- $this->assertEqual($this->toolkit, $image->toolkit, 'Image had toolkit set.');
+ $this->assertEqual($this->toolkit, $image->toolkit, t('Image had toolkit set.'));
$this->assertToolkitOperationsCalled(array('load', 'get_info'));
}
@@ -56,7 +59,7 @@ class ToolkitTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('resize'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['resize'][0][1], 1, 'Width was passed correctly');
$this->assertEqual($calls['resize'][0][2], 2, 'Height was passed correctly');
}
@@ -70,7 +73,7 @@ class ToolkitTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('resize'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['resize'][0][1], 10, 'Width was passed correctly');
$this->assertEqual($calls['resize'][0][2], 5, 'Height was based off aspect ratio and passed correctly');
}
@@ -83,7 +86,7 @@ class ToolkitTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('resize', 'crop'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['crop'][0][1], 7.5, 'X was computed and passed correctly');
$this->assertEqual($calls['crop'][0][2], 0, 'Y was computed and passed correctly');
@@ -99,7 +102,7 @@ class ToolkitTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('rotate'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['rotate'][0][1], 90, 'Degrees were passed correctly');
$this->assertEqual($calls['rotate'][0][2], 1, 'Background color was passed correctly');
}
@@ -112,7 +115,7 @@ class ToolkitTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('crop'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['crop'][0][1], 1, 'X was passed correctly');
$this->assertEqual($calls['crop'][0][2], 2, 'Y was passed correctly');
$this->assertEqual($calls['crop'][0][3], 3, 'Width was passed correctly');
@@ -127,7 +130,7 @@ class ToolkitTest extends ToolkitTestBase {
$this->assertToolkitOperationsCalled(array('desaturate'));
// Check the parameters.
- $calls = image_test_get_all_calls();
+ $calls = $this->imageTestGetAllCalls();
$this->assertEqual(count($calls['desaturate'][0]), 1, 'Only the image was passed.');
}
}
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 51409d5..d365f97 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
@@ -8,6 +8,7 @@
namespace Drupal\system\Tests\Image;
use Drupal\simpletest\WebTestBase;
+use Drupal\system\Plugin\ImageToolkitManager;
use stdClass;
/**
@@ -30,7 +31,8 @@ abstract class ToolkitTestBase extends WebTestBase {
parent::setUp();
// Use the image_test.module's test toolkit.
- $this->toolkit = 'test';
+ $manager = new ImageToolkitManager($this->container->getParameter('container.namespaces'));
+ $this->toolkit = $manager->createInstance('test');
// Pick a file for testing.
$file = current($this->drupalGetTestFiles('image'));
@@ -44,7 +46,7 @@ abstract class ToolkitTestBase extends WebTestBase {
$this->image->toolkit = $this->toolkit;
// Clear out any hook calls.
- image_test_reset();
+ $this->imageTestReset();
}
/**
@@ -57,7 +59,7 @@ abstract class ToolkitTestBase extends WebTestBase {
*/
function assertToolkitOperationsCalled(array $expected) {
// Determine which operations were called.
- $actual = array_keys(array_filter(image_test_get_all_calls()));
+ $actual = array_keys(array_filter($this->imageTestGetAllCalls()));
// Determine if there were any expected that were not called.
$uncalled = array_diff($expected, $actual);
@@ -77,4 +79,33 @@ abstract class ToolkitTestBase extends WebTestBase {
$this->assertTrue(TRUE, 'No unexpected operations were called.');
}
}
+
+ /**
+ * Resets/initializes the history of calls to the test toolkit functions.
+ */
+ function imageTestReset() {
+ // Keep track of calls to these operations
+ $results = array(
+ 'load' => array(),
+ 'save' => array(),
+ 'settings' => array(),
+ 'resize' => array(),
+ 'rotate' => array(),
+ 'crop' => array(),
+ 'desaturate' => array(),
+ );
+ state()->set('image_test.results', $results);
+ }
+
+ /**
+ * Gets an array of calls to the test toolkit.
+ *
+ * @return array
+ * An array keyed by operation name ('load', 'save', 'settings', 'resize',
+ * 'rotate', 'crop', 'desaturate') with values being arrays of parameters
+ * passed to each call.
+ */
+ function imageTestGetAllCalls() {
+ return state()->get('image_test.results') ?: array();
+ }
}
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 58e6d58..dbdf6a5 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1830,39 +1830,65 @@ function system_file_system_settings_submit($form, &$form_state) {
* Form builder; Configure site image toolkit usage.
*
* @ingroup forms
- * @see system_settings_form()
+ * @see system_image_toolkit_settings_submit()
*/
-function system_image_toolkit_settings() {
- $toolkits_available = image_get_available_toolkits();
- $current_toolkit = image_get_toolkit();
+function system_image_toolkit_settings($form, &$form_state) {
+ $manager = Drupal::service('image.toolkit.manager');
+ $toolkits_available = $manager->getAvailableToolkits();
+ $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 <a href='!gd-link'>PHP's built-in image processing functions</a> 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 available toolkits allow the user to select the image toolkit to
+ // use and load the 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.
+ 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 <a href="@url">PHP\'s image documentation</a>.', 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 = Drupal::service('image.toolkit.manager');
+ foreach ($manager->getAvailableToolkits() as $id => $definition) {
+ $toolkit = $manager->createInstance($id);
+ $toolkit->settingsFormSubmit($form, $form_state);
+ }
}
/**
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 935bc07..8ddbf09 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1360,44 +1360,6 @@ function hook_init() {
}
/**
- * Define image toolkits provided by this module.
- *
- * The file which includes each toolkit's functions must be declared as part of
- * the files array in the module .info.yml file so that the registry will find
- * and parse it.
- *
- * The toolkit's functions must be named image_toolkitname_operation().
- * where the operation may be:
- * - 'load': Required. See image_gd_load() for usage.
- * - 'save': Required. See image_gd_save() for usage.
- * - 'settings': Optional. See image_gd_settings() for usage.
- * - 'resize': Optional. See image_gd_resize() for usage.
- * - 'rotate': Optional. See image_gd_rotate() for usage.
- * - 'crop': Optional. See image_gd_crop() for usage.
- * - 'desaturate': Optional. See image_gd_desaturate() for usage.
- *
- * @return
- * An array with the toolkit name as keys and sub-arrays with these keys:
- * - 'title': A string with the toolkit's title.
- * - 'available': A Boolean value to indicate that the toolkit is operating
- * properly, e.g. all required libraries exist.
- *
- * @see system_image_toolkits()
- */
-function hook_image_toolkits() {
- return array(
- 'working' => array(
- 'title' => t('A toolkit that works.'),
- 'available' => TRUE,
- ),
- 'broken' => array(
- 'title' => t('A toolkit that is "broken" and will not be listed.'),
- 'available' => FALSE,
- ),
- );
-}
-
-/**
* Alter an email message created with the drupal_mail() function.
*
* hook_mail_alter() allows modification of email messages created and sent
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index e066efb..eeb8616 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -2052,7 +2052,7 @@ function system_update_8048() {
update_module_enable(array('menu_link'));
// Add the langcode column if it doesn't exist.
- if (!db_field_exists('menu_links', 'langcode')) {
+ if (!db_field_exists('menu_inks', 'langcode')) {
$column = array(
'description' => 'The {language}.langcode of this entity.',
'type' => 'varchar',
@@ -2158,6 +2158,20 @@ function system_update_8051() {
}
/**
+ * Moves image toolkit settings from variable to config.
+ *
+ * @ingroup config_upgrade
+ */
+function system_update_8052() {
+ 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 4fc06f7..149e2ee 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -3650,19 +3650,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 fb6b720..92f1ca2 100644
--- a/core/modules/system/tests/modules/image_test/image_test.module
+++ b/core/modules/system/tests/modules/image_test/image_test.module
@@ -4,135 +4,3 @@
* @file
* Helper module for the image tests.
*/
-
-/**
- * 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()
- */
-function image_test_reset() {
- // Keep track of calls to these operations
- $results = array(
- 'load' => array(),
- 'save' => array(),
- 'settings' => array(),
- 'resize' => array(),
- 'rotate' => array(),
- 'crop' => array(),
- 'desaturate' => array(),
- );
- state()->set('image_test.results', $results);
-}
-
-/**
- * Get an array with the all the calls to the toolkits since image_test_reset()
- * was called.
- *
- * @return
- * An array keyed by operation name ('load', 'save', 'settings', 'resize',
- * 'rotate', 'crop', 'desaturate') with values being arrays of parameters
- * passed to each call.
- */
-function image_test_get_all_calls() {
- return state()->get('image_test.results') ?: array();
-}
-
-/**
- * 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 _image_test_log_call($op, $args) {
- $results = state()->get('image_test.results') ?: array();
- $results[$op][] = $args;
- state()->set('image_test.results', $results);
-}
-
-/**
- * Image tookit's settings operation.
- */
-function image_test_settings() {
- _image_test_log_call('settings', array());
- return array();
-}
-
-/**
- * Image toolkit's get_info operation.
- */
-function image_test_get_info(stdClass $image) {
- _image_test_log_call('get_info', array($image));
- return array();
-}
-
-/**
- * Image tookit's load operation.
- */
-function image_test_load(stdClass $image) {
- _image_test_log_call('load', array($image));
- return $image;
-}
-
-/**
- * Image tookit's save operation.
- */
-function image_test_save(stdClass $image, $destination) {
- _image_test_log_call('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;
-}
-
-/**
- * Image tookit's crop operation.
- */
-function image_test_crop(stdClass $image, $x, $y, $width, $height) {
- _image_test_log_call('crop', array($image, $x, $y, $width, $height));
- return TRUE;
-}
-
-/**
- * Image tookit's resize operation.
- */
-function image_test_resize(stdClass $image, $width, $height) {
- _image_test_log_call('resize', array($image, $width, $height));
- return TRUE;
-}
-
-/**
- * Image tookit's rotate operation.
- */
-function image_test_rotate(stdClass $image, $degrees, $background = NULL) {
- _image_test_log_call('rotate', array($image, $degrees, $background));
- return TRUE;
-}
-
-/**
- * Image tookit's desaturate operation.
- */
-function image_test_desaturate(stdClass $image) {
- _image_test_log_call('desaturate', array($image));
- return TRUE;
-}
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..63d3461
--- /dev/null
+++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/BrokenToolkit.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\image_test\Plugin\system\imagetoolkit\BrokenToolkit.
+ */
+
+namespace Drupal\image_test\Plugin\system\imagetoolkit;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines a Test toolkit for image manipulation within Drupal.
+ *
+ * @Plugin(
+ * id = "broken",
+ * title = @Translation("A dummy toolkit that is broken")
+ * )
+ */
+class BrokenToolkit extends TestToolkit {
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::isAvailable().
+ */
+ public static function isAvailable() {
+ return FALSE;
+ }
+}
diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/TestToolkit.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/TestToolkit.php
new file mode 100644
index 0000000..b2634ac
--- /dev/null
+++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/system/imagetoolkit/TestToolkit.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\image_test\Plugin\system\imagetoolkit\TestToolkit.
+ */
+
+namespace Drupal\image_test\Plugin\system\imagetoolkit;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\system\Plugin\ImageToolkitInterface;
+
+/**
+ * Defines a Test toolkit for image manipulation within Drupal.
+ *
+ * @Plugin(
+ * id = "test",
+ * title = @Translation("A dummy toolkit that works")
+ * )
+ */
+class TestToolkit extends PluginBase implements ImageToolkitInterface {
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::settingsForm().
+ */
+ public function settingsForm() {
+ $this->logCall('settings', array());
+ return array();
+ }
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::settingsFormSubmit().
+ */
+ public function settingsFormSubmit($form, &$form_state) {}
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::getInfo().
+ */
+ public function getInfo($image) {
+ $this->logCall('get_info', array($image));
+ return array();
+ }
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::load().
+ */
+ public function load($image) {
+ $this->logCall('load', array($image));
+ return $image;
+ }
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::save().
+ */
+ public function save($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().
+ */
+ public function crop($image, $x, $y, $width, $height) {
+ $this->logCall('crop', array($image, $x, $y, $width, $height));
+ return TRUE;
+ }
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::resize().
+ */
+ public function resize($image, $width, $height) {
+ $this->logCall('resize', array($image, $width, $height));
+ return TRUE;
+ }
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::rotate().
+ */
+ public function rotate($image, $degrees, $background = NULL) {
+ $this->logCall('rotate', array($image, $degrees, $background));
+ return TRUE;
+ }
+
+ /**
+ * Implements \Drupal\system\Plugin\ImageToolkitInterface::desaturate().
+ */
+ public function desaturate($image) {
+ $this->logCall('desaturate', array($image));
+ return TRUE;
+ }
+
+ /**
+ * Stores the values passed to a toolkit call.
+ *
+ * @param string $op
+ * One of the image toolkit operations: 'get_info', 'load', 'save',
+ * 'settings', 'resize', 'rotate', 'crop', 'desaturate'.
+ * @param array $args
+ * Values passed to hook.
+ *
+ * @see \Drupal\system\Tests\Image\ToolkitTestBase::imageTestReset()
+ * @see \Drupal\system\Tests\Image\ToolkitTestBase::imageTestGetAllCalls()
+ */
+ protected function logCall($op, $args) {
+ $results = state()->get('image_test.results') ?: array();
+ $results[$op][] = $args;
+ state()->set('image_test.results', $results);
+ }
+
+ /**
+ * Implements Drupal\system\Plugin\ImageToolkitInterface::isAvailable().
+ */
+ public static function isAvailable() {
+ return TRUE;
+ }
+}