Archive for category image

Images and CFDocument performance

Sometime back one of our customer reported that using png images in cfdocument makes it very very slow. I could not replicate it with any png image but it did happen with his png image.

Today, Andy reported a similar issue but this time it was for jpeg images. In both the cases, performance hit is huge and it does not happen with all the images.

I spent a significant amount of time debugging it today and it turns out that the reason for both the issues are same – there is something special about these images and that is colorspace. All these images actually had a different colorspace than what java imaging uses. Because of this, when we need the pixel values of image to print it on pdf (by calling BufferedImage.getRGB()), it tries to convert this colorspace to RGB colorspace and that is very very costly. That is where the entire time goes. So how do you fix it? I opened all the images in an image editor and saved it again. This time it got saved in standard RGB colorspace and the time taken to create the pdf got reduced from 110 sec to 1.5 seconds. That is huge!!! Isn’t it? But can you control all the images over the web? NO.. right? Read on.. there is more to this story.

A little bit of looking up on web pointed me to this Sun bug which is the exact same bug which we were hitting. Thankfully it got fixed in mustang i.e JDK1.6 which ColdFusion8 uses by default. But hey wait a second.. Didn’t Andy say that he is seeing it on ColdFusion 8? why do we still see this happening when it is fixed in JDK1.6? It appears that this bug was fixed only in core JDK api but not in JAI (Java advanced imaging) codecLib that ColdFusion 8 uses. So what do we do now? You can do either of these two

  1. Remove clibwrapper_jiio.jar from “lib” folder.
  2. Or, set this system property to the JVM. -Dcom.sun.media.imageio.disableCodecLib=true . You can set this in [cf-install_dir]/runtime/bin/jvm.config if you are using standalone coldfusion server.

You should keep in mind that codecLib libraries are native libraries which are meant to increase the java imaging performance. So disabling it might degrade the performance of CFImage somewhere. Also keep in mind that removing this jar or disabling codecLib will not result into any loss of functionality – it just means that all image operations will be pure java.

There is another related Sun’s bug which I thought might be useful to you. Image loading might get very slow if the server is running in debug mode. Your server is running in debug mode if you see something like this in your jvm.config or VM startup option.

-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005

This bug got introduced in JDK1.6 and does not exist on 1.5. So make sure that you are not running the server in debug mode.

To summarise, you can do following to increase the performance of image loading in cfdocument.

  1. If you are on JDK1.5, there is not much you can do. The only option is to change the colorspace of images. I will try to see if we can address this in ColdFusion code.
  2. If you are on JDK1.6,
    • Disable codecLib as mentioned above.
    • Disable debug mode by removing the complete debug string mentioned above
  3. In addition to this, you might want to use ‘localurl’ attribute for cfdocument tag. See this for more details.

Tags: , ,

Finding Image Type for a file

This is a follow up on my previous post on URLConnection APIs
We will use this API to find out the image type of a given file. Here is the udf.

<cffunction name="getMimeType">
    <cfargument name="filepath">
 
    <cfset var urlConn = createObject("java", "java.net.URLConnection")>
    <cfset var fileobj = fileopen(filepath, "readbinary")>
    <!--- just read the first 20 characters of the file as thats sufficient --->
    <cfset var bytes = fileread(fileobj, 20)>
    <cfset var istream = createObject("java", "java.io.ByteArrayInputStream").init(bytes)>
    <cfset fileobj.close()>
    <cfreturn urlConn.guessContentTypeFromStream(istream)>
</cffunction>
<cffunction name="GetImageType">
    <cfargument name="filepath">
    <cfset var mimetype = getMimeType(filepath)>
    <cfset var imagetype="">
    <cfif not isDefined("mimetype")>
        <cfthrow message="Not an Image file">
    </cfif>
    <cfswitch expression="#mimetype#">
        <cfcase Value="image/gif">
            <cfset imagetype="gif">
        </cfcase>
        <cfcase Value="image/x-bitmap">
            <cfset imagetype="bmp">
        </cfcase>
        <cfcase Value="image/png">
            <cfset imagetype="png">
        </cfcase>
        <cfcase Value="image/jpeg">
            <cfset imagetype="jpeg">
        </cfcase>
        <cfcase Value="image/jpg">
            <cfset imagetype="jpeg">
        </cfcase>
        <cfdefaultcase><cfthrow message="Not an Image file">
        </cfdefaultcase>
        </cfswitch>
        <cfreturn imagetype>
</cffunction>

If I run the code below

<cfset filepath = "C:\temp\test.jpg">
<cfoutput>#GetImageType(filepath)#</cfoutput>

It nicely prints out “jpeg”.

You should note that I used the new File IO function added in ColdFusion 8 using which I can read as many no of bytes from the file as I want. No more reading the entire file into memory.

More extensive post on File IO functions coming next !