Simple ColdFusion 8 Drop Shadow Example

I'm definitely not the first person to do this - but I've been itching to do drop shadows ever since I started playing with ColdFusion 8's new image functionality. My UDF is rather simple. It takes an image and duplicates it. It fills the canvas with white - and than adds an offset black square the same size as the original image. It does a blur, and then pastes on the original image.

That by itself isn't too interesting, but what was interesting is why I had to duplicate the original image. When I first wrote the code, I simply used imageNew. However, whenever I tried to imageOverlay the original image onto the new one, I got:

Overlay operation requires the two sources to match in number of bands and data type.

Stumped - I dumped imageInfo on both. I wasn't sure what bands meant - but colormode_type on my original image was "ComponentColorModel" ,and the value in my new image made from scratch was "PackedColorModel". That made as much sense to me as arithmetic would make to Paris Hilton. So for the heck of it, I just tried imageNew using ARGB. I figured grayscale wouldn't work. Using ARGB didn't help at all.

So does anyone know how you would make an image from scratch that would work with a (as far as I know) average JPG?

The code is pasted at the very bottom. Let me show some examples of the output. First the original image.


Writing PHP is hard!

Now to make the drop shadow:

<cfset myimage=makeShadow("sadgirl.jpg",5,5)>
<cfimage action="writeToBrowser" source="#myimage#">


.Net makes Mommy and Daddy fight.

And finally an example with what I call the blood red shadow:

<cfset myimage=makeShadow("sadgirl.jpg",5,5, "90,0,0")>
<cfimage action="writeToBrowser" source="#myimage#">


Rails broke all my toys and Ruby killed my dog!

And finally - the UDF. Enjoy:

<cffunction name="makeShadow" returnType="any" output="false">
   <cfargument name="image" type="any" required="true">
   <cfargument name="offset" type="numeric" required="true">
   <cfargument name="blur" type="numeric" required="true">
   <cfargument name="shadowcol" type="string" required="false" default="145,145,145">
   <cfargument name="backgroundcol" type="string" required="false" default="white">
   
   <cfset var newwidth = "">
   <cfset var newheight = "">
   <cfset var shadow = "">
   
   <!--- if not image, assume path --->
   <cfif not isImage(arguments.image) and not isImageFile(arguments.image)>
      <cfthrow message="The value passed to makeShadow was not an image.">
   </cfif>
   
   <cfif isImageFile(arguments.image)>
      <cfset arguments.image = imageRead(arguments.image)>
   </cfif>

   <cfset newwidth = arguments.image.width + (2*offset)>
   <cfset newheight = arguments.image.height + (2*offset)>

   <!--- make a black image the same size as orig --->
   <cfset shadow = duplicate(arguments.image)>
   <cfset imageResize(shadow, newwidth, newheight)>
   <cfset imageSetDrawingColor(shadow,arguments.backgroundcol)>
   <cfset imageDrawRect(shadow, 0, 0, newwidth, newheight, true)>
   <cfset imageSetDrawingColor(shadow,arguments.shadowcol)>
   <cfset imageDrawRect(shadow, arguments.offset, arguments.offset, arguments.image.width, arguments.image.height, true)>
   
   <cfset imageBlur(shadow, arguments.blur)>
   
   <!--- copy orig --->
   <cfset imagePaste(shadow,arguments.image,0,0)>
   
   <cfreturn shadow>
</cffunction>

Comments

Great captions....And cool example.
# Posted By Michael McConnell | 10/10/07 4:33 PM
I love it, keep the examples coming - that is why I love your blog.
# Posted By Dave Dugdale | 10/10/07 5:11 PM
Hi Ray,

Could you send more than one images to browser at a time; like this,
<cfset myimage=makeShadow("sadgirl1.jpg",5,5, "90,0,0")>
<cfimage action="writeToBrowser" source="#myimage#">

<cfset myimage=makeShadow("sadgirl2.jpg",5,5, "90,0,0")>
<cfimage action="writeToBrowser" source="#myimage#">

.NET is far more powerfull than JAI, I would suggest have a look to APIs

System.Drawing.Imaging
System.Drawing

no doubt that CF is cool, but certainly not in every functionality.

Thanks
# Posted By Sana | 10/10/07 5:14 PM
Sana:

1) Yes, you can use N writeToBrowser actions in one request.

2) Heh, well, you have to remember that CF's image stuff is a layer on top of the lower level code (not sure if it is JAI, I assume it is). CF lets you use Dot Net as easily as Java. So I could use either of them. But the point is - 99% of what I need is covered in the functions CFML gives me. That's where the real power is. I mean - have you seen other CF Image demos? It truly is simple to work with images.
# Posted By Raymond Camden | 10/10/07 5:23 PM
VERY interesting. I am curious about how processor-intensive this is. Any thoughts?
# Posted By James Edmunds | 10/10/07 6:41 PM
James - it ran very fast for me - BUT - this is the kind of thing I'd do once. Ie, I wouldn't make drop shadows on the fly for every request. Ditto for thumbnails. You know - I should probably add a ScaleTo attribute that will let you shrink it at the same time.
# Posted By Raymond Camden | 10/10/07 7:50 PM
If you want to get down to the Java, you can create an object of type java.awt.image.BufferedImage, manipulate the color model and pass it back into the constructor for ColdFusion's image type, coldfusion.image.Image, and I think the coldfusion.image.Image class has a method that allows you to extract that image's BufferedImage class as well. I've got an example of how to make a transparent background up at my site: http://www.jonhartmann.com/programming/
# Posted By Jon Hartmann | 10/10/07 8:22 PM
@Sana

It could be argued that ImageMagick is more powerful than .Net's imaging stuff. ImageMagick can be used by Ruby, PHP, and even Java. And I'm guessing that it would be possible to use ImageMagick with ColdFusion as well.

And ColdFusion could possibly get much more powerful features with imaging in the next release. After all, who owns ColdFusion? And aren't they known for having some photo software?


I'm sure .Net is a good development platform, but until it runs on a Mac or Linux (yes I know about Mono), many of us aren't interested in it. We prefer development environments that run on more than a single platform.

Just my opinion.
# Posted By Jeff Self | 10/11/07 7:59 AM
Good point Jeff. But I'll still argue that it isn't just about who has the bigger API (sorry, couldn't resist). When I look at what CF's tools let me do - it covers the 99% of what a typical web app needs to do image wise and makes it easy. That to me is where CF shines. (Does that make sense?)
# Posted By Raymond Camden | 10/11/07 8:07 AM