Creating Transparent Gif's with ColdFusion 8 - Part 2
In Part 1 (or as I like to think of it "More than you ever wanted to know about GIF's") we learned how to extract the color components from a GIF's color model, and use the Raster to obtain the color index of a specific pixel. We can now use that information to construct a new color model, but this time with a transparent color.
<cfscript>Now that we have a model that supports transparency, we can use it to create our transparent GIF. This is done by creating a new BufferedImage., which is then passed into the ImageNew() function, to create a ColdFusion compatible image object. That is it. We now have a transparent GIF!
// cfSearching: colors[1] - red, colors[2] - green, colors[3] - blue
IndexColorModel = createObject("java", "java.awt.image.IndexColorModel");
newModel = IndexColorModel.init(javacast("int", 8),
javacast("int", colorSize),
colors[1],
colors[2],
colors[3],
javacast("int", transparentColorIndex)
);
</cfscript>
<cfscript>
BufferedImage = createObject("java", "java.awt.image.BufferedImage");
newBufferedImage = BufferedImage.init( newModel,
sourceImage.getRaster(),
sourceImage.isAlphaPremultiplied(),
javacast("null", "")
);
transparentImage = ImageNew( newBufferedImage );
</cfscript>
Caveats
Now the thing to remember about GIF's is that they only support fully opaque or fully transparent pixels. So GIF's with curved edges may appear jagged. You can create smoother edges by using anti-aliasing. However, this effect is achieved by blending the edges with the image background color, which results in a faint halo of color around the edges. So you must display the image on a similar background color to maintain the illusion of transparency. This is just the nature of GIF's. The PNG format on the other hand, is able to achieve smooth edges by using variable transparency. By using semi-transparent colors, it creates the illusion of smooth edges without the halo effect.Finally, the code
This function is rough, but you can test it out using one of the examples below. I have included examples of how to create a new transparent GIF and convert an existing one. Comments, corrections or suggestions are always welcome.Enjoy!
Use an existing GIF - Example 1
<!--- use the pixel at position x=0, y=0 for the transparent color --->Use an existing GIF - Example 2
<cfset sourceImage = ImageNew("https://www.google.com/accounts/reader/screenshot_en.gif")>
<cfset newImage = convertToTransparentGif(source=sourceImage, x=0, y=0)>
<div style="clear: both;">
Use an existing GIF - Example 1<br>
Notice the faint halo of color around the edges due to anti-aliasing<br><br>
<div style="background-color: #ffffcc; padding: 10;float: left;">
<b>Original (opaque)</b><br><br>
<img src="https://www.google.com/accounts/reader/screenshot_en.gif">
</div>
<div style="background-color: #ffffcc; padding: 10;float: right;">
<b>New (transparent, but with anti-aliasing)</b><br><br>
<cfimage action="writeToBrowser" source="#newImage#" format="gif">
</div>
<br><br>
</div>
<!--- use the pixel at position x=0, y=0 for the transparent color --->Create a New Transparent GIF - Example 1
<cfset sourceImage = ImageNew("http://www.sun.com/software/images/promotions/getjava.gif")>
<cfset newImage = convertToTransparentGif(source=sourceImage, x=0, y=0)>
<div style="clear: both;">
<div style="clear: both;">
Use an existing GIF - Example 2<br>
Notice the faint halo of color around the edges due to anti-aliasing<br><br>
</div>
<div style="background-color: #b0c4de; padding: 10;float: left;">
<b>Original (opaque)</b><br><br>
<img src="http://www.sun.com/software/images/promotions/getjava.gif">
</div>
<div style="background-color: #f5f5f5; padding: 10;float: left;">
<b>New (transparent, but with anti-aliasing)</b><br><br>
<cfimage action="writeToBrowser" source="#newImage#" format="gif">
</div>
</div>
<!--- cfSearching: Note the type is *ARGB* and anti-aliasing is ON --->Create a New Transparent GIF - Example 2
<cfset imageBackgroundColor = "##ffffff">
<cfset img = ImageNew("", 400, 200, "argb", imageBackgroundColor)>
<cfset ImageSetAntiAliasing(img, "ON")>
<cfset ImageSetDrawingColor(img, "##336600")>
<cfset ImageDrawRoundRect(img, 125, 26, 200, 99, 20, 20, "true")>
<cfset ImageSetDrawingColor(img,"##003366")>
<cfset fontAttr = StructNew()>
<cfset fontAttr.font = "Arial">
<cfset fontAttr.size = 32>
<cfset fontAttrr.style = "bold">
<cfset ImageDrawText(img, "Can you see ..", 65, 80, fontAttr)>
<cfset ImageSetDrawingColor(img, "##003366")>
<cfset ImageDrawRoundRect(img, 32, 90, 200, 99, 20,20, "true")>
<cfset ImageSetDrawingColor(img,"##336600")>
<cfset ImageDrawText(img, "right through me?", 92, 155, fontAttr)>
<cfset newImage = convertToTransparentGif(img, imageBackgroundColor)>
<cfset ImageWrite(newImage, ExpandPath("newGifExample1.gif"))>
<div style="clear: both;">
Create a new GIF - Example 1<br>
Notice the faint halo of color around the edges due to anti-aliasing<br><br>
</div>
<br>
<div style="background-color: #f5f5f5; width: 400; height: 200;">
<cfimage action="writeToBrowser" source="#newImage#" format="gif">
</div>
<!--- cfSearching: Note the type is *RGB* and anti-aliasing is OFF --->ConvertToTransparentGif Function
<cfset img = ImageNew("", 400, 200, "rgb")>
<cfset ImageSetAntiAliasing(img, "OFF")>
<cfset ImageSetDrawingColor(img, "##336600")>
<cfset ImageDrawRoundRect(img, 125, 26, 200, 99, 20, 20, "true")>
<cfset ImageSetDrawingColor(img,"##003366")>
<cfset fontAttr = StructNew()>
<cfset fontAttr.font = "Arial">
<cfset fontAttr.size = 32>
<cfset fontAttrr.style = "bold">
<cfset ImageDrawText(img, "Can you see ..", 65, 80, fontAttr)>
<cfset ImageSetDrawingColor(img, "##003366")>
<cfset ImageDrawRoundRect(img, 32, 90, 200, 99, 20, 20, "true")>
<cfset ImageSetDrawingColor(img,"##336600")>
<cfset ImageDrawText(img, "right through me?", 92, 155, fontAttr)>
<cfset newImage = convertToTransparentGif(source=img, x=0, y=0)>
<cfset ImageWrite(newImage, ExpandPath("newGifExample2.gif"))>
<div style="clear: both;">
Create a new GIF - Example 2<br>
Notice the edges are jagged because we did not use anti-aliasing<br><br>
</div>
<br>
<div style="background-color: #b0c4de; width: 400; height: 200;">
<cfimage action="writeToBrowser" source="#newImage#" format="gif">
</div>
Based on code from PaulFMendler
http://forum.java.sun.com/thread.jspa?forumID=20&threadID=425160
<cffunction name="convertToTransparentGif" returntype="any" access="public" output="true"
hint="Converts a ColdFusion image, or BufferedImage object, into a transparent GIF">
<cfargument name="source" type="any" required="true" hint="A ColdFusion image object or a BufferedImage">
<cfargument name="color" type="string" required="false" default="" hint="The transparent color in hexadecimal format">
<cfargument name="x" type="numeric" required="false" default="-1" hint="Use the pixel at position x,y as the transparent color">
<cfargument name="y" type="numeric" required="false" default="-1" hint="Use the pixel at position x,y as the transparent color">
<cfset Local = structNew()>
<!--- cfSearching: validate that a valid image object was supplied --->
<cfif NOT IsImage(arguments.source) AND NOT IsInstanceOf(arguments.source, "java.awt.image.BufferedImage")>
<cfthrow message="Invalid argument. Source must be a ColdFusion image or a BufferedImage">
</cfif>
<!--- cfSearching: verify the correct transparency arguments were supplied --->
<cfset Local.transparentColor = replace(trim(arguments.color), "##", "", "all")>
<cfif Local.transparentColor eq "" AND arguments.x EQ -1 AND arguments.y EQ -1>
<cfthrow message="Missing argument. You must supply either a transparent color OR the X and Y coordinates of the transparent pixel.">
<cfelseif Local.transparentColor neq "" AND (arguments.x NEQ -1 OR arguments.y NEQ -1)>
<cfthrow message="Too many arguments. Supply EITHER a transparent color OR the X and Y coordinates of the transparent pixel.">
</cfif>
<cfset Local.useTransparentColor = len(Local.transparentColor)>
<cfif Local.useTransparentColor>
<!--- cfSearching: transparent color must be a 6 character hex value --->
<cfif len(Local.transparentColor) NEQ 6 OR ReFindNoCase("[^0-9a-f]", Local.transparentColor)>
<cfthrow message="Invalid argument. Transparent color must be a 6 character hexidecimal value.">
</cfif>
<cfelse>
<!--- cfSearching: x,y coordinates must be >= zero --->
<cfif arguments.x LTE -1 OR arguments.y LTE -1>
<cfthrow message="Invalid or missing argument. The minimum value for X and Y coordinates is 0.">
</cfif>
</cfif>
<cfscript>
Local.sourceImage = arguments.source;
// cfSearching: extract the BufferedImage if needed
if ( IsImage(arguments.source) ) {
Local.sourceImage = ImageGetBufferedImage(arguments.source);
}
// cfSearching: encode the image as a gif to create the correct color model
if ( NOT IsInstanceOf(Local.sourceImage.getColorModel(), "java.awt.image.IndexColorModel")) {
Local.ImageIO = createObject("java", "javax.imageio.ImageIO");
Local.bao = createObject("java", "java.io.ByteArrayOutputStream").init();
Local.ImageIO.write( Local.sourceImage, "gif", Local.bao);
Local.bai = createObject("java", "java.io.ByteArrayInputStream").init( Local.bao.toByteArray() );
Local.sourceImage = Local.ImageIO.read( Local.bai );
}
// cfSearching: extract the color model information
Local.sourceModel = Local.sourceImage.getColorModel();
Local.colorSize = Local.sourceModel.getMapSize();
// cfSearching: Construct byte array to store rgb component values from the color model
// cfSearching: Based on source from Christian Cantrell's blog http://weblogs.macromedia.com/cantrell/archives/2004/01/byte_arrays_and_1.cfm
Local.byteClass = createObject("java", "java.lang.Byte").TYPE;
Local.reflectArray = createObject("java","java.lang.reflect.Array");
Local.dimen = [ 3, Local.colorSize ];
Local.colors = Local.reflectArray.newInstance( Local.byteClass, javacast("int[]", Local.dimen) );
// cfSearching: Grab the rgb color components from source color model
// cfSearching: colors[1] - red, colors[2] - green, colors[3] - blue
Local.sourceModel.getReds( Local.colors[1] );
Local.sourceModel.getGreens( Local.colors[2] );
Local.sourceModel.getBlues( Local.colors[3] );
// cfSearching: initialize the transparent color index to "none"
Local.transIndex = -1;
if (Local.useTransparentColor) {
// cfSearching: Extract decimal RGB values of transparent color
Local.transRed = InputBaseN( Left(Local.transparentColor, 2), 16);
Local.transGreen = InputBaseN( Mid(Local.transparentColor, 3, 2), 16);
Local.transBlue = InputBaseN( Right(Local.transparentColor, 2), 16);
// cfSearching: Find the index of the transparent color in the source color model
for (Local.index = 1; Local.index LTE Local.colorSize; Local.index = Local.index + 1) {
// cfSearching: RGB color component arrays should all be the same size
Local.currRed = BitAnd( Local.colors[1][Local.index], 255);
Local.currGreen = BitAnd( Local.colors[2][Local.index], 255);
Local.currBlue = BitAnd( Local.colors[3][Local.index], 255);
if ( (Local.currRed EQ Local.transRed) AND (Local.currGreen EQ Local.transGreen)
AND (Local.currBlue EQ Local.transBlue)) {
// cfSearching: must subtract 1 because java arrays are zero based
Local.transIndex = Local.index - 1;
break;
}
}
}
else {
// cfSearching: Otherwise, use the pixel at the given coordinates as the transparent color
Local.transIndex = Local.sourceImage.getRaster().getSample(
javacast("int", arguments.x),
javacast("int", arguments.y),
javacast("int", 0)
);
}
// cfSearching: create new color model with transparent color
Local.IndexColorModel = createObject("java", "java.awt.image.IndexColorModel");
Local.newModel = Local.IndexColorModel.init(javacast("int", 8),
javacast("int", Local.colorSize),
Local.colors[1],
Local.colors[2],
Local.colors[3],
javacast("int", Local.transIndex)
);
Local.BufferedImage = createObject("java", "java.awt.image.BufferedImage");
Local.newBufferedImage = Local.BufferedImage.init(
Local.newModel,
Local.sourceImage.getRaster(),
Local.sourceImage.isAlphaPremultiplied(),
javacast("null", "")
);
</cfscript>
<cfreturn ImageNew( Local.newBufferedImage )>
</cffunction>
4 comments:
Hey I just found your blog via another post on cfbloggers.org - but I notice you don't have an about page (or even a byline for that matter). So...who are you?? :D
Eh, I am not one of the heavy hitters. So I did not think an about me section was warranted. That and I loathe writing profiles, and rarely read them ;) One day when I am running the world with my ColdFusion applications, I will hire a professional to spin a suitably fascinating and impressive profile. But I would not hold your breath waiting for that to happen ;)
Hi.. I was in the middle of something when i thought of one thing..
Suppose i upload an Image. the image with the cartoon on it.. I want that the background of the image should be black or any other i specify leaving that cartton over ir. The background should change. I am also using the watremak on it..
So here is my part of work i did. i am not an expert in handling image functions but it may involve advance java i thought. so i thought i share with you i tried something like this:
I tried using the ImageSetBackgroundColor with ImageRect Function but that did not effected. Is there any way of doing it
Cheers
@randhawaz.com,
If you want to post a small amount of code, it has to be html escaped ie replace < and > symbols with < and >
-Leigh
Post a Comment