Here's a patch to

// $Id: imagecache.module,v 1.19.2.26 2007/03/15 08:53:36 quicksketch Exp $

that implements an action to desaturate an image. As desaturation is not (yet) implemented in Drupal's image.inc, some routines (that should/could maybe go into image.inc) are added at the end of the file. I attach the patch as a file and paste it here inline:

Index: imagecache.module
===================================================================
--- imagecache.module   (revision 102)
+++ imagecache.module   (working copy)
@@ -196,6 +196,12 @@
             watchdog('imagecache', t('Imagecache crop action ID %id failed.', array('%id' => $action['actionid'])), WATCHDOG_ERROR);
           }
           break;
+
+        case 'desaturate':
+          if (!image_desaturate($tmpdestination, $tmpdestination)) {
+            watchdog('imagecache', t('Imagecache desaturate action ID %id failed.', array('%id' => $action['actionid'])), WATCHDOG_ERROR);
+          }
+          break;
       }
     }
     file_move($tmpdestination, $destination);
@@ -675,7 +681,11 @@
           );
           break;
 
-      case 'watermark':
+      case 'desaturate':
+       // no options
+       break;
+ 
+     case 'watermark':
         //Think about this one...        
     }
     $form[$actionid]['remove'] = array(
@@ -689,11 +699,12 @@
   $helptext['scale']  = t('<strong>Scale</strong>: Resize an image maintaining the original aspect-ratio (only one value necessary).');
   $helptext['resize'] = t('<strong>Resize</strong>: Resize an image to an exact set of dimensions, ignoring aspect ratio.');
   $helptext['crop']   = t('<strong>Crop</strong>: Crop an image to the rectangle specified by the given offsets and dimensions.');
+  $helptext['desaturate']   = t('<strong>Desaturate</strong>: Convert an image to gray-scale.');
   $description = '<ul><li>' . implode('</li><li>',$helptext) . '</li><ul>';
   
   $form['newaction'] = array(
     '#type' => 'select',
-    '#options' => array('' => t('select...'), 'scale' => t('Scale'), 'resize' => t('Resize'), 'crop' => t('Crop')),
+    '#options' => array('' => t('select...'), 'scale' => t('Scale'), 'resize' => t('Resize'), 'crop' => t('Crop'), 'desaturate' => t('Desaturate')),
     '#title' => t('Add a New Action'),
     '#description' => $description,
   );
@@ -758,3 +769,54 @@
     file_delete($ipath);
   }
 }
+
+/**
+ * Utilities to desaturate an image (i.e., to convert it to gray scale).
+ *
+ * Note: This might go into image.inc.
+ */
+if (!function_exists('image_desaturate')) {
+  function image_desaturate($source, $destination) {
+    return image_toolkit_invoke('desaturate', array($source, $destination));
+  }
+}
+if (!function_exists('image_gd_desaturate')) {
+  function image_gd_desaturate($source, $destination) {
+    $info = image_get_info($source);
+    if (!$info) {
+      return FALSE;
+    }
+    $width  = $info['width'];
+    $height = $info['height'];
+
+    $im = image_gd_open($source, $info['extension']);
+    $res = imageCreateTrueColor($width, $height);
+    
+    for ($y = 0; $y < $height; ++$y)
+      for ($x = 0; $x < $width; ++$x) {
+       $rgb = imagecolorat($im, $x, $y);
+       $red   = ($rgb >> 16) & 0xFF;
+       $green = ($rgb >> 8)  & 0xFF;
+       $blue  = $rgb & 0xFF;
+
+       $gray = round(.299*$red + .587*$green + .114*$blue);
+
+       // shift gray level to the left
+       $grayR = $gray << 16;   // R: red
+       $grayG = $gray << 8;    // G: green
+       $grayB = $gray;         // B: blue
+       $grayColor = $grayR | $grayG | $grayB;
+
+       // set the pixel color
+       imagesetpixel($res, $x, $y, $grayColor);
+       imagecolorallocate($res, $gray, $gray, $gray);
+      }
+    
+    $result = image_gd_close($res, $destination, $info['extension']);
+
+    imageDestroy($res);
+    imageDestroy($im);
+    
+    return $result;
+  }
+}

Kaspar

CommentFileSizeAuthor
#1 imagecache_0.txt4.02 KBhbfkf
imagecache.txt3.43 KBhbfkf

Comments

hbfkf’s picture

StatusFileSize
new4.02 KB

Here's a bugfix (for images with CLUTs) and a performance improvement for PHP5:

Index: imagecache.module
===================================================================
--- imagecache.module   (revision 104)
+++ imagecache.module   (working copy)
@@ -196,6 +196,12 @@
             watchdog('imagecache', t('Imagecache crop action ID %id failed.', array('%id' => $action['actionid'])), WATCHDOG_ERROR);
           }
           break;
