--- D:/Dokumente und Einstellungen/Administrator/Desktop/canvasactions.inc	Tue May 19 20:03:30 2009
+++ D:/htdocs/mesport/sites/all/modules/contrib/imagecache_actions/canvasactions.inc	Mon Aug 31 04:30:11 2009
@@ -606,76 +606,128 @@
     return TRUE;
   }
   
+  // read settings
   $width = $image->info['width'];
   $height = $image->info['height'];
   $radius = $action['radius'];
   $independent_corners = $action['independent_corners_set']['independent_corners'];
   $radii = $action['independent_corners_set']['radii'];
-  $diameter = array();
-
-  if ($action['antialias']) {
-    $width = $width * 3;
-    $height = $height * 3;
-    $radius = $radius * 3;
-    foreach ($radii as $corner => $corner_radius) {
-      $radii[$corner] = $radii[$corner] * 3;
+  
+  $im = &$image->resource;
+  // prepare drawing on the alpha channel
+  imagesavealpha($im, TRUE);
+  imagealphablending($im, false);
+  
+  foreach ($radii as $key => $r) {
+    if (!$independent_corners) {
+      // use the all-the-same radius setting
+      $r = $radius;
     }
-  }
-
-  foreach ($radii as $corner => $corner_radius) {
-    if(!$independent_corners){
-      // Use the unique maths for independant corners, 
-      // even if they are all the same.
-      $radii[$corner] = $radius;
+    // key can be 'tl', 'tr', 'bl', 'br'.
+    $is_bottom = ($key{0}=='b');
+    $is_right = ($key{1}=='r');
+    
+    // dx and dy are in "continuous coordinates",
+    // and mark the distance of the pixel middle to the image border.
+    for ($dx = .5; $dx < $r; ++$dx) {
+      for ($dy = .5; $dy < $r; ++$dy) {
+        
+        // ix and iy are in discrete pixel indices,
+        // counting from the top left
+        $ix = floor($is_right ? $width-$dx : $dx);
+        $iy = floor($is_bottom ? $height-$dy : $dy);
+        
+        $opacity = _canvasactions_roundedcorners_pixel_opacity($dx, $dy, $r);
+        if ($opacity >= 1) {
+          // we can finish this row,
+          // all following pixels will be fully opaque.
+          break;
+        }
+        
+        // color lookup at ($ix, $iy).
+        $color = imagecolorat($im, $ix, $iy);
+        $color = imagecolorsforindex($im, $color);
+        
+        // Keys are 'red', 'green', 'blue', 'alpha'.
+        // We prefer boring numbers instead.
+        foreach (array_keys($color) as $i => $key) {
+          $rgba[$i] = $color[$key];
+        }
+        
+        if (isset($rgba[3])) {
+          $rgba[3] = 127 - round($opacity * (127 - $rgba[3]));
+        } else {
+          $rgba[3] = 127 - round($opacity * 127);
+        }
+        // value should not be more than 127, and not less than 0.
+        $rgba[3] = ($rgba[3] > 127) ? 127 : (($rgba[3] < 0) ? 0 : $rgba[3]);
+        $color = imagecolorallocatealpha($im, $rgba[0], $rgba[1], $rgba[2], $rgba[3]);
+        imagesetpixel($im, $ix, $iy, $color);
+      }
     }
-    $diameter[$corner] = $radii[$corner] * 2;
   }
   
-  // Create a mask with rounded corners
-  $mask = imagecreatetruecolor($width, $height);
-
-  // Is using the toolkit really worth the bother?
-  // Just doing it in an attempt to be consistent
-  $mask_image = (object)array(
-    'resource' => &$mask, 
-    'info' => array('width' => $width, 'height' => $height, 'extension' => 'png'), 
-    'toolkit' => $image->toolkit,
-    'resource' => &$mask,
-  );
-
-  // Start with a blank slate
-  $background = imagecolorallocatealpha($mask, 255, 255, 255, 127);
-  imagesavealpha($mask, TRUE);
-  imagealphablending($mask, false);
-  imagefilledrectangle($mask, 0, 0, $width, $height, $background);
-  // Place solid lumps on it
-  $foreground = imagecolorallocatealpha($mask, 0, 0, 0, 0);
+  return TRUE;
+}
 
-  // Place blobs in the corners
-  ImageFilledEllipse($mask, $radii['tl'], $radii['tl'], $diameter['tl'], $diameter['tl'], $foreground);
-  ImageFilledEllipse($mask, $width-$radii['tr'], $radii['tr'], $diameter['tr'], $diameter['tr'], $foreground);
-  ImageFilledEllipse($mask, $radii['bl'], $height-$radii['bl'], $diameter['bl'], $diameter['bl'], $foreground);
-  ImageFilledEllipse($mask, $width-$radii['br'], $height-$radii['br'], $diameter['br'], $diameter['br'], $foreground);
-  
-  // Block out the middle
-  ImageFilledRectangle($mask, $radii['tl'], 0, $width*0.5, $height*0.5, $foreground);
-  ImageFilledRectangle($mask, 0, $radii['tl'], $width*0.5, $height*0.5, $foreground);
-  ImageFilledRectangle($mask, $width*0.5, 0, $width-$radii['tr'], $height*0.5, $foreground);
-  ImageFilledRectangle($mask, $width*0.5, $radii['tr'], $width, $height*0.5, $foreground);
-  ImageFilledRectangle($mask, 0, $height*0.5, $width*0.5, $height-$radii['bl'], $foreground);
-  ImageFilledRectangle($mask, $radii['bl'], $height*0.5, $width*0.5, $height, $foreground);
-  ImageFilledRectangle($mask, $width*0.5, $height*0.5, $width, $height-$radii['br'], $foreground);
-  ImageFilledRectangle($mask, $width*0.5, $height*0.5, $width-$radii['br'], $height, $foreground);
 
-  if ($action['antialias']) {
-    // Use toolkit so scale down again. 
-    imageapi_image_scale($mask_image, $width/3, $height/3);
-    $mask = $mask_image->resource;
+/**
+ * Calculate the transparency value for a rounded corner pixel
+ * 
+ * @param $x
+ *   distance from pixel center to image border (left or right)
+ *   should be an integer + 0.5
+ *   
+ * @param $y
+ *   distance from pixel center to image border (top or bottom)
+ *   should be an integer + 0.5
+ *   
+ * @param $r
+ *   radius of the rounded corner
+ *   should be an integer
+ *   
+ * @return float
+ *   opacity value between 0 (fully transparent) and 1 (fully opaque).
+ */
+function _canvasactions_roundedcorners_pixel_opacity($x, $y, $r) {
+  if ($x < 0 || $y < 0) {
+    return 0;
+  } else if ($x > $r || $y > $r) {
+    return 1;
+  }
+  $dist_2 = ($r-$x)*($r-$x) + ($r-$y)*($r-$y);
+  $r_2 = $r*$r;
+  if ($dist_2 > ($r+.8)*($r+.8)) {
+    return 0;
+  } else if ($dist_2 < ($r-.8)*($r-.8)) {
+    return 1; 
+  } else {
+    // this pixel needs special analysis.
+    // thanks to a quite efficient algorithm, we can afford 10x antialiasing :)
+    $opacity = .5;
+    if ($x > $y) {
+      // cut the pixel into 10 vertical "stripes"
+      for ($dx=-0.45; $dx<.5; $dx+=.1) {
+        // find out where the rounded corner edge intersects with the stripe
+        // this is plain triangle geometry.
+        $dy = $r - $y - sqrt($r_2 - ($r-$x-$dx)*($r-$x-$dx));
+        $dy = ($dy > .5) ? .5 : (($dy < -.5) ? -.5 : $dy);
+        // count the opaque part of the stripe.
+        $opacity -= .1 * $dy;
+      }
+    } else {
+      // cut the pixel into 10 horizontal "stripes"
+      for ($dy=-0.45; $dy<.5; $dy+=.1) {
+        // this is the math:
+        //   ($r-$x-$dx)^2 + ($r-$y-$dy)^2 = $r^2
+        //   $dx = $r - $x - sqrt($r^2 - ($r-$y-$dy)^2) 
+        $dx = $r - $x - sqrt($r_2 - ($r-$y-$dy)*($r-$y-$dy));
+        $dx = ($dx > .5) ? .5 : (($dx < -.5) ? -.5 : $dx);
+        $opacity -= .1 * $dx;
+      }
+    }
+    return ($opacity < 0) ? 0 : (($opacity > 1) ? 1 : $opacity);
   }
-  // Now we have a mask. Merge to get a result
-  canvasactions_mask($image, $mask_image);
-  
-  return TRUE;
 }
 
 /**
