Reminder: Duplicate() Does Not Work With ColdFusion Images

Posted September 26, 2008 at 9:41 AM by Ben Nadel

Tags: ColdFusion

Someone contacted me this morning needing help debugging an image thumbnailing problem. Part of the issue that they were running into was that the Duplicate() function did not seem to work with ColdFusion image objects. This can be confusing since the documentation seems to indicate that it will work:

Task: Create a ColdFusion image from another ColdFusion image.

Functions and Tags: ImageCopy function with the ImageWrite function or the Duplicate function, or by passing the image to the ImageNew function or the cfimage tag.

This language is quite awkward and a little ambiguous; but, it does seem to state that there are many ways to duplicate a ColdFusion image object. Unfortunately, most of these don't actually work - at least not in the way that I have interpreted them.

Let's run though a few demos. First, we are going to read in a picture of the beautiful, sexy, two-time Olympic Gold medalist, and "Dancing With The Stars" celebrity, Misty May. Then, we are going to duplicate Misty May's image using ColdFusion's Duplicate() method. Then we are going to add a caption to both images and write them to the browser:

  • <!--- Read in the image of the very cute Misty May. --->
  • <cfimage
  • action="read"
  • source="./misty_may.jpg"
  • name="objMistyMay"
  • />
  •  
  • <!---
  • Now that we have the clean Misty May picture in memory
  • as a ColdFusion image objects, let's duplicate it.
  • --->
  • <cfset objMistyMayDuplicate = Duplicate( objMistyMay ) />
  •  
  •  
  • <!--- Add a caption to the ORIGINAL image. --->
  • <cfset ImageAddCaption(
  • objMistyMay,
  • "Dang, she is wicked cute!"
  • ) />
  •  
  • <!--- Add a caption to the DUPLICATE image. --->
  • <cfset ImageAddCaption(
  • objMistyMayDuplicate,
  • "Even her duplicate is hot!"
  • ) />
  •  
  •  
  • <!--- Write ORIGINAL image to the browser. --->
  • <cfimage
  • action="writetobrowser"
  • source="#objMistyMay#"
  • format="jpg"
  • />
  •  
  • <!--- Write DUPLICATE image to the browser. --->
  • <cfimage
  • action="writetobrowser"
  • source="#objMistyMayDuplicate#"
  • format="jpg"
  • />

When we run this code, we get the following output:

 
 
 
 
 
 
Sexy Misty May VollyBall Picture Duplicated Using ColdFusion Duplicate() Method. 
 
 
 

Notice that the second caption written to the duplicate image was actually written to both images. This is because the Duplicate() method only creates a copy of the image reference, not a copy of the underlying binary data. It seems as if Duplicate() is no different than simply assigning the image to two variables.

So, Duplicate() clearly doesn't work. But what about the other techniques mentioned in the documentation above? Let's try "passing the image to the ImageNew function":

  • <!---
  • Now that we have the clean Misty May picture in memory
  • as a ColdFusion image objects, let's duplicate it using
  • the ImageNew() function.
  • --->
  • <cfset objMistyMayDuplicate = ImageNew( objMistyMay ) />

This gives us the same output as above.

OK, but what about "by passing the image to the ImageNew function or the cfimage tag":

  • <!---
  • Now that we have the clean Misty May picture in memory
  • as a ColdFusion image objects, let's duplicate it using
  • the CFImage tag.
  • --->
  • <cfimage
  • action="read"
  • source="#objMistyMay#"
  • name="objMistyMayDuplicate"
  • />

Unfortunately, this also gives us the same output as above. It looks like so far 3 out of the 4 methods in the documentation don't actually do anything.

It turns out, the ImageCopy() method is the only one of the documented techniques that works. To demonstrate this, let's create a ColdFusion user defined function that wraps around the ImageCopy() method:

  • <cffunction
  • name="ImageDuplicate"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I duplicate a ColdFusion image.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Image"
  • type="any"
  • required="true"
  • hint="I am the image being duplicated."
  • />
  •  
  • <!---
  • Return the duplicated image by copying the entire
  • canvas to a new ColdFusion image object.
  • --->
  • <cfreturn ImageCopy(
  • ARGUMENTS.Image,
  • 0,
  • 0,
  • ImageGetWidth( ARGUMENTS.Image ),
  • ImageGetHeight( ARGUMENTS.Image )
  • ) />
  • </cffunction>

With this method in place, we can now replace our duplication line with the following:

  • <!---
  • Now that we have the clean Misty May picture in memory
  • as a ColdFusion image objects, let's duplicate it using
  • our user defined function that leverages ImageCopy().
  • --->
  • <cfset objMistyMayDuplicate = ImageDuplicate( objMistyMay ) />

With this code in place, when we re-run the above demo, we get the following output:

 
 
 
 
 
 
Sexy Misty May VolleyBall Image Duplicated Using ColdFusion's ImageCopy() Function. 
 
 
 

As you can see, each of the above image now has the appropriate caption. This is because the second caption is written to the duplicate image which, thanks to ImageCopy() was not a reference to the original image.

