In the early days of Java, Sun produced a really handy set of classes to handle JPEG images. These included some really nifty little features like the ability to easily set the amount of compression and the resolution. When ImageIO came along, the class was deprecated. This means that it is still in Java but not guaranteed to be in any later releases. ImageIO was more complicated to use for JPEG images and we felt the earlier code produced better results so we continued to use it.
How do I update my old code?
The old JPEG classes have now been removed and will no longer compile. Here is the old and new versions so you can see the changes if you are still using these classes.
public static void saveAsJPEG(String jpgFlag,BufferedImage image_to_save, float JPEGcompression, FileOutputStream fos) throws IOException {
//useful documentation at http://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
//useful example program at http://johnbokma.com/java/obtaining-image-metadata.html to output JPEG data
//old jpeg class
//com.sun.image.codec.jpeg.JPEGImageEncoder jpegEncoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(fos);
//com.sun.image.codec.jpeg.JPEGEncodeParam jpegEncodeParam = jpegEncoder.getDefaultJPEGEncodeParam(image_to_save);
// Image writer
JPEGImageWriter imageWriter = (JPEGImageWriter) ImageIO.getImageWritersBySuffix(“jpeg”).next();
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
imageWriter.setOutput(ios);
//and metadata
IIOMetadata imageMetaData = imageWriter.getDefaultImageMetadata(new ImageTypeSpecifier(image_to_save), null);
if (jpgFlag != null){
int dpi = 96;
try {
dpi = Integer.parseInt(jpgFlag);
} catch (Exception e) {
e.printStackTrace();
}
//old metadata
//jpegEncodeParam.setDensityUnit(com.sun.image.codec.jpeg.JPEGEncodeParam.DENSITY_UNIT_DOTS_INCH);
//jpegEncodeParam.setXDensity(dpi);
//jpegEncodeParam.setYDensity(dpi);
//new metadata
Element tree = (Element) imageMetaData.getAsTree(“javax_imageio_jpeg_image_1.0?);
Element jfif = (Element)tree.getElementsByTagName(“app0JFIF”).item(0);
jfif.setAttribute(“Xdensity”, Integer.toString(dpi));
jfif.setAttribute(“Ydensity”, Integer.toString(dpi));
}
if(JPEGcompression>=0 && JPEGcompression<=1f){
//old compression
//jpegEncodeParam.setQuality(JPEGcompression,false);
// new Compression
JPEGImageWriteParam jpegParams = (JPEGImageWriteParam) imageWriter.getDefaultWriteParam();
jpegParams.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
jpegParams.setCompressionQuality(JPEGcompression);
}
//old write and clean
//jpegEncoder.encode(image_to_save, jpegEncodeParam);
//new Write and clean up
imageWriter.write(imageMetaData, new IIOImage(image_to_save, null, null), null);
ios.close();
imageWriter.dispose();
}
Are there any other alternatives?
We were not happy with ImageIO’s JPEG performance, so we developed our own solution called JDeli. You can find out how to read and write JPEG images in Java with JDeli and other libraries in these articles
Are you a Java Developer working with Image files?
// Read an image
BufferedImage bufferedImage = JDeli.read(dicomImageFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(heicImageFile);
// Write an image
JDeli.write(bufferedImage, "heic", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(jpegImageFile);
// Write an image
JDeli.write(bufferedImage, "jpeg", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(jpeg2000ImageFile);
// Write an image
JDeli.write(bufferedImage, "jpx", outputStreamOrFile);
// Write an image
JDeli.write(bufferedImage, "pdf", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(pngImageFile);
// Write an image
JDeli.write(bufferedImage, "png", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(tiffImageFile);
// Write an image
JDeli.write(bufferedImage, "tiff", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(webpImageFile);
// Write an image
JDeli.write(bufferedImage, "webp", outputStreamOrFile);
Why do developers choose JDeli over free alternatives?
- Works with newer image formats such as AVIF, HEIC, JPEG XL, WEBP
- Better support than alternatives for JPEG, PNG, TIFF.
- Prevent JVM crashes caused by native code in other image libraries
- Better performance than other popular Java image libraries
Thank you very much for this post. This was very helpful!
There is an error in the line with
“imageWriter.write(imageMetaData, new IIOImage(image_to_save, null, null), null);”
At the last argument you must provide “jpegParams” or otherwise the
compression quality will not be applied and all resulting images will have
the same file size (in my case @ Java 7u76).
Thanks for pointing out.
Thanks a lot for your code!
I used it to set dpi of a jpg image but i had to make some changes to make it work.
You get metadata as tree and modify dpi values, but you need to set the modified tree as metadata after changes. So i had to add this line after setting Xdensity and Ydensity attributes:
imageMetaData.setFromTree(“javax_imageio_jpeg_image_1.0”, tree);
Then, i had to modify one of the last lines in this way:
imageWriter.write(imageMetaData, new IIOImage(image_to_save, null, imageMetaData), jpegParams);
Thanks a lot for your code!But DPI settings are not valid.
JPEGImageWriter imageWriter = (JPEGImageWriter) ImageIO.getImageWritersBySuffix(“jpg”).next();
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
imageWriter.setOutput(ios);
IIOMetadata imageMetaData = imageWriter.getDefaultImageMetadata(new ImageTypeSpecifier(image), null);
Element tree = (Element) imageMetaData.getAsTree(“javax_imageio_jpeg_image_1.0”);
Element jfif = (Element) tree.getElementsByTagName(“app0JFIF”).item(0);
for (int i = 0; i < jfif.getAttributes().getLength(); i++) {
Node attribute = jfif.getAttributes().item(i);
System.out.println(attribute.getNodeName() + "="
+ attribute.getNodeValue());
}
jfif.setAttribute("Xdensity", Integer.toString(dpi));
jfif.setAttribute("Ydensity", Integer.toString(dpi));
imageMetaData.setFromTree("javax_imageio_jpeg_image_1.0", tree);
JPEGImageWriteParam jpegParams = (JPEGImageWriteParam) imageWriter.getDefaultWriteParam();
jpegParams.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
jpegParams.setCompressionQuality(1f);
//imageWriter.write(imageMetaData, new IIOImage(image, null, null), null);
imageWriter.write(imageMetaData, new IIOImage(image, null, imageMetaData), jpegParams);
ios.close();
imageWriter.dispose();
// This is OK!!!!
//JPEGImageEncoder jpegEncoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(fos);
//JPEGEncodeParam jpegEncodeParam = jpegEncoder.getDefaultJPEGEncodeParam(image);
//jpegEncodeParam.setDensityUnit(JPEGEncodeParam.DENSITY_UNIT_DOTS_INCH);
//jpegEncodeParam.setXDensity(dpi);
//jpegEncodeParam.setYDensity(dpi);
//jpegEncoder.encode(image, jpegEncodeParam);
// Invalid DPI settings! Why???
JPEGImageWriter imageWriter = (JPEGImageWriter) ImageIO.getImageWritersBySuffix(“jpg”).next();
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
imageWriter.setOutput(ios);
IIOMetadata imageMetaData = imageWriter.getDefaultImageMetadata(new ImageTypeSpecifier(image), null);
Element tree = (Element) imageMetaData.getAsTree(“javax_imageio_jpeg_image_1.0”);
Element jfif = (Element) tree.getElementsByTagName(“app0JFIF”).item(0);
for (int i = 0; i < jfif.getAttributes().getLength(); i++) {
Node attribute = jfif.getAttributes().item(i);
System.out.println(attribute.getNodeName() + "="
+ attribute.getNodeValue());
}
jfif.setAttribute("Xdensity", Integer.toString(dpi));
jfif.setAttribute("Ydensity", Integer.toString(dpi));
imageMetaData.setFromTree("javax_imageio_jpeg_image_1.0", tree);
JPEGImageWriteParam jpegParams = (JPEGImageWriteParam) imageWriter.getDefaultWriteParam();
jpegParams.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
jpegParams.setCompressionQuality(1f);
//imageWriter.write(imageMetaData, new IIOImage(image, null, null), null);
imageWriter.write(imageMetaData, new IIOImage(image, null, imageMetaData), jpegParams);
ios.close();
imageWriter.dispose();
Thanks for the post. Very helpful. I tried the same in my application and the image is getting written. My only concern is, in my existing code I have a line – jpegEncoder.setJPEGEncodeParam(jpegEncodeParam);
Will this too be handled by the current code or do I have to write any additional code for the same?
The JPEG support in both 7 and before is patchy.I would now recommend using our new JDeli library for all JPEG handling.
Thanks Mark for your time.
It will be great if a sample (like the current blog) can be shared for the JDeli library.
We added a blog post at https://blog.idrsolutions.com/how-to-write-jpeg-images-in-java/ and there are several blog posts on JDeli on the blog (https://blog.idrsolutions.com/?s=jdeli). There are also full javadoc (https://files.idrsolutions.com/maven/site/jdeli/apidocs/index.html) We do not currently have this setting in JDeli but it could be added as an enhancement.
Thanks Mark, I would also like to know if the code jpegEncoder.setJPEGEncodeParam(jpegEncodeParam);
gets handled as per the ImageIO code given in the current blog as we currently wish to continue with ImageIO