ColdFusion 8 introduces the CFImage tag and dozens of image manipulation functions. We have already looked at reading and writing image files and we have covered the basic yet powerful image manipulation possible through CFImage tag actions alone. Now we know enough to be able to really start exploring the vast feature set of ColdFusion 8 image manipulation. When it comes to something like this your imagination is really the only limit, so it makes it a bit more difficult to come up with simple ways to learn this stuff. I find that task-based learning works well and will let us accomplish real-world goals while touching on many aspects of image manipulation.
We'll also cover adding watermarks to photos. There are a number of ways to do this like building the watermark from scratch or using an existing watermark image. To start with, I'm going to demonstrate using an existing watermark image. Hands down, this is going to give you the best results. ColdFusion 8 provides amazing image manipulation, but it's not a replacement for an image/photo editing application such as Adobe Fireworks (nor was that ever the intention).
To create my watermark image, I'm going to open up my sweet-ass Fireworks application and create a transparent canvas. The transparent canvas is important because we don't want the overlaid image to be too obvious. Then I'm going to type "Kinky Solutions" and give it a nice orange glow affect:
Notice that the background of this canvas (in Adobe Fireworks) has a checkered white-and-gray pattern. This signifies that the background is transparent. Then I export this image as a PNG32. This will compress the image while maintaining transparency.
Now, let's take a look at the image we're working with and the watermark we're going to apply:
<!--- Read in the original image. --->
<cfset objImage = ImageRead( "./blue_eyes.jpg" ) />
<!--- Write it to the browser. --->
<cfimage
action="writetobrowser"
source="#objImage#"
/>
<!--- Read in the Kinky Solutions watermark. --->
<cfset objWatermark = ImageNew(
"./kinky_solutions_watermark_png32.png"
) />
<!--- Write it to the browser. --->
<cfimage
action="writetobrowser"
source="#objWatermark#"
/>
Running this code, we get the following output:
At this point the watermark is on a white background so you can't tell that it's transparent, but as you'll see, the PNG image format keeps the same transparency that we had in our Fireworks application. OK, so now what we're gonna do is read in both images and then paste the watermark onto the image of the girl with the blue eyes (Note: I'm adding a watermark for demonstration purposes, this photo isn't my property or the property of Kinky Solutions).
<!--- Read in the original image. --->
<cfset objImage = ImageRead( "./blue_eyes.jpg" ) />
<!--- Read in the Kinky Solutions watermark. --->
<cfset objWatermark = ImageNew(
"./kinky_solutions_watermark_png32.png"
) />
<!---
Turn on anti-aliasing on the existing image for the pasting to render nicely.
--->
<cfset ImageSetAntialiasing(
objImage,
"on"
) />
<!---
When we paste the watermark onto the photo, we don't want it to be fully visible. So let's set the drawing transparency to 50% before we paste.
--->
<cfset ImageSetDrawingTransparency(
objImage,
50
) />
<!---
Paste the watermark on to the image. We're going to paste this into the bottom right corner.
--->
<cfset ImagePaste(
objImage,
objWatermark,
(objImage.GetWidth() - objWatermark.GetWidth()),
(objImage.GetHeight() - objWatermark.GetHeight())
) />
<!--- Write it to the browser. --->
<cfimage
action="writetobrowser"
source="#objImage#"
/>
This code displays some cool functions. After we read in the images, the first thing we do is turn on anti-aliasing for future draw actions to the primary image. Anti-aliasing gives things a slightly fuzzy look that can be more pleasing to the eye and help manipulations to appear more natural. ColdFusion 8's documentation recommends turning this on for Paste actions. I'd say that's especially true in this case where our watermark has a fuzzy edge because of the Glow affect we applied.
Whether you're setting the drawing anti-aliasing, the background color, the drawing color, or the line stroke properties, understand that you're setting these properties for a specific image, not all images. Understand too that these properties will hold true for all image manipulation actions made to that specific image unless the properties are explicitly changed.
Before we paste the watermark onto the original image, we set the drawing transparency to be 50%. This doesn't make the current image transparent; it makes all future draw actions 50% transparent when they're applied to the current image. The idea here is that not only are we using a watermark with a transparent background when we paste it into the current image, we're going to adjust its alpha channel so that some of the original image shows up through the watermark.
The paste action itself is rather simple. It takes the source image, the target image, and the x and y coordinates of where to paste the target image. The target image (second argument) is always pasted onto the source image (first argument). The x and y coordinates are just like a web page - they start at the top left (0,0) and increase going down and to the right.
When I paste in the watermark, I'm using both the dimensions of the source and target images to calculate the x and y coordinates of the paste. Notice that I'm using .GetWidth() and .GetHeight() actions. These are the undocumented underlying Java methods available to the ColdFusion image object. If you're not comfortable, you can replace these with the more verbose ImageGetWidth() and ImageGetHeight() ColdFusion 8 methods. Running the code above, we get a really nice Kinky Solutions watermark affect:
Pretty slick. There's some graininess around the watermark, but in all fairness the glow effect plus the transparency plus the JPG optimization is just a bad combination for small file sizes. If I had exported this image as a PNG rather than a JPG, the quality would have been higher. So much of image manipulation magic is finding the right balance between quality and image size. You want things to look sweet, but you don't want a 100KB image that has no compression.
So what happens if you don't have an application like Adobe Fireworks to make your watermark image? All is not lost. You can still create a watermark image manually and then paste it in using a similar technique. For this next demo, we're going to create a second image using ColdFusion 8 that has a gray box with the text, Kinky Solutions, centered on it. Then just as we did in the previous example, we're going to paste it over the original image:
<!--- Read in the original image. --->
<cfset objImage = ImageRead( "./blue_eyes.jpg" ) />
<!---
Create a new image for the watermark with the given dimensions and give it a very light gray canvas color.
--->
<cfset objWatermark = ImageNew(
"",
100,
20,
"rgb",
"##F0F0F0"
) />
<!---
Set the drawing color. This will be the color for all image manipulations going forward.
--->
<cfset ImageSetDrawingColor(
objWatermark,
"##666666"
) />
<!--- Draw the rectangle. --->
<cfset ImageDrawRect(
objWatermark,
0,
0,
(objWatermark.GetWidth() - 1),
(objWatermark.GetHeight() - 1)
) />
<!---
Set text drawing anti-aliasing. This will give the text a smoother rendered look (rather than the jagged text you'd see on a Web page).
--->
<cfset ImageSetAntialiasing(
objWatermark,
"on"
) />
<!--- Create text attributes. --->
<cfset objAttributes = {
Font = "Verdana",
Size = "10",
Style = "Italic"
} />
<!--- Draw the watermark text onto our watermark image. --->
<cfset ImageDrawText(
objWatermark,
"Kinky Solutions",
11,
14,
objAttributes
) />
<!---
When we paste the watermark onto the photo, we don't want it to be fully visible. So let's set the drawing transparency to 50% before we paste.
--->
<cfset ImageSetDrawingTransparency(
objImage,
50
) />
<!---
Paste the manually created watermark image onto the photo. This time, we don't need to turn on any anti-aliasing since the watermark has solid borders. The anti-alisasing only helps us when the pasted image has an anti-aliased perimeter.
--->
<cfset ImagePaste(
objImage,
objWatermark,
(objImage.GetWidth() - objWatermark.GetWidth() - 3),
(objImage.GetHeight() - objWatermark.GetHeight() - 3)
) />
<!--- Write the image with the new watermark to the browser. --->
<cfimage
action="writetobrowser"
source="#objImage#"
/>
Now instead of reading in a watermark image, we're creating an entirely new image canvas using ImageNew(), which takes the target image (left blank since we're creating it from scratch), the width and height, the image type, and the canvas color. For this image we use an RGB color set. This method can also take an ARGB color set, but the documentation isn't very clear on what that is. I assume that the "A" stands for Alpha, but I couldn't figure out how to make a transparent canvas (we'll discuss this later on).
Once our canvas is ready, we set our drawing color and then draw our rectangle, which is really just a border. I'm using the ImageDrawRect() method rather than the ImageAddBorder() method since adding a border actually changes the size of the canvas; drawing a rectangle, on the other hand, gives us the complete freedom to create a border of any dimension we want in the current canvas size. I'm leaving out the sixth ImageDrawRect() argument, which is a flag for filling the rectangle (with the same color as the border itself, the drawing color); we want the light gray of the canvas color to show through.
Next we draw the Kinky Solutions text to the watermark. Before this action, we turn on the anti-aliasing to give the text a more pleasing rendering. When we draw the text onto the canvas, we give it the x and y coordinate and the text properties. The properties affect the font and size, but not the text color. Text color is determined by the general drawing color set for the canvas (i.e., SetDrawingColor()). It is also important to understand how text is drawn. I'm not sure why this is, but when drawing the text, the x and y coordinates are for the bottom left point of the text, NOT the top left. We're drawing our text at (14, 11), which you can clearly see is the bottom left coordinate:
And again when we paste this watermark on the source canvas, we
want to paste with a 50% transparency. Notice this time though that we
didn't turn on anti-aliasing for the source canvas. Our watermark has
solid edges and a border. We don't really need this edge to be
anti-aliased.
Running the code above, we get:
Clearly this is not as nice as the first demo (at least in my opinion), but it does give you the ability to create a decent watermark if you don't have Fireworks. Technically we could also have just drawn the watermark rectangle and the text directly onto the source image without having to create a watermark canvas; but, by creating a second canvas, I could demonstrate more ColdFusion 8 functionality.
In our first demo we dealt with a PNG watermark that had a transparent canvas. In our second demo we dealt with a watermark that had a rectangular opaque canvas. What happens if we want to create a transparent canvas from scratch? There may be a way to create a transparent canvas inherent to ColdFusion 8, but I couldn't find it in the documentation or figure it out on my own. Transparent canvases, however, are really useful for things like watermarking so being able to create them is important. In this next demo, I'll show you how to create a transparent canvas using a neat little hack.
This transparent canvas hack relies on the fact that ColdFusion 8 can read in image data stored in a Base64 encoding. What we do is read in a new image using Base64 data that represents a 1x1 transparent GIF image. This will create an image that is 1x1 with a transparent canvas. Then we just need to resize the image to the dimensions of our canvas.
In this next demo we'll create a transparent watermark canvas from scratch using the hack above. Then we'll draw the text, Kinky Solutions, blur the canvas, and paste it over the source image:
<!--- Read in the original image. --->
<cfset objImage = ImageRead( "./blue_eyes.jpg" ) />
<!---
Read in the spacer image. This is the Base64 encoding of a 1x1 transparent spacer GIF. This will let you create a transparent canvas without having to have the spacer image on hand. Here, I'm putting in the Base64 headers for demonstration (but they're not needed).
--->
<cfset objWatermark = ImageReadBase64(
"data:image/gif;base64," &
"R0lGODlhAQABAIAAAP///////yH5BAEHAAEALAAAAAABAAEAAAICTAEAOw=="
) />
<!---
Resize the transparent image to be the size of the target canvas. This will result in a larger transparent canvas.
--->
<cfset ImageResize(
objWatermark,
190,
30
) />
<!---
Set text drawing anti-aliasing to be on. We're going to be writing text and then blurring it, so we want to let it be a little fuzzy.
--->
<cfset ImageSetAntialiasing(
objWatermark,
"on"
) />
<!--- Set the drawing color to be white. --->
<cfset ImageSetDrawingColor(
objWatermark,
"##FFFFFF"
) />
<!--- Create text attributes. --->
<cfset objAttributes = {
Font = "Arial Black",
Size = "20"
} />
<!--- Draw the water mark text. --->
<cfset ImageDrawText(
objWatermark,
"Kinky Solutions",
11,
20,
objAttributes
) />
<!--- Blur the watermark image. --->
<cfset ImageBlur(
objWatermark,
6
) />
<!---
Set text drawing anti-aliasing to be on for the target image. Since the image we are pasting in, we want a slight blurring.
--->
<cfset ImageSetAntialiasing(
objImage,
"on"
) />
<!---
Paste the manually created watermark image onto the photo. This time, we don't need to turn on any anti aliasing since the watermark has solid borders. The anti-alisasing only helps us when the pasted image has anti-aliased perimeter.
--->
<cfset ImagePaste(
objImage,
objWatermark,
(objImage.GetWidth() - objWatermark.GetWidth()),
(objImage.GetHeight() - objWatermark.GetHeight())
) />
<!--- Write the image with the new watermark to the browser. --->
<cfimage
action="writetobrowser"
source="#objImage#"
/>
Notice that the image we are reading in is the Base64 data:
R0lGODlhAQABAIAAAP///////yH5BAEHAAEALAAAAAABAAEAAAICTAEAOw==
This is the encoded 1x1 transparent GIF. We're also including the headers in the Base64 data, but this isn't required (it works if you take it out); I left it in just for demonstration purposes. Because we're using Base64 data rather than an actual GIF image, it lets us create transparent canvases without having a physical image on hand.
Running the above code, we get:
Clearly this is a hack. I'm
sure that ColdFusion 8 has a way to do this that's more elegant, but
for now, until I can figure it out, this is a rather simple workaround.
I hope, however, that you're beginning to see how amazing the
ColdFusion 8 image manipulation functionality is.
This article was reprinted with permision from Ben Nadel's blog:
www.bennadel.com/blog/775-Learning-ColdFusion-8-CFImage-Part-III-Watermarks-And-Transparency.htm