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 + (2offset)> <cfset newheight = arguments.image.height + (2offset)>

<!--- 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>

Archived Comments

Comment 1 by Michael McConnell posted on 10/11/2007 at 1:33 AM

Great captions....And cool example.

Comment 2 by Dave Dugdale posted on 10/11/2007 at 2:11 AM

I love it, keep the examples coming - that is why I love your blog.

Comment 3 by Sana posted on 10/11/2007 at 2:14 AM

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

Comment 4 by Raymond Camden posted on 10/11/2007 at 2:23 AM

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.

Comment 5 by James Edmunds posted on 10/11/2007 at 3:41 AM

VERY interesting. I am curious about how processor-intensive this is. Any thoughts?

Comment 6 by Raymond Camden posted on 10/11/2007 at 4:50 AM

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.

Comment 7 by Jon Hartmann posted on 10/11/2007 at 5:22 AM

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/...

Comment 8 by Jeff Self posted on 10/11/2007 at 4:59 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.

Comment 9 by Raymond Camden posted on 10/11/2007 at 5:07 PM

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?)

Comment 10 by Connie DeCinko posted on 4/27/2009 at 11:52 PM

Ray,

Did you resolve the issue with this error?
Overlay operation requires the two sources to match in number of bands and data type.

Comment 11 by Raymond Camden posted on 4/28/2009 at 1:04 AM

Sorry, nope.

Comment 12 by Gavy posted on 5/24/2009 at 10:33 AM

Hi ray! You are using this:

<cfset imagePaste(shadow,arguments.image,0,0)>

to paste the image on to the browser. What if we need to store image in the folder. do we need to use the filewrite function even if the cfimage latest patch is applied.

using of getimagebytes (undocumented feature) and the filewrite function to make it work.

Comment 13 by Raymond Camden posted on 5/24/2009 at 4:54 PM

I'm not writing it to the browser. I'm writing it to the image object. Once you are done calling the UDF you can save it, write it to the browser, whatever.

Comment 14 by Dani posted on 10/17/2009 at 9:32 AM

Hello Ray, how are you?
Ray, is there any way to get a transparent background instead of a white one?

Thanks!!

Comment 15 by Raymond Camden posted on 10/17/2009 at 5:33 PM

Have you tried imageSetDrawingTransparancy?

Comment 16 by Dani posted on 10/17/2009 at 6:40 PM

Hi Ray, I've tried, but it didn't work (I'm not sure I'm using it right though).
What I do set the transparency to the color, not to the white background. I guess I'm a little bit confused.

Thanks Ray.

Comment 17 by Dani posted on 10/17/2009 at 7:00 PM

Let me put it more clear: When I set <cfset ImagesetDrawingTransparency(shadow,50)>, it creates the transparency to the shadow itself, not to the white background. The site I'm working with brings an image from the DB into the home page, which has a background color.

Dani

Comment 18 by Raymond Camden posted on 10/18/2009 at 6:39 PM

But you do want the shadow to be transparent, right? Or do you mean the shadow color is transparent?

Comment 19 by Dani posted on 10/18/2009 at 7:24 PM

Hello Ray. Basically all the samples I see for a drop shadow on the fly are on a white background. The background of the site I'm working on is like yours in the blog.
I fact, looking at your blog, that is exactly what I want. Each comment looks like is above the background, it doesn't look flat. If I'm not wrong, I see a drop shadow at the lower right corner. The problem is that I can't create a graphic because each image has a different size (are paintings).
This is where I would like to apply it:

http://www.thiezmultimedia....

The drop shadow would go instead of the frame around the painting.

Thanks Ray for your time.

Comment 20 by Raymond Camden posted on 10/19/2009 at 7:46 AM

So notice the last argument is background color. Did you try setting it instead of using the default value?

Comment 21 by Ali posted on 10/19/2009 at 7:47 PM

Can you have the shadow 'drop' in a different direction?
I have a design that requires a couple of images to have the shadow top-right
Thanks

Comment 22 by Raymond Camden posted on 10/19/2009 at 7:52 PM

Ali, it should be possible if you modify the UDF. You can see how it puts it to the right and lower.

Comment 23 by Ali posted on 10/19/2009 at 8:16 PM

Thanks Ray. Have got it working manually, now I'll see if I can get it with an additional argument(s)

Comment 24 by Jim Gibson posted on 11/27/2009 at 9:42 PM

Hi Ray. Since the drop shadow has the same origin as the original, your example probably does not apply to my situation: I want to overlay image2 on image1 at a specific xy offset from image1's origin. How can I accomplish that? Is there a way to resize image2's canvas and offset the original pixels to the canvas' lower right corner?

Comment 25 by Jim Gibson posted on 11/27/2009 at 10:22 PM

