Adverts

Archives By Subject

Calendar

Mon Tue Wed Thu Fri Sat Sun
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

Search

RSS


Tags

adobe apache book review cfimage coldfusion google google chrome hosting iis internet explorer java javascript jquery lucene photoshop regex ses urls software review sql injection svn trac

Subscribe

Enter your email address to subscribe to this blog.

Rounding image corners in CF8

I'm currently working on a site where the staff can upload images for display. Part of the design requirement, is that the corners of these images are rounded off. Step forward coldfuson 8...

Image support in coldfusion was long overdue. Although there were some very good 3rd party components (such as the Alagad Image Component), there should have been something bundled with coldfusion many versions ago. Coldfusion 8 comes with the new <cfimage> tag and dozens of image manipulation and drawing functions. So drawing rounded corners should be easy?

Unfortunately not...

What I wanted to build was a generic function that could apply rounded corners of any colour and size to a coldfuson image object.

My first thought was to create partially transparent corner images in photoshop and paste them on top of the target image. Although this would have worked for one project, I knew that further projects would require the same functionality, but with different sized corners and different colours. I didn't even bother trying this method.

I started looking at image functions such as ImageDrawRoundRect() and ImageDrawArc(). The problem with all of these though, is that they masked the wrong part of the images - all that was showing of the original image was the corners. I wanted to somehow reverse them.

After some playing around, I discovered that you can create a new image object with a transparent background. Next was to draw a rounded rectangle over it. The surprise came when I tried the ImageNegative() function on it - it reversed the image so that the centre was transparent as required. This could then be placed on top of the target image as shown in the code below:

img = imageNew(sourcepath);
   ImageSetAntialiasing(img);
   
   corner = ImageNew("",ImageGetWidth(img),ImageGetHeight(img),"argb");
   ImageSetDrawingColor(corner,red);
   ImageSetAntialiasing(corner);
   ImageDrawRoundRect(corner, 0, 0, ImageGetWidth(img), ImageGetHeight(img), diameter, diameter, true);
   ImageNegative(corner);
   ImagePaste(img, corner, 0, 0);

After a little testing, it soon became obvious that this technique seemed to ignore the colour setting of the corners - it seems that the negative of transparent was always white. While this would be ok for most projects, there would be some where I needed corners of a different colour because the page had a non-white background.

After further experimenting, it seems that the strange ImageXORDrawingMode() function was actually of use:

img = imageNew(sourcepath);
   ImageSetAntialiasing(img);
   
   corner = ImageNew("",ImageGetWidth(img),ImageGetHeight(img),"argb");
   ImageSetDrawingColor(corner,red);
   ImageSetAntialiasing(corner);
   ImageDrawRoundRect(corner, 0, 0, ImageGetWidth(img), ImageGetHeight(img), diameter, diameter, true);
   ImageNegative(corner);
   ImagePaste(img, corner, 0, 0);

   ImageXORDrawingMode(corner,"ffffff");
   ImageDrawRect(corner, 0, 0, ImageGetWidth(img), ImageGetHeight(img), true);

This method met all my requirements, but somewhere along the line, the anti-aliasing was lost. For larger corners the results were ok, but a smaller radius could get very blocky.

Final Function

The final attempt took a very different approach. I realised that the ImageDrawRoundRect() function could also be used without being filled. You could also vary the width of the line.

The basic idea was to draw a rounded rectangle on top of the target image. The rectangle would be larger than the image, so that only the curved corners would overlap. Careful calulation of the rectangle's dimensions and thickness of line were required. The method worked well though after some adjustments. The final working function is shown below:

<cffunction name="roundImageCorners" returntype="any" access="public" output="false" hint="I take an image, object and return it with the corners rounder off">
      <cfargument name="image" type="any" required="true" hint="The image to be modified" />
      <cfargument name="radius" type="numeric" required="false" default="50" hint="Radius width of the corners" />
      <cfargument name="bgcolor" type="string" required="false" default="ffffff" hint="Colour of the rounder corners" />
      
      <cfscript>
         var t = "";
         var pen = structNew();
         var root2 = sqr(2);
         
         ImageSetAntialiasing(arguments.image);
         t = arguments.radius * (root2 - 1);
         ImageSetDrawingColor(arguments.image,arguments.bgcolor);
         pen.width = t;
         ImageSetDrawingStroke(arguments.image,pen);
         ImageDrawRoundRect(arguments.image, 0-(t/2), 0-(t/2), ImageGetWidth(arguments.image)+t, ImageGetHeight(arguments.image)+t, arguments.radius+(t/2), arguments.radius+(t/2), false);
         
         return arguments.image;
      </cfscript>
   </cffunction>