There's nothing wrong with doing something like reading a file in to memory more than once if you need to create a duplicate; I've done that a bunch of times. But, when it comes to creating a duplicate ColdFusion image from an existing ColdFusion image, ImageCopy() is the only method that I have been able to use successfully.

If you are interested in the ImageAddCaption() method, I just added it for testing purposes:

  • <cffunction
  • name="ImageAddCaption"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I add a caption to the bottom of the image.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Image"
  • type="any"
  • required="true"
  • hint="I am the image we are updating."
  • />
  •  
  • <cfargument
  • name="Caption"
  • type="string"
  • required="true"
  • hint="I am the caption being added to the image."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!---
  • Set the local drawing color for our caption
  • text background.
  • --->
  • <cfset ImageSetDrawingColor(
  • ARGUMENTS.Image,
  • "##262626"
  • ) />
  •  
  • <!---
  • Draw background rectangle (over which our caption
  • text will be drawn).
  • --->
  • <cfset ImageDrawRect(
  • ARGUMENTS.Image,
  • 0,
  • (ImageGetHeight( ARGUMENTS.Image ) - 23),
  • ImageGetWidth( ARGUMENTS.Image ),
  • (ImageGetHeight( ARGUMENTS.Image ) - 23),
  • true
  • ) />
  •  
  • <!--- Set the drawing color for the caption text. --->
  • <cfset ImageSetDrawingColor(
  • ARGUMENTS.Image,
  • "##FFFFFF"
  • ) />
  •  
  • <!--- Set the font properties. --->
  • <cfset LOCAL.FontProperties = {
  • Style = "bold",
  • Font = "verdana",
  • Size = "12"
  • } />
  •  
  • <!--- Write the caption text to image. --->
  • <cfset ImageDrawText(
  • ARGUMENTS.Image,
  • ARGUMENTS.Caption,
  • 10,
  • (ImageGetHeight( ARGUMENTS.Image ) - 7),
  • LOCAL.FontProperties
  • ) />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>



Reader Comments

Sep 26, 2008 at 11:41 AM // reply »
132 Comments

Hope you reported this. Definitely bugs. :/


Sep 26, 2008 at 11:49 AM // reply »
11,238 Comments

@Elliott,

I reported this back in the CF8 public beta ;) It was even addressed some where, but I can't find the post. One of the Adobe developers said that they did this on purpose because they didn't want the memory duplication to get out of hand. Since images can be very large, they decided a copy-by-reference made more sense.

I think the documentation is just wrong.


Sep 26, 2008 at 12:35 PM // reply »
17 Comments

Steve here: taking fault for having the thumbnail set problem :P

Actually Ben has exposed something here that wasn't really my issue, Duplicate() seems to be working for ME LOL,

My REAL issue was: if I read a file in with
<cfimage action="read" name="myImage">

then delete or rename the original file,

whenever I call a resize method, passing in source="#myImage#"
it complains the original file is no longer on the disk.

I can cdump toString(myImage) and see a nice long base64 string,
so it appears it IS in memory...

I just want to know if someone has some insight on why passing an object in as source doesn't work.

So don't jump on me about this,
also, I suspect that duplicate isn't the whole problem, but possibly setting the caption may suffer from the same bug that resize functions do, They don't work on the object, they work on data from the source file?

My reasoning behind this is,
if I load an image into an object,
and I resize myImage 50x50,
then I resize myImage 300x300,
I get a large blurry image (from the loss of the 1st resize)

if I use <cfset thm1 = Duplicate(myImage)>
and then I resize thm1 to 50x50,
and then I resize myimage to 300x300
I get 2 perfect resizes (the larger one is not blurry)
Pointing to the fact that duplicate is working in this case?

NOTE: I still can't rename or remove the original file or the resizes will fail.


Sep 26, 2008 at 1:14 PM // reply »
17 Comments

How about this. It seems to show duplicate DOES duplicate the image data, and I can change thm without changing the original,
(I can also change the original without changing the thm)

[code]
<cfimage action="read" source="#photo#" name="objImage">

<cfset thm = Duplicate(objImage)>

<cfoutput>
objImage<br>
<textarea cols="80" rows="20">
#ToBase64(objImage)#
</textarea>
thm<br>
<textarea cols="80" rows="20">
#ToBase64(thm)#
</textarea>

<cfset ImageScaleToFit(thm,50,"")>
objImage after thm resize (still the same)<br>
<textarea cols="80" rows="20">
#ToBase64(objImage)#
</textarea>
thm after resize<br>
<textarea cols="80" rows="20">
#ToBase64(thm)#
</textarea>

</cfoutput>

[/code]

I think that when you use duplicate it still copies the objImage.Source property, and other properties that may remain the same, and soem functions are not really accepting an object as source="">


Sep 26, 2008 at 1:40 PM // reply »
11,238 Comments

@Steve,

I think something is definitely funny with the way things are being handled. I want to see if I can duplicate things on my end.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 19, 2013 at 2:31 PM
My Experience With AngularJS - The Super-heroic JavaScript MVW Framework
It's funny really just how well that image describes the way I would imagine most people that go with angular for some project is. I have had a similar roller-coaster ride with it as well, but not qu ... read »
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools