From e22e10089f24586cda031020dd710c7155c9d5dc Mon Sep 17 00:00:00 2001
From: Claudiu Cristea <clau.cristea@gmail.com>
Date: Wed, 26 Jun 2013 17:10:33 +0300
Subject: [PATCH] Fixed #204497 by claudiu.cristea, Murz, tylor, edmund.kwok,
 RobLoach, sun, dman | FiReaNG3L: Added Configurable
 background color when cropping to larger dimensions.

---
 core/includes/image.inc                            |   30 ++--
 core/modules/image/image.admin.inc                 |   83 ++++++++---
 core/modules/image/image.effects.inc               |   32 ++---
 .../Drupal/image/Tests/ImageAdminStylesTest.php    |    7 +-
 .../Drupal/image/Tests/ImageCropBackgroundTest.php |  152 ++++++++++++++++++++
 .../lib/Drupal/image/Tests/ImageEffectsTest.php    |    5 +-
 .../system/Plugin/ImageToolkit/GDToolkit.php       |   30 ++--
 .../Drupal/system/Plugin/ImageToolkitInterface.php |   27 ++--
 .../Drupal/system/Tests/Image/ToolkitGdTest.php    |    4 +-
 .../image_test/Plugin/ImageToolkit/TestToolkit.php |   12 +-
 10 files changed, 306 insertions(+), 76 deletions(-)
 create mode 100644 core/modules/image/lib/Drupal/image/Tests/ImageCropBackgroundTest.php