+
+        case 'desaturate':
+          if (!image_desaturate($tmpdestination, $tmpdestination)) {
+            watchdog('imagecache', t('Imagecache desaturate action ID %id failed.', array('%id' => $action['actionid'])), WATCHDOG_ERROR);
+          }
+          break;
       }
     }
     file_move($tmpdestination, $destination);
@@ -675,7 +681,11 @@
           );
           break;
 
-      case 'watermark':
+      case 'desaturate':
+       // no options
+       break;
+ 
+     case 'watermark':
         //Think about this one...        
     }
     $form[$actionid]['remove'] = array(
@@ -689,11 +699,12 @@
   $helptext['scale']  = t('<strong>Scale</strong>: Resize an image maintaining the original aspect-ratio (only one value necessary).');
   $helptext['resize'] = t('<strong>Resize</strong>: Resize an image to an exact set of dimensions, ignoring aspect ratio.');
   $helptext['crop']   = t('<strong>Crop</strong>: Crop an image to the rectangle specified by the given offsets and dimensions.');
+  $helptext['desaturate']   = t('<strong>Desaturate</strong>: Convert an image to gray-scale.');
   $description = '<ul><li>' . implode('</li><li>',$helptext) . '</li><ul>';
   
   $form['newaction'] = array(
     '#type' => 'select',
-    '#options' => array('' => t('select...'), 'scale' => t('Scale'), 'resize' => t('Resize'), 'crop' => t('Crop')),
+    '#options' => array('' => t('select...'), 'scale' => t('Scale'), 'resize' => t('Resize'), 'crop' => t('Crop'), 'desaturate' => t('Desaturate')),
     '#title' => t('Add a New Action'),
     '#description' => $description,
   );
@@ -758,3 +769,71 @@
     file_delete($ipath);
   }
 }
+
+/**
+ * Utilities to desaturate an image (i.e., to convert it to gray scale).
+ *
+ * Note: This might go into image.inc.
+ */
+if (!function_exists('image_desaturate')) {
+  function image_desaturate($source, $destination) {
+    return image_toolkit_invoke('desaturate', array($source, $destination));
+  }
+}
+if (!function_exists('image_gd_desaturate')) {
+  function image_gd_desaturate($source, $destination) {
+    $info = image_get_info($source);
+    if (!$info) {
+      return FALSE;
+    }
+    $width  = $info['width'];
+    $height = $info['height'];
+
+    $im = image_gd_open($source, $info['extension']);
+
+    if (function_exists('imagefilter')) { // PHP 5
+      if (!imagefilter($im, IMG_FILTER_GRAYSCALE)) {
+       return FALSE;
+      }
+      $result = image_gd_close($im, $destination, $info['extension']);
+      imageDestroy($im);
+
+    } else {                              // PHP < 5
+      $res = imageCreateTrueColor($width, $height);
+      
+      for ($y = 0; $y < $height; ++$y)
+       for ($x = 0; $x < $width; ++$x) {
+         $rgb = imagecolorat($im, $x, $y);
+         if (imageistruecolor($im)) {
+           $red   = ($rgb >> 16) & 0xFF;
+           $green = ($rgb >> 8)  & 0xFF;
+           $blue  = $rgb & 0xFF;
+         } else {
+           $rgb = imagecolorsforindex($im, $rgb);
+           $red   = $rgb['red'];
+           $green = $rgb['green'];
+           $blue  = $rgb['blue'];
+         }
+         
+         $gray = round(.299*$red + .587*$green + .114*$blue);
+         
+         // shift gray level to the left
+         $grayR = $gray << 16;   // R: red
+         $grayG = $gray << 8;    // G: green
+         $grayB = $gray;         // B: blue
+         $grayColor = $grayR | $grayG | $grayB;
+         
+         // set the pixel color
+         imagesetpixel($res, $x, $y, $grayColor);
+         imagecolorallocate($res, $gray, $gray, $gray);
+       }
+      
+      $result = image_gd_close($res, $destination, $info['extension']);
+      
+      imageDestroy($res);
+      imageDestroy($im);
+    }
+    
+    return $result;
+  }
+}
guardian’s picture

subscribing, would love to see a desaturate action commited

jpsalter’s picture

This worked well for me. I too would like to see it committed. I used it on this website for user images (I think the gray scale images look good with the stock Drupal theme):
http://techunderground.org/tech-underground-members

joemac79’s picture

Wow, this seems awesome, exactly where would I put this. ( I'm a PHP noob) ??

dopry’s picture

Status: Needs review » Fixed

committed to head. gracias. I got the plug in interface done, so bring 'em on...

Anonymous’s picture

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for two weeks with no activity.