Helping out on #coldfusion on dalnet, the topic of resizing JPEG images without having to install a external Java lib came up.
There are 2 things that make this slightly tricky, but not all together impossible:
- Documentation for com.sun.image.codec.jpeg.JPEGCodec can be very difficult to find if you don't know where you are looking
- Scaling a BufferedImage produces a Image, which can't be encoded into JPEG, so you need to create a new BufferedImage with the given Image.
So, here is the code I used as a test case to take an image of a Nissan 350z and scale it down by a quarter:
<cfscript>
//need this to do all the JPEG work
JpegCodec = createObject("java", "com.sun.image.codec.jpeg.JPEGCodec");
//reading in file
inputStream = createObject("java", "java.io.FileInputStream").init(expandPath("nissan.jpg"));
//pushing out the file
outputStream = createObject("java", "java.io.FileOutputStream").init(expandPath("nissan1.jpg"));
//decodes the JPEG
decoder = jpegCodec.createJPEGDecoder(inputStream);
//give us an image to scale
image = decoder.decodeAsBufferedImage();
//make it 1/4 the size
height = round(image.getHeight() /4);
width = round(image.getWidth() / 4);
//this is java.awt.image.Image, but we need a java.awt.image.BufferedImage!
scaledImage = image.getScaledInstance(JavaCast("int", width), JavaCast("int", height), image.SCALE_DEFAULT);
//create a new BufferedImage
newBufferedImage = createObject("java", "java.awt.image.BufferedImage").init(JavaCast("int", width), JavaCast("int", height), image.TYPE_INT_RGB);
//draw the image on the buffered image
graphics = newBufferedImage.createGraphics();
graphics.drawImage(scaledImage, 0, 0, Javacast("null", ""));
//encode it
encoder = jpegCodec.createJPEGEncoder(outputStream);
encoder.encode(newBufferedImage);
//close off the resources
outputStream.close();
</cfscript>
So there you have it – JPEG scaling without using an external Java library. Enjoy.
Comments
Error Occurred While Processing Request
JavaCast Type null must be one of the following types ("int","long","float","double","boolean","string")
The error occurred in C:localhtdocslocalhostjpg.cfm: line 29
27 : //draw the image on the buffered image
28 : graphics = newBufferedImage.createGraphics();
29 : graphics.drawImage(scaledImage, 0, 0, Javacast("null", 0));
30 :
31 : //encode it
Please try the following:
* Check the ColdFusion documentation to verify that you are using the correct syntax.
* Search the Knowledge Base to find a solution to your problem.
Browser Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8) Gecko/20051111 Firefox/1.5
Remote Address 127.0.0.1
Referrer
Date/Time 28-Nov-05 02:38 PM
Stack Trace
at cfjpg2ecfm1623948356.runPage(C:localhtdocslocalhostjpg.cfm:29)
coldfusion.runtime.JavaCastException: JavaCast Type null must be one of the following types ("int","long","float","double","boolean","string")
at coldfusion.runtime.CFPage.JavaCast(CFPage.java:5376)
at cfjpg2ecfm1623948356.runPage(C:localhtdocslocalhostjpg.cfm:29)
at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:147)
at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:357)
at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:62)
at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:107)
at coldfusion.filter.PathFilter.invoke(PathFilter.java:80)
at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:47)
at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:35)
at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:43)
at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
at coldfusion.CfmServlet.service(CfmServlet.java:105)
at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:91)
at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42)
at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:252)
at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:527)
at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:192)
at jrunx.scheduler.ThreadPool$DownstreamMetrics.invokeRunnable(ThreadPool.java:348)
at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:451)
at jrunx.scheduler.ThreadPool$UpstreamMetrics.invokeRunnable(ThreadPool.java:294)
at jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
This code works fine for me, what version of cf are you using? I am using 7 and the cf docs show null as being a valid argument. If you take a look back to the 6.1 docs though it does not show null as a valid argument, so maybe thats the problem.
You need a null here though because the methods 4th argument is looking for type ImageObserver and we are not passing it one. Still strange though because the docs for 7 include this statement
Warning: Do not assign the results of JavaCast("null","") to a ColdFusion variable. Unexpected results will occur.
Mark got any ideas?
Ahhh, nevermind, we are not casting a coldfusion variable, this should work fine in 7. Im not sure about 6.1 though
Yep, you are correct, it’s a CF7 code.
To c/p out of the docs:
A ColdFusion variable that holds a scalar or string type. Must be "" if type is null.
So if you are using 6.1, you will have to come up with an alternate way to get a null value (not sure how?).
Mind you it was code I had whipped up in about 15 minutes, just to prove a concept….
Yeap, there is no JavaCast type NULL in CF 6.1 while it works with CF7!
There are a few CFC available that don’t require external Java libraries. You can try one here:
http://www.olimpo.ch/tmt/cfc/tmt_img/
I think mark was just showing an example of how to use this particular one. Plus the error we were getting above was coming from the java.awt package, a standard package that is included.
Nice script 🙂 I’ve been wondering if something like this was possible without installing a package. Great job! and thanks for the post.
Thanks for picking this up!
So how would you do it if you wanted to make it a certain pixel width to proportion? Is there a method you can call to make it resize it to a certain width?
Ryan,
This would be handled when you decide what width and height you want the newly sized Image to be – i.e. here:
//make it 1/4 the size
height = round(image.getHeight() /4);
width = round(image.getWidth() / 4);
//this is java.awt.image.Image, but we need a java.awt.image.BufferedImage!
scaledImage = image.getScaledInstance(JavaCast("int", width), JavaCast("int", height), image.SCALE_DEFAULT);
Technically the scaled width and height can be whatever you want, so nothing is stopping you from delving into some Math and working out what the height and width are, and then making sure that the values are in proportion to the original values.
I’ve just made the image quarter of a size for demonstrative purposes.
I thought you might have to do some math for that, I was just wondering if there was a method that just took straight pixels. This will work though. Thanks!
It’s really nice to be able to control the output JPG qaulity, inserting this code will do that.
//encode it
encoder = jpegCodec.createJPEGEncoder(outputStream);
//create quality param
param = encoder.getDefaultJPEGEncodeParam(newBufferedImage);
//assign JPG quality 0-1
param.setQuality(JavaCast("float",.82),true);
//set JPG quality
encoder.setJPEGEncodeParam(param);
encoder.encode(newBufferedImage);
//close off the resources
outputStream.close();
Thanks for this great example
Below the outputStream.close(); the following code could/should be inserted.
// Close the connection to the original image
inputStream.close();
Thank you for your example and keep up the good work
The below code works in Cold Fusion 6. I wrote it as a function. Function calls are below the code and can be customized.
<cfscript>
function resizeImg(imgid,width,height,type) {
serverdir = "d:inetpubwhpd_images";
imgInputPath = serverdir & "full" & imgid & ".jpg";
imgOutputPath = serverdir & "" & type & "" & imgid & ".jpg";
//WriteOutput("output: " & imgOutputPath) & "<br>";
//WriteOutput("input: " & imgInputPath);
// for JPEG work
JpegCodec = createObject("java", "com.sun.image.codec.jpeg.JPEGCodec");
//reading in file
inputStream = createObject("java", "java.io.FileInputStream").init(imgInputPath);
// output destination
outputStream = createObject("java", "java.io.FileOutputStream").init(imgOutputPath);
//decodes the JPEG
decoder = jpegCodec.createJPEGDecoder(inputStream);
//give us an image to scale
image = decoder.decodeAsBufferedImage();
//this is java.awt.image.Image, but we need a java.awt.image.BufferedImage!
scaledImage = image.getScaledInstance(JavaCast("int", width), JavaCast("int", height), image.SCALE_DEFAULT);
//create a new BufferedImage
newBufferedImage = createObject("java", "java.awt.image.BufferedImage").init(JavaCast("int", width), JavaCast("int", height), image.TYPE_INT_RGB);
//draw the image on the buffered image
graphics = newBufferedImage.createGraphics();
imageObserver = createObject("java", "java.awt.Button").init();
//this was a dummy object since java doesn’t let me pass a null.
graphics.drawImage(scaledImage, 0, 0, imageObserver);
// Javacast("null", "")); null is not a valid argument in 6.1
// this line would have gone where imageObserver is now.
//encode it
encoder = jpegCodec.createJPEGEncoder(outputStream);
encoder.encode(newBufferedImage);
//close off the resources
outputStream.close();
}
</cfscript>
<!— create a thumbnail—>
<cfset thumbnail = resizeImg(ufnum, tn_w, tn_h, "tn")>
<!— create a med res web size —>
<cfset websize = resizeImg(ufnum, wb_w, wb_h, "web")>
Thanks for a nice script 😉
I love you, can you have my babies!
I have used this code and scaled images and it all works like a wheel but I want to stream the scaled image to the web page. I want to dump the scaled image as a variable which I can feed to the <img src=#X#> tag
I am currently using JAVAX.imageio.imageIO object to handle file writing …
Anybody got any ideas in advance …