diff --git a/core/includes/image.inc b/core/includes/image.inc
index 9e2d06f..b90a236 100644
--- a/core/includes/image.inc
+++ b/core/includes/image.inc
@@ -189,12 +189,14 @@ function image_resize($image, $width, $height) {
  *   An image object returned by image_load().
  * @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.
+ * @param string $background_color
+ *   (optional) An hexadecimal string specifying the background color to use for
+ *   uncovered area of the image after the rotation. Examples: "#RGB",
+ *   "#RRGGBB". For images that support transparency, this will default to
+ *   transparent. Otherwise it will be white.
+ * @param string $background_alpha
+ *   (optional) The value of alpha transparency for $background_color. Defaults
+ *   to 0.
  *
  * @return bool
  *   TRUE on success, FALSE on failure.
@@ -202,8 +204,8 @@ function image_resize($image, $width, $height) {
  * @see image_load()
  * @see \Drupal\system\Plugin\ImageToolkitInterface::rotate()
  */
-function image_rotate($image, $degrees, $background = NULL) {
-  return $image->toolkit->rotate($image, $degrees, $background);
+function image_rotate($image, $degrees, $background_color = NULL, $background_alpha = 0) {
+  return $image->toolkit->rotate($image, $degrees, $background_color, $background_alpha);
 }
 
 /**
@@ -219,6 +221,14 @@ function image_rotate($image, $degrees, $background = NULL) {
  *   The target width, in pixels.
  * @param int $height
  *   The target height, in pixels.
+ * @param string $background_color
+ *   (optional) An hexadecimal string specifying the background color to use for
+ *   the additional area created when cropping to larger dimensions than the
+ *   source. Examples: "#RGB", "#RRGGBB". For images that support transparency,
+ *   this will default to transparent. Otherwise will be white.
+ * @param string $background_alpha
+ *   (optional) The value of alpha transparency for $background_color. Defaults
+ *   to 0.
  *
  * @return bool
  *   TRUE on success, FALSE on failure.
@@ -227,7 +237,7 @@ function image_rotate($image, $degrees, $background = NULL) {
  * @see image_scale_and_crop()
  * @see \Drupal\system\Plugin\ImageToolkitInterface::crop()
  */
-function image_crop($image, $x, $y, $width, $height) {
+function image_crop($image, $x, $y, $width, $height, $background_color = NULL, $background_alpha = 0) {
   $aspect = $image->info['height'] / $image->info['width'];
   if (empty($height)) $height = $width / $aspect;
   if (empty($width)) $width = $height * $aspect;
@@ -235,7 +245,7 @@ function image_crop($image, $x, $y, $width, $height) {
   $width = (int) round($width);
   $height = (int) round($height);
 
-  return $image->toolkit->crop($image, $x, $y, $width, $height);
+  return $image->toolkit->crop($image, $x, $y, $width, $height, $background_color, $background_alpha);
 }
 
 /**
diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index 02af74e..869f450 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -349,18 +349,6 @@ function image_effect_form_submit($form, &$form_state) {
 }
 
 /**
- * Element validate handler to ensure a hexadecimal color value.
- */
-function image_effect_color_validate($element, &$form_state) {
-  if ($element['#value'] != '') {
-    $hex_value = preg_replace('/^#/', '', $element['#value']);
-    if (!preg_match('/^#[0-9A-F]{3}([0-9A-F]{3})?$/', $element['#value'])) {
-      form_error($element, t('!name must be a hexadecimal color value.', array('!name' => $element['#title'])));
-    }
-  }
-}
-
-/**
  * Element validate handler to ensure that either a height or a width is
  * specified.
  */
@@ -461,6 +449,10 @@ function image_crop_form($data) {
     '#description' => t('The part of the image that will be retained during the crop.'),
   );
 
+  // Add elements for possible additional area added when cropping to larger
+  // dimensions.
+  image_effect_color('image_crop', $form, $data);
+
   return $form;
 }
 
@@ -483,15 +475,10 @@ function image_rotate_form($data) {
     '#field_suffix' => '&deg;',
     '#required' => TRUE,
   );
-  $form['bgcolor'] = array(
-    '#type' => 'textfield',
-    '#default_value' => (isset($data['bgcolor'])) ? $data['bgcolor'] : '#FFFFFF',
-    '#title' => t('Background color'),
-    '#description' => t('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.'),
-    '#size' => 7,
-    '#maxlength' => 7,
-    '#element_validate' => array('image_effect_color_validate'),
-  );
+
+  // Add elements for exposed areas added when rotating.
+  image_effect_color('image_rotate', $form, $data);
+
   $form['random'] = array(
     '#type' => 'checkbox',
     '#default_value' => (isset($data['random'])) ? $data['random'] : 0,
@@ -502,6 +489,60 @@ function image_rotate_form($data) {
 }
 
 /**
+ * Add background color element to effect form.
+ *
+ * Some effects, like 'rotate' and 'crop' may add new areas to the image. Admins
+ * must be able to define a color that will be used to fill the additional
+ * created areas.
+ *
+ * @param string $effect_id
+ *   The effect id (Example: 'image_crop', 'image_rotate').
+ * @param array $form
+ *   The form array passed by reference.
+ * @param array $data
+ *   The current configuration for this effect.
+ *
+ * @see image_crop_form()
+ * @see image_rotate_form()
+ */
+function image_effect_color($effect_id, &$form, $data) {
+  // Load effect definition.
+  $effect = image_effect_definition_load($effect_id);
+
+  $states = array(
+    '#states' => array(
+      'visible' => array(
+        ':input[name="data[background_has_color]"]' => array('checked' => TRUE),
+      ),
+    ),
+  );
+
+  $form['background_has_color'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Add color to additional created areas'),
+    '#description' => t('Applying the %effect effect may reveal additional areas. By default, those areas are transparent, if the image type allows, or black if the image has no transparency capability. Check to define a specific color to be filled into new areas.', array('%effect' => $effect['label'])),
+    '#default_value' => empty($data['background_has_color']) ? FALSE : $data['background_has_color'],
+  );
+  $form['background_color'] = array(
+    '#type' => 'color',
+    '#title' => t('Color'),
+    '#default_value' => empty($data['background_color']) ? '' : $data['background_color'],
+    '#size' => 12,
+    '#maxlength' => 7,
+    '#description' => t('Pickup or enter a hex string specifying the color. Examples: "#RGB", "#RRGGBB".'),
+  ) + $states;
+  $form['background_alpha'] = array(
+    '#type' => 'number',
+    '#title' => t('Alpha transparency'),
+    '#default_value' => empty($data['background_alpha']) ? 0 : $data['background_alpha'],
+    '#description' => t('From 0 (opaque) to 100% (transparent).'),
+    '#field_suffix' => '%',
+    '#min' => 0,
+    '#max' => 100,
+  ) + $states;
+}
+
+/**
  * Returns HTML for the page containing the list of image styles.
  *
  * @param $variables
diff --git a/core/modules/image/image.effects.inc b/core/modules/image/image.effects.inc
index 619b09b..cecc7ab 100644
--- a/core/modules/image/image.effects.inc
+++ b/core/modules/image/image.effects.inc
@@ -180,12 +180,20 @@ function image_crop_effect($image, array $data) {
   // Set sane default values.
   $data += array(
     'anchor' => 'center-center',
+    'background_has_color' => FALSE,
+    'background_color' => NULL,
+    'background_alpha' => 0,
   );
 
   list($x, $y) = explode('-', $data['anchor']);
   $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'])) {
+
+  $has_background_color = (bool) $data['background_has_color']; 
+  $background_color = $has_background_color ? $data['background_color'] : NULL;
+  $background_alpha = $has_background_color ? $data['background_alpha'] : 0;
+
+  if (!image_crop($image, $x, $y, $data['width'], $data['height'], $background_color, $background_alpha)) {
     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;
   }
@@ -262,30 +270,22 @@ function image_rotate_effect($image, $data) {
   // Set sane default values.
   $data += array(
     'degrees' => 0,
-    'bgcolor' => NULL,
+    'background_has_color' => FALSE,
+    'background_color' => NULL,
+    'background_alpha' => 0,
     'random' => FALSE,
   );
 
-  // Convert short #FFF syntax to full #FFFFFF syntax.
-  if (strlen($data['bgcolor']) == 4) {
-    $c = $data['bgcolor'];
-    $data['bgcolor'] = $c[0] . $c[1] . $c[1] . $c[2] . $c[2] . $c[3] . $c[3];
-  }
-
-  // Convert #FFFFFF syntax to hexadecimal colors.
-  if ($data['bgcolor'] != '') {
-    $data['bgcolor'] = hexdec(str_replace('#', '0x', $data['bgcolor']));
-  }
-  else {
-    $data['bgcolor'] = NULL;
-  }
+  $has_background_color = (bool) $data['background_has_color']; 
+  $background_color = $has_background_color ? $data['background_color'] : NULL;
+  $background_alpha = $has_background_color ? $data['background_alpha'] : 0;
 
   if (!empty($data['random'])) {
     $degrees = abs((float) $data['degrees']);
     $data['degrees'] = rand(-1 * $degrees, $degrees);
   }
 
-  if (!image_rotate($image, $data['degrees'], $data['bgcolor'])) {
+  if (!image_rotate($image, $data['degrees'], $background_color, $background_alpha)) {
     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;
   }
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php
index 973a8ef..e2decaa 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php
@@ -89,6 +89,9 @@ function testStyle() {
         'data[width]' => 130,
         'data[height]' => 131,
         'data[anchor]' => 'center-center',
+        'data[background_has_color]' => TRUE,
+        'data[background_color]' => '#ffff00',
+        'data[background_alpha]' => 0,
       ),
       'image_desaturate' => array(
         // No options for desaturate.
@@ -96,7 +99,9 @@ function testStyle() {
       'image_rotate' => array(
         'data[degrees]' => 5,
         'data[random]' => 1,
-        'data[bgcolor]' => '#FFFF00',
+        'data[background_has_color]' => TRUE,
+        'data[background_color]' => '#ffff00',
+        'data[background_alpha]' => 0,
       ),
     );
 
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageCropBackgroundTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageCropBackgroundTest.php
new file mode 100644
index 0000000..6dc0962
--- /dev/null
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageCropBackgroundTest.php
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\image\Tests\ImageCropBackgroundTest.
+ */
+
+namespace Drupal\image\Tests;
+
+use Drupal\image\Plugin\Core\Entity\ImageStyle;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests that image cropping uses the correct background color.
+ */
+class ImageCropBackgroundTest extends WebTestBase {
+
+  protected $image;
+  protected $style1;
+  protected $style2;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('image');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Image crop background color',
+      'description' => 'Check if after cropping to larger dimensions, the uncovered area has the correct color.',
+      'group' => 'Image',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    // Source image width and height.
+    $width = 50;
+    $height = 25;
+
+    // Create a new non-squared image filled with pure blue color.
+    $resource = imagecreatetruecolor($width, $height);
+    imagefill($resource, 0, 0, imagecolorallocate($resource, 0, 0, 255));
+
+    // Create an image destination.
+    $extension_png = image_type_to_extension(IMAGETYPE_PNG);
+    $file = 'public://' . $this->randomName() . $extension_png;
+
+    // Save test image to disk.
+    $this->image = (object) array(
+      'source' => $file,
+      'resource' => $resource,
+      'info' => array(
+        'width' => $width,
+        'height' => $height,
+        'extension' => ltrim($extension_png, '.'),
+        'mime_type' => image_type_to_mime_type(IMAGETYPE_PNG),
+      ),
+      'toolkit' => \Drupal::service('image.toolkit'), // \Drupal::config('system.image')->get('toolkit'),
+    );
+    image_save($this->image, $file);
+
+    // Create two dummy styles.
+    $this->style1 = entity_create('image_style', array('name' => 'color', 'label' => 'Color'));
+    $this->style1->save();
+    $this->style2 = entity_create('image_style', array('name' => 'no_color', 'label' => 'No color'));
+    $this->style2->save();
+
+    // Attach a single "image_crop" effect to the first style that enlarges the
+    // source image to add additional area filled with red (#FF0000).
+    $effect = array(
+      'weight' => 0,
+      'name' => 'image_crop',
+      'data' => array(
+        'width' => 50,
+        'height' => 50,
+        'anchor' => 'center-center',
+        'background_has_color' => TRUE,
+        'background_color' => '#ff0000',
+        'background_alpha' => 0,
+      ),
+    );
+    image_effect_save($this->style1, $effect);
+
+    // Attach a single "image_crop" effect to the second style that enlarges the
+    // source image to add additional area with no specified fill.
+    $effect = array(
+      'weight' => 0,
+      'name' => 'image_crop',
+      'data' => array('width' => 50, 'height' => 50, 'anchor' => 'center-center', 'background_color' => ''),
+      'data' => array(
+        'width' => 50,
+        'height' => 50,
+        'anchor' => 'center-center',
+        'background_has_color' => FALSE,
+        'background_color' => '',
+        'background_alpha' => 0,
+      ),
+    );
+    image_effect_save($this->style2, $effect);
+  }
+
+  /**
+   * Tests that image cropping uses the correct background color.
+   */
+  function testCropBackgroundColor() {
+    // Check if physical image file is really created.
+    $this->assertTrue(is_file($this->image->source), format_string('The source image (50x25) filled with blue (#0000ff) has been created: %url.', array('%url' => $this->image->source)));
+
+    $styles = array(
+      $this->style1->id() => array(
+        'style' => $this->style1,
+        'description' => 'Cropping center-to-center from 50x20 to 50x50 with red (#ff0000) color on additional area.',
+        'expected_color' => 'red (#ff0000)',
+        'expected_color_map' => array('red' => 255, 'green' => 0, 'blue' => 0, 'alpha' => 0),
+      ),
+      $this->style2->id() => array(
+        'style' => $this->style2,
+        'description' => 'Cropping center-to-center from 50x20 to 50x50 with no color specified for additional area.',
+        'expected_color' => 'transparent',
+        'expected_color_map' => array('red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 127),
+      ),
+    );
+
+    foreach ($styles as $style_name => $style_data) {
+      $name = array('%name' => $style_name);
+      $color = array('%color' => $style_data['expected_color']);
+      $uri = image_style_path($style_name, $this->image->source);
+
+      // Check if the Image Style has been created.
+      $this->assertTrue($style_data['style'], format_string('The Image Style "%name" has been created and is usable.', $name));
+
+      // Inform on current operation.
+      $this->assertTrue(TRUE, $style_data['description']);
+
+      // Create derivative with a simple GET.
+      $this->drupalGet(image_style_url($style_name, $this->image->source));
+
+      // Load image as resource to inspect the pixel from (5, 5) which is in the
+      // new added area.
+      $this->assert(TRUE, format_string('Checking a pixel color from the new area, at (5, 5) in "%file". Expecting %color.', $color + array('%file' => $uri)));
+      $image = image_load($uri);
+      $rgb = imagecolorat($image->resource, 5, 5);
+      $colors = imagecolorsforindex($image->resource, $rgb);
+      $this->assertIdentical($colors, $style_data['expected_color_map'], format_string('Pixel from (5, 5) is %color', $color));
+    }
+  }
+
+}
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
index 7f24796..74a276b 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
@@ -111,13 +111,14 @@ function testDesaturateEffect() {
    */
   function testRotateEffect() {
     // @todo: need to test with 'random' => TRUE
-    $this->assertTrue(image_rotate_effect($this->image, array('degrees' => 90, 'bgcolor' => '#fff')), 'Function returned the expected value.');
+    $data = array('degrees' => 90, 'background_has_color' => TRUE, 'background_color' => '#ffffff', 'background_alpha' => 0);
+    $this->assertTrue(image_rotate_effect($this->image, $data), 'Function returned the expected value.');
     $this->assertToolkitOperationsCalled(array('rotate'));
 
     // Check the parameters.
     $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');
+    $this->assertEqual($calls['rotate'][0][2], '#ffffff', 'Background color was passed correctly');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
index 06cb4a6..4acb59c 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Plugin\PluginBase;
 use Drupal\Component\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Utility\Color;
 use Drupal\system\Plugin\ImageToolkitInterface;
 
 /**
@@ -68,7 +69,7 @@ public function resize($image, $width, $height) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::rotate().
    */
-  public function rotate($image, $degrees, $background = NULL) {
+  public function rotate($image, $degrees, $background_color = NULL, $background_alpha = 0) {
     // 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));
@@ -79,14 +80,12 @@ public function rotate($image, $degrees, $background = NULL) {
     $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);
+    if (!empty($background_color)) {
+      if ($background = Color::hexToRgb($background_color)) {
+        $background = imagecolorallocatealpha($image->resource, $background['red'], $background['green'], $background['blue'], $background_alpha);
       }
-      $background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 0);
     }
-    // Set the background color as transparent if $background is NULL.
+    // Set the background color as transparent if $background_color is NULL.
     else {
       // Get the current transparent color.
       $background = imagecolortransparent($image->resource);
@@ -120,12 +119,23 @@ public function rotate($image, $degrees, $background = NULL) {
   }
 
   /**
-   * Implements \Drupal\system\Plugin\ImageToolkitInterface::crop().
+   * {@inheritdoc}
    */
-  public function crop($image, $x, $y, $width, $height) {
+  public function crop($image, $x, $y, $width, $height, $background_color = NULL, $background_alpha = 0) {
     $res = $this->createTmp($image, $width, $height);
 
-    if (!imagecopyresampled($res, $image->resource, 0, 0, $x, $y, $width, $height, $width, $height)) {
+    // Fill the background color if it has been defined.
+    if (!empty($background_color)) {
+      if ($background = Color::hexToRgb($background_color)) {
+        $background = imagecolorallocatealpha($res, $background['red'], $background['green'], $background['blue'], $background_alpha);
+        imagefill($res, 0, 0, $background);
+      }
+    }
+
+    // Copy the source image to our new destination image. We use
+    // $image->info['width'] instead of $width because we are copying using the
+    // source image's width and height, not the destination width and height.
+    if (!imagecopyresampled($res, $image->resource, -$x, -$y, 0, 0, $image->info['width'], $image->info['height'], $image->info['width'], $image->info['height'])) {
       return FALSE;
     }
 
diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
index 0b22f3c..bc83360 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
@@ -55,19 +55,21 @@ function resize($image, $width, $height);
    *   $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.
+   * @param string $background_color
+   *   (optional) An hexadecimal string specifying the background color to use
+   *   for uncovered area of the image after the rotation. Examples: "#RGB",
+   *   "#RRGGBB". For images that support transparency, this will default to
+   *   transparent. Otherwise it will be white.
+   * @param string $background_alpha
+   *   (optional) The value of alpha transparency for $background_color.
+   *   Defaults to 0.
    *
    * @return bool
    *   TRUE or FALSE, based on success.
    *
    * @see image_rotate()
    */
-  function rotate($image, $degrees, $background = NULL);
+  function rotate($image, $degrees, $background_color = NULL, $background_alpha = 0);
 
   /**
    * Crops an image.
@@ -83,13 +85,22 @@ function rotate($image, $degrees, $background = NULL);
    *   The width of the cropped area, in pixels.
    * @param int $height
    *   The height of the cropped area, in pixels.
+   * @param string $background_color
+   *   (optional) An hexadecimal string specifying the background color to use
+   *   for the additional area created when cropping to larger dimensions than
+   *   the source. Examples: "#RGB", "#RRGGBB". For images that support
+   *   transparency, this will default to transparent. Otherwise it will be
+   *   white.
+   * @param string $background_alpha
+   *   (optional) The value of alpha transparency for $background_color.
+   *   Defaults to 0.
    *
    * @return bool
    *   TRUE or FALSE, based on success.
    *
    * @see image_crop()
    */
-  function crop($image, $x, $y, $width, $height);
+  function crop($image, $x, $y, $width, $height, $background_color = NULL, $background_alpha = 0);
 
   /**
    * Converts an image resource to grayscale.
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 624dc79..7041bbd 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
@@ -165,14 +165,14 @@ function testManipulations() {
       $operations += array(
         'rotate_5' => array(
           'function' => 'rotate',
-          'arguments' => array(5, 0xFF00FF), // Fuchsia background.
+          'arguments' => array(5, '#FF00FF'), // Fuchsia background.
           'width' => 42,
           'height' => 24,
           'corners' => array_fill(0, 4, $this->fuchsia),
         ),
         'rotate_90' => array(
           'function' => 'rotate',
-          'arguments' => array(90, 0xFF00FF), // Fuchsia background.
+          'arguments' => array(90, '#FF00FF'), // Fuchsia background.
           'width' => 20,
           'height' => 40,
           'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php
index 12ebae7..256fded 100644
--- a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php
+++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php
@@ -62,10 +62,10 @@ public function save($image, $destination) {
   }
 
   /**
-   * Implements \Drupal\system\Plugin\ImageToolkitInterface::crop().
+   * {@inheritdoc}
    */
-  public function crop($image, $x, $y, $width, $height) {
-    $this->logCall('crop', array($image, $x, $y, $width, $height));
+  public function crop($image, $x, $y, $width, $height, $background_color = NULL, $background_alpha = 0) {
+    $this->logCall('crop', array($image, $x, $y, $width, $height, $background_color, $background_alpha));
     return TRUE;
   }
 
@@ -78,10 +78,10 @@ public function resize($image, $width, $height) {
   }
 
   /**
-   * Implements \Drupal\system\Plugin\ImageToolkitInterface::rotate().
+   * {@inheritdoc}
    */
-  public function rotate($image, $degrees, $background = NULL) {
-    $this->logCall('rotate', array($image, $degrees, $background));
+  public function rotate($image, $degrees, $background_color = NULL, $background_alpha = 0) {
+    $this->logCall('rotate', array($image, $degrees, $background_color, $background_alpha));
     return TRUE;
   }
 
-- 
1.7.9.6 (Apple Git-31.1)

