Specify a background color for crops since they can add area to the canvas.

From:  <>

http://drupal.org/node/437702
---

 includes/image.inc                         |   30 +++++--
 modules/simpletest/tests/image.test        |   31 ++++++-
 modules/simpletest/tests/image_test.module |    8 +-
 modules/system/image.gd.inc                |  122 +++++++++++++++++-----------
 4 files changed, 122 insertions(+), 69 deletions(-)


diff --git includes/image.inc includes/image.inc
index 4309ac9..45bff2c 100644
--- includes/image.inc
+++ includes/image.inc
@@ -250,19 +250,22 @@ function image_resize(stdClass $image, $width, $height) {
  * @param $degrees
  *   The number of (clockwise) degrees to rotate the image.
  * @param $background
- *   An hexadecimal integer specifying the background color to use for the
- *   uncovered area of the image after the rotation. E.g. 0x000000 for black,
- *   0xff00ff for magenta, and 0xffffff for white. For images that support
- *   transparency, this will default to transparent. Otherwise it will
- *   be white.
+ *   An optional, 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. If the format
+ *   supports transparency and the $transparent parameter is TRUE this value
+ *   will be ignored.
+ * @param $transparent
+ *   Boolean indicating that formats that support transparency (e.g. PNG and
+ *   GIF) should ignore the background color and use a transparent background.
  * @return
  *   TRUE or FALSE, based on success.
  *
  * @see image_load()
  * @see image_gd_rotate()
  */
-function image_rotate(stdClass $image, $degrees, $background = NULL) {
-  return image_toolkit_invoke('rotate', $image, array($degrees, $background));
+function image_rotate(stdClass $image, $degrees, $background = 0xffffff, $transparent = TRUE) {
+  return image_toolkit_invoke('rotate', $image, array($degrees, $background, $transparent));
 }
 
 /**
@@ -278,6 +281,15 @@ function image_rotate(stdClass $image, $degrees, $background = NULL) {
  *   The target width, in pixels.
  * @param $height
  *   The target height, in pixels.
+ * @param $background
+ *   An optional, hexadecimal integer specifying the background color to use
+ *   for areas added during crops that add area to the image. E.g. 0x000000 for
+ *   black, 0xff00ff for magenta, and 0xffffff for white. If the format
+ *   supports transparency and the $transparent parameter is TRUE this value
+ *   will be ignored.
+ * @param $transparent
+ *   Boolean indicating that formats that support transparency (e.g. PNG and
+ *   GIF) should ignore the background color and use a transparent background.
  * @return
  *   TRUE or FALSE, based on success.
  *
@@ -285,7 +297,7 @@ function image_rotate(stdClass $image, $degrees, $background = NULL) {
  * @see image_scale_and_crop()
  * @see image_gd_crop()
  */
-function image_crop(stdClass $image, $x, $y, $width, $height) {
+function image_crop(stdClass $image, $x, $y, $width, $height, $background = 0xffffff, $transparent = TRUE) {
   $aspect = $image->info['height'] / $image->info['width'];
   if (empty($height)) $height = $width / $aspect;
   if (empty($width)) $width = $height * $aspect;
@@ -293,7 +305,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_invoke('crop', $image, array($x, $y, $width, $height, $background, $transparent));
 }
 
 /**
diff --git modules/simpletest/tests/image.test modules/simpletest/tests/image.test
index cd2423b..2367884 100644
--- modules/simpletest/tests/image.test
+++ modules/simpletest/tests/image.test
@@ -149,20 +149,21 @@ class ImageToolkitTestCase extends DrupalWebTestCase {
    * Test the image_rotate() function.
    */
   function testRotate() {
-    $this->assertTrue(image_rotate($this->image, 90, 1), t('Function returned the expected value.'));
+    $this->assertTrue(image_rotate($this->image, 90, 0xabcdef, TRUE), t('Function returned the expected value.'));
     $this->assertToolkitOperationsCalled(array('rotate'));
 
     // Check the parameters.
     $calls = image_test_get_all_calls();
     $this->assertEqual($calls['rotate'][0][1], 90, t('Degrees were passed correctly'));
-    $this->assertEqual($calls['rotate'][0][2], 1, t('Background color was passed correctly'));
+    $this->assertEqual($calls['rotate'][0][2], 0xabcdef, t('Background color was passed correctly'));
+    $this->assertEqual($calls['rotate'][0][3], TRUE, t('Transparent was passed correctly'));
   }
 
   /**
    * Test the image_crop() function.
    */
   function testCrop() {
-    $this->assertTrue(image_crop($this->image, 1, 2, 3, 4), t('Function returned the expected value.'));
+    $this->assertTrue(image_crop($this->image, 1, 2, 3, 4, 0xabcdef, TRUE), t('Function returned the expected value.'));
     $this->assertToolkitOperationsCalled(array('crop'));
 
     // Check the parameters.
@@ -171,6 +172,8 @@ class ImageToolkitTestCase extends DrupalWebTestCase {
     $this->assertEqual($calls['crop'][0][2], 2, t('Y was passed correctly'));
     $this->assertEqual($calls['crop'][0][3], 3, t('Width was passed correctly'));
     $this->assertEqual($calls['crop'][0][4], 4, t('Height was passed correctly'));
+    $this->assertEqual($calls['crop'][0][5], 0xabcdef, t('Background was passed correctly'));
+    $this->assertEqual($calls['crop'][0][6], TRUE, t('Transparent was passed correctly'));
   }
 
   /**
@@ -310,6 +313,20 @@ class ImageToolkitGdTestCase extends DrupalWebTestCase {
         'height' => 12,
         'corners' => array_fill(0, 4, $this->white),
       ),
+      'upcrop_colored' => array(
+        'function' => 'crop',
+        'arguments' => array(0, 0, 80, 40, 0xFF00FF, FALSE),
+        'width' => 80,
+        'height' => 40,
+        'corners' => array_fill(0, 4, $this->fuchsia),
+      ),
+      'upcrop_transparent' => array(
+        'function' => 'crop',
+        'arguments' => array(0, 0, 80, 40, 0xFF00FF, TRUE),
+        'width' => 80,
+        'height' => 40,
+        'corners' => array_fill(0, 4, $this->fuchsia),
+      ),
       'scale_and_crop' => array(
         'function' => 'scale_and_crop',
         'arguments' => array(10, 8),
@@ -324,28 +341,28 @@ class ImageToolkitGdTestCase extends DrupalWebTestCase {
       $operations += array(
         'rotate_5' => array(
           'function' => 'rotate',
-          'arguments' => array(5, 0xFF00FF), // Fuchsia background.
+          'arguments' => array(5, 0xFF00FF, FALSE), // 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, 0xFF00FF, FALSE), // Fuchsia background.
           'width' => 20,
           'height' => 40,
           'corners' => array($this->fuchsia, $this->red, $this->green, $this->blue),
         ),
         'rotate_transparent_5' => array(
           'function' => 'rotate',
-          'arguments' => array(5),
+          'arguments' => array(5, 0xffffff, TRUE),
           'width' => 42,
           'height' => 24,
           'corners' => array_fill(0, 4, $this->transparent),
         ),
         'rotate_transparent_90' => array(
           'function' => 'rotate',
-          'arguments' => array(90),
+          'arguments' => array(90, 0xffffff, TRUE),
           'width' => 20,
           'height' => 40,
           'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
diff --git modules/simpletest/tests/image_test.module modules/simpletest/tests/image_test.module
index 86c0c64..23b2d9f 100644
--- modules/simpletest/tests/image_test.module
+++ modules/simpletest/tests/image_test.module
@@ -100,8 +100,8 @@ function image_test_save(stdClass $image, $destination) {
 /**
  * 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));
+function image_test_crop(stdClass $image, $x, $y, $width, $height, $background, $transparent) {
+  _image_test_log_call('crop', array($image, $x, $y, $width, $height, $background, $transparent));
   return TRUE;
 }
 
@@ -116,8 +116,8 @@ function image_test_resize(stdClass $image, $width, $height) {
 /**
  * Image tookit's rotate operation.
  */
-function image_test_rotate(stdClass $image, $degrees, $background = NULL) {
-  _image_test_log_call('rotate', array($image, $degrees, $background));
+function image_test_rotate(stdClass $image, $degrees, $background, $transparent) {
+  _image_test_log_call('rotate', array($image, $degrees, $background, $transparent));
   return TRUE;
 }
 
diff --git modules/system/image.gd.inc modules/system/image.gd.inc
index d009caf..cc5e6c8 100644
--- modules/system/image.gd.inc
+++ modules/system/image.gd.inc
@@ -106,17 +106,20 @@ function image_gd_resize(stdClass $image, $width, $height) {
  * @param $degrees
  *   The number of (clockwise) degrees to rotate the image.
  * @param $background
- *   An hexadecimal integer specifying the background color to use for the
- *   uncovered area of the image after the rotation. E.g. 0x000000 for black,
- *   0xff00ff for magenta, and 0xffffff for white. For images that support
- *   transparency, this will default to transparent. Otherwise it will
- *   be white.
+ *   An optional, 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. If the format
+ *   supports transparency and the $transparent parameter is TRUE this value
+ *   will be ignored.
+ * @param $transparent
+ *   Boolean indicating that formats that support transparency (e.g. PNG and
+ *   GIF) should ignore the background color and use a transparent background.
  * @return
  *   TRUE or FALSE, based on success.
  *
  * @see image_rotate()
  */
-function image_gd_rotate(stdClass $image, $degrees, $background = NULL) {
+function image_gd_rotate(stdClass $image, $degrees, $background = 0xffffff, $transparent = TRUE) {
   // PHP installations using non-bundled GD do not have imagerotate.
   if (!drupal_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));
@@ -127,38 +130,31 @@ function image_gd_rotate(stdClass $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);
-    }
-    $background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 0);
+  $rgb = array();
+  for ($i = 16; $i >= 0; $i -= 8) {
+    $rgb[] = (($background >> $i) & 0xFF);
   }
-  // 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);
+  // If the image has a transparent color and that's used
+  $transparent_index = imagecolortransparent($image->resource);
+  if ($transparent && $transparent_index != 0) {
+    // GIFs need to reassign the transparent color after performing the rotate
+    // so make note of the current, transparent RGB value.
+    if ($image->info['extension'] == 'gif') {
+      $gif_transparent_rgb = imagecolorsforindex($image->resource, $transparent_index);
     }
+    $background = $transparent_index;
   }
-
-  // Images are assigned a new color pallete 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);
-    }
+  else {
+    $background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 0);
   }
 
+  // Rotate it.
   $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']);
+  // Update the GIFs transparent color.
+  if (isset($gif_transparent_rgb)) {
+    $background = imagecolorexactalpha($image->resource, $gif_transparent_rgb['red'], $gif_transparent_rgb['green'], $gif_transparent_rgb['blue'], $gif_transparent_rgb['alpha']);
     imagecolortransparent($image->resource, $background);
   }
 
@@ -181,15 +177,24 @@ function image_gd_rotate(stdClass $image, $degrees, $background = NULL) {
  *   The width of the cropped area, in pixels.
  * @param $height
  *   The height of the cropped area, in pixels.
+ * @param $background
+ *   An optional, hexadecimal integer specifying the background color to use
+ *   for areas added during crops that add area to the image. E.g. 0x000000 for
+ *   black, 0xff00ff for magenta, and 0xffffff for white. If the format
+ *   supports transparency and the $transparent parameter is TRUE this value
+ *   will be ignored.
+ * @param $transparent
+ *   Boolean indicating that formats that support transparency (e.g. PNG and
+ *   GIF) should ignore the background color and use a transparent background.
  * @return
  *   TRUE or FALSE, based on success.
  *
  * @see image_crop()
  */
-function image_gd_crop(stdClass $image, $x, $y, $width, $height) {
-  $res = image_gd_create_tmp($image, $width, $height);
+function image_gd_crop(stdClass $image, $x, $y, $width, $height, $background = 0xffffff, $transparent = TRUE) {
+  $res = image_gd_create_tmp($image, $width, $height, $background, $transparent);
 
-  if (!imagecopyresampled($res, $image->resource, 0, 0, $x, $y, $width, $height, $width, $height)) {
+  if (!imagecopy($res, $image->resource, 0, 0, $x, $y, $width, $height)) {
     return FALSE;
   }
 
@@ -284,37 +289,56 @@ function image_gd_save(stdClass $image, $destination) {
  * @return
  *   A GD image handle.
  */
-function image_gd_create_tmp(stdClass $image, $width, $height) {
+function image_gd_create_tmp(stdClass $image, $width, $height, $background = 0xffffff, $transparency = TRUE) {
   $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']);
+  // Convert the hexadecimal background value to a color index value.
+  $rgb = array();
+  for ($i = 16; $i >= 0; $i -= 8) {
+    $rgb[] = (($background >> $i) & 0xFF);
+  }
 
-      // Flood with our new transparent color.
-      imagefill($res, 0, 0, $transparent);
-      imagecolortransparent($res, $transparent);
+dsm('transparency ' . $transparent);
+  if ($transparent && $image->info['extension'] == 'gif') {
+dsm('making a transparent gif');
+    // Check if the GIF has an existing transparent color.
+    $transparent_index = imagecolortransparent($image->resource);
+dsm('$transparent_index');dsm($transparent_index);
+    if ($transparent_index >= 0) {
+#      $gif_transparent_rgb = imagecolorsforindex($image->resource, $transparent_index);
+#dsm('$gif_transparent_rgb:'); dsm($gif_transparent_rgb);
+#      $background = imagecolorallocate($res, $gif_transparent_rgb['red'], $gif_transparent_rgb['green'], $gif_transparent_rgb['blue']);
+      $background = $transparent_index;
+    }
+    else {
+      $background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 127);
     }
+dsm($background);
+    // Flood with our new color then mark it as transparent.
+    imagefill($res, 0, 0, $background);
+#    imagefilledrectangle($res, 0, 0, $width, $height, $background);
+    imagecolortransparent($res, $background);
   }
-  elseif ($image->info['extension'] == 'png') {
+  elseif ($transparent && $image->info['extension'] == 'png') {
+dsm('making a transparent png');
     imagealphablending($res, FALSE);
-    $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
-    imagefill($res, 0, 0, $transparency);
+    $background = imagecolorallocatealpha($res, 0, 0, 0, 127);
+    imagefill($res, 0, 0, $background);
+#    imagefilledrectangle($res, 0, 0, $width, $height, $background);
     imagealphablending($res, TRUE);
     imagesavealpha($res, TRUE);
   }
   else {
-    imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
+dsm('making solid bg');
+    $background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 0);
+    imagefill($res, 0, 0, $background);
+#    imagefilledrectangle($res, 0, 0, $width, $height, $background);
   }
 
   return $res;
 }
 
+
 /**
  * @} End of "ingroup image".
  */