Comments
Azadi Saryev's Gravatar while your requirement may have called for uploaded images to be permanently altered to have rounded corners, in general this is more of an image display property, and thus can always be easily manipulated with the wonderful jQuery and its amazing plug-ins like CurvyCorners and similar others. The original images will thus be preserved (just in case that a futre re-design of the site will need standard straight-corner images... or will use some css overlay techniques to add different corners or other overlays), but on-screen the images will be rendered with any corners you want...

just a $0.02 thought...
# Posted By Azadi Saryev | 20/10/08 11:46
gareth's Gravatar Thanks Azadi - I'll take a look at them.
# Posted By gareth | 20/10/08 14:45
Sam Nicholson's Gravatar It would be easier to use jQuery i'm sure but doing it using CF8...good job :)
# Posted By Sam Nicholson | 20/10/08 21:30
Charles's Gravatar I've tried using this and I'm getting the following error:

java.lang.Double cannot be cast to java.lang.String

Can you create an example that shows the function being called to read an image off the server and round it, then display it?

Thanks.
# Posted By Charles | 24/11/08 16:43
gareth's Gravatar Hi Sam. Are you passing in a coldfusion image object?

I've adapted the following example from livedocs:
http://livedocs.adobe.com/coldfusion/8/htmldocs/fu...

I haven't tested this, so let me know how you get on.

<cfscript>
myImage=ImageRead("http://www.google.com/images/logo.gif";);

myImage = roundImageCorners(myImage);

ImageWrite(myImage,"google-logo.gif");
</cfscript>
<p>This image has been downloaded by ColdFusion:</p>
<img src="google-logo.gif">
<p>This is the original image:</p>
<img src="http://www.google.com/images/logo.gif";/>
# Posted By gareth | 24/11/08 17:25
Charles's Gravatar Thanks - but that code gives the Java Lang Double error as well :-(

<cffunction name="roundImageCorners" returntype="any" access="public" output="false" hint="I take an image, object and return it with the corners rounder off">

<cfargument name="image" type="any" required="true" hint="The image to be modified" />

<cfargument name="radius" type="numeric" required="false" default="50" hint="Radius width of the corners" />

<cfargument name="bgcolor" type="string" required="false" default="ffffff" hint="Colour of the rounder corners" />

<cfscript>
var t = "";
var pen = structNew();
var root2 = sqr(2);

ImageSetAntialiasing(arguments.image);
t = arguments.radius * (root2 - 1);
ImageSetDrawingColor(arguments.image,arguments.bgcolor);
pen.width = t;
ImageSetDrawingStroke(arguments.image,pen);
ImageDrawRoundRect(arguments.image, 0-(t/2), 0-(t/2), ImageGetWidth(arguments.image)+t, ImageGetHeight(arguments.image)+t, arguments.radius+(t/2), arguments.radius+(t/2), false);

return arguments.image;
</cfscript>
   
</cffunction>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitiona...;
<html xmlns="http://www.w3.org/1999/xhtml">;
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Untitled Document</title>
</head>
<body>

<cfscript>
myImage = ImageRead("test.gif");
myImage = roundImageCorners(myImage);
ImageWrite(myImage,"google-logo.gif");
</cfscript>
<p>This image has been downloaded by ColdFusion:</p>
<img src="google-logo.jpg">
<p>This is the original image:</p>
<img src="http://www.google.com/images/logo.gif">;
</body>
</html>
# Posted By Charles | 24/11/08 19:00
sethron's Gravatar Gareth,
Thanks for the function. I was able to send a image to the ImageRead function, and have it written to what
looks like the CFFileServlet directory using the CF8 writeToBrowser action with cfimage.

<code>
What?<br>
<cfscript>
   myImage = ImageRead("http://sethron.net/images/Dreamjob.jpg";);
   myImage = roundImageCorners(myImage);
</cfscript>

<cfimage action="writeToBrowser" source="#myImage#" >
This.<br>
</code>

That in turn created somthing similar to:
<code>
<img src="/CFFileServlet/_cf_image/_cfimg-1234561234561234567.PNG" alt="" />
</code>
# Posted By sethron | 12/11/09 22:38
DonQ's Gravatar cfSearching did a great writeup of a rounded corner mask using java objects, which also had anti aliasing, and since the end result is a png the cropped edges can be alpha'd

http://cfsearching.blogspot.com/2007/12/creating-p...
# Posted By DonQ | 31/03/11 16:24
Adobe Certified Professional - Macromedia ColdFusion MX 7 Advanced Developer Powered By ColdFusion 8 aggregated by mxna aggregated by coldfusionBloggers