Wow, I feel really stupid. The last cfset in your example UDF has x and y offset parameters for a paste of one image over the other. That's what I needed.

Comment 26 by Amer Ghalayini posted on 12/7/2009 at 5:41 PM

The images are hilarious with comment

Comment 27 by Daniel Gaspar posted on 12/11/2009 at 9:42 PM

Hey Jeff,
The only way I could get this to work when using the imageNew() function is by saving the new image to a temp directory then loading it again.

<cfset MyTempImage = imageNew('','400','400','rgb','FFFFFF') />
<cfset ImageWrite(MyTempImage,GetTempDirectory()&'MyTempImage.jpg','.99') />
<cfset MyTempImage = ImageRead(GetTempDirectory()&'MyTempImage.jpg') />

This is with CF 8. Kind of a hack and feels like yet another CF 'gotcha'. (like isDefined("session.myVar")!).
Anyone else find a better way around this?

-Dan

Comment 28 by Daniel Gaspar posted on 12/15/2009 at 3:15 AM

...of course imagePaste DOES work with imageNew, so that is probably the best way to avoid the whole issue.

Comment 29 by Vlad posted on 2/6/2011 at 9:40 PM

I love the images! I love Coldfusion!

Comment 30 by Misty posted on 6/18/2012 at 12:35 PM

Hi ray, What if we need to add the opacity to it, how that could be added

Comment 31 by Raymond Camden posted on 6/19/2012 at 5:50 AM

I believe you want to use imageSetDrawingOpacity.

Comment 32 by Misty posted on 9/9/2013 at 10:38 AM

Hi ray,

I am using the ImageUtils.CFC library. I can create a reflection and drop shadow separately. However, now I have a condition where I want to merge the drop shadow and reflection together.

It should work like this. First it should create the reflection and then from that reflection it should create the drop shadow.

Here is the code for both of them. I am just having issues trying to combine them.

I am using the ImageUtils.CFC library. I can create a reflection and drop shadow separately. However, now I have a condition where I want to merge the drop shadow and reflection together.

It should work like this. First it should create the reflection and then from that reflection it should create the drop shadow.

Here is the code for both of them. I am just having issues trying to combine them.

reflection Code:

<cfimage action="read" source="#Path#\#sFile#" name="objImage">
<cfset objImage = objImageUtils.ReflectImage(objImage,"#form.side#",
"###form.bgcolor#","#form.offset#","#form.size#")>
<cfimage action="write" source="#objImage#"
destination="#Path#\#sFile#" overwrite="true">

Drop Shadow Color:

<cfimage action="read" source="#Path#\#sFile#" name="objImage">
<cfset objImage = objImageUtils.makeShadow(objImage,"#form.offset2#",
"#form.blur#","###form.shadowcolor#","###form.backgroundcol#")>
<cfimage action="write" source="#objImage#"
destination="#Path#\#sFile#" overwrite="true">
<cfoutput>
<img src="temp/#sFile#" />
</cfoutput>

Comment 33 by Raymond Camden posted on 9/11/2013 at 5:15 AM

So what happens? I see you do one, write it, then read it and do the other. Is it only showing one effect or something else? Are the sample images online so I can see?

Comment 34 by Misty posted on 9/11/2013 at 2:13 PM

yes, I have working Code, here is the link:

http://tinyurl.com/oqjy4mo

Upload an image and keep its size to 200, and choose bothoptions reflect and drop shadow too. you see the overlapping there in the image,

Comment 35 by Raymond Camden posted on 9/11/2013 at 2:43 PM

I tried running your app but the result was so small I couldn't see. I'd recommend you make a new CFM with just the code that demonstrates the bug and run that and then show me the result.

Comment 36 by Misty posted on 9/11/2013 at 3:45 PM

Hi ray, here is the code i am doing to join the Both:

<cfif (isDefined('form.reflect') and form.reflect IS 1) AND (isDefined('form.dropshadow') and form.dropshadow IS 1)>
<!--- Combine Both --->
<cfimage action="read" source="#Path#\#sFile#" name="objImage">
<cfset objImage = objImageUtils.ReflectImage(objImage,"#form.side#","###form.bgcolor#","#form.offset#","#form.size#")>
<cfset objImage2 = objImageUtils.makeShadow(objImage,"#form.offset2#","#form.blur#","###form.shadowcolor#","###form.backgroundcol#")>
<cfset i = Imagenew("",objImage.width,objImage.height,'rgb','###form.shadowcolor#')>
<cfset Imagepaste(objImage,i,10,10)>
<cfimage action="write" source="#objImage#" destination="#Path#\#sFile#" overwrite="true">

Comment 37 by Raymond Camden posted on 9/11/2013 at 4:04 PM

Try writing the image after reflectImage, save it as like 'afterimage.jpg' or somesuch. See if it at least step 1 is ok.