Since Ben Nadel was kind enough to help me out a few days ago on an unusual coding requirement and wrote a post about it as well, I thought I would write one myself and showcase an actual use-case scenario for it.
The real coding request that I had was to allow users to upload images through a CMS, assign some properties to them, and the CMS would create a gallery slider for the homepage. Each slide would show a small section of the previous one at the left hand side corner:
Even though ColdFusion comes with a plethora of native image functions, there isn’t anything that could achieve this (and please correct me if I am wrong). That’s where Ben’s example came really handy. Please read his post first to get an idea of how to “punch out” a section of a photo, and I will continue with the code to get the effect shown above for the slider.
There are two things we need to be very careful about in creating such effects:
- All our images need to be in PNG format.
We could definitely start off with anything other than that (like JPG), but we need to convert them first before we can work on them. This is so that we can use PNG transparencies, which are needed to create the diagonal slicing effects. - All images we work with need to have the same dimensions.
Since we are going to overlay images on top of each other, it really helps if everything just fits within the same size. We can verify this beforehand by having a crop tool, or just use ColdFusion’s ImageResize() and ImageCrop() functions.
These are the steps to get this effect rocking:
- Read the 2 images that will be joined. Let’s refer to them as the “left-hand” and “right-hand” images.
- Create the image that will be used to punch through the left-hand slider image.
- Punch through the left-hand slider image to create a transparency for the image part we don’t want.
- Overlay the punched out image on top of the right-hand image.
And here’s the complete code below. Inline comments describe it more detail and point to the 4 steps outlined above.
<!--- Create a border for the image to make them stand out. Add a page background so that we can more easily see transparencies. ---> <style type="text/css"> body { background: #CCC ; } img { border: 3px solid #990000 ; } </style> <!--- STEP 1: Read the 2 images that will be joined. Resize them to 600px width to fit the rest of the images. Remember: it's easier to work with images of the same dimensions. ---> <cfset imgLeft = ImageRead( "left-hand-image.png" ) /> <cfset imgRight = ImageRead( "right-hand-image.png" ) /> <cfset ImageResize(imgLeft, 600, "") /> <cfset ImageResize(imgRight, 600, "") /> <!--- STEP 2: Create the 'punch' image. Here we create it on the fly using a transparent canvas, but we could also create it in any image editing tool (like Photoshop) and simply read it from the file system. We start with a clean transparent canvas. ---> <cfset imgPunch = ImageNew( "", 600, 400, "argb" ) /> <!--- Turn on anti-aliasing to make sliced corners smoother. ---> <cfset ImageSetAntialiasing(imgPunch, "on") /> <!--- Set the values for the x and y coordinates to create the connecting points of the lines segments. ---> <cfset x = ArrayNew(1)> <cfset y = ArrayNew(1)> <cfset x[1] = imgPunch.GetWidth() / 10 * 3 /> <cfset y[1] = "0" /> <cfset x[2] = imgPunch.GetWidth() /> <cfset y[2] = "0" > <cfset x[3] = imgPunch.GetWidth() /> <cfset y[3] = imgPunch.GetHeight() > <cfset x[4] = imgPunch.GetWidth() / 10 * 1 /> <cfset y[4] = imgPunch.GetHeight() /> <!--- Set the color of the area inside the points. ---> <cfset ImageSetDrawingColor(imgPunch, "gray") /> <!--- Draw lines on the image using the defined points. ---> <cfset ImageDrawLines(imgPunch, x, y, true, true) /> <cfimage action="writetobrowser" source="#imgPunch#" /> <!--- STEP 3: Use the punch image that was created to punch out a transparent hole through the left-hand image. We start with a clean transparent canvas. ---> <cfset imgCanvas = ImageNew( "", 600, 400, "argb" ) /> <!--- Overlay the left-hand image on top. ---> <cfset ImagePaste(imgCanvas, imgLeft, 0, 0) /> <!--- Punch a transparent hole through the image. ---> <cfset punchedImage = imagePunch( imgCanvas, imgPunch ) /> <!--- Write out the newly created image. ---> <cfimage action="writetobrowser" source="#punchedImage#" /> <!--- STEP 4: Finally, overlay the punched out left-hand image on top of the right hand image. ---> <cfset ImagePaste(imgRight, punchedImage, 0, 0) /> <!--- Write out the final image. ---> <cfimage action="writetobrowser" source="#imgRight#" /> <!--- Ben Nadel's function included here. ---> <cffunction name="imagePunch" access="public" returntype="any" output="false" hint="I punch the second image through the first image."> <!--- Define arguments. ---> <cfargument name="baseImage" type="any" required="true" hint="I am the ColdFssion Image that is being manipulated." /> <cfargument name="punchImage" type="any" required="true" hint="I am the ColdFusion image that is being PUNCHED through the base image." /> <cfargument name="punchX" type="numeric" required="false" default="0" hint="I am the X-coordinate of the top/left coordianate of the punch image." /> <cfargument name="punchY" type="numeric" required="false" default="0" hint="I am the Y-coordinate of the top/left coordianate of the punch image." /> <!--- In order to manipulate the base image in more complex ways, we're going to create a Graphics instance as a proxy to the underlying base image data. ---> <cfset local.graphics = imageGetBufferedImage( baseImage ) .createGraphics() /> <!--- Set the composite rules for the graphics engine to replace all pixels in the base that correspond to pixels in the Punch image with an alpha transparency. We are punching "OUT" any pixels in the base image that have overlapping pixels in the punch image. ---> <cfset local.graphics.setComposite( createObject( "java", "java.awt.AlphaComposite" ).DstOut ) /> <!--- Draw the punch image over the base image - let the composite setting render the punch. ---> <cfset local.graphics.drawImage( imageGetBufferedImage( punchImage ), javaCast( "int", punchX ), javaCast( "int", punchY ), javaCast( "null", "" ) ) /> <!--- Return the mutated base image (since the image is passed by- reference, there's no real need to return it - the original image has been mutated). ---> <cfreturn baseImage /> </cffunction>
Here’s another example of how you can use this method to create a nice joined image of two rugs:
Very cool! Glad this could help 😀