One of the best features about Java is the amount of low-level complexity it removes, allowing you to focus on developing the application. However, this does sometimes hide some important issues.
We found one of these with the ImageIO class which offers a whole series of methods to save an image as a Tiff, a PNG or a JPEG. All the complexity is hidden and you can save out the image in one line of code (magic). Unfortunately, it also hides an interesting feature…
ImageIO will happily save all kinds of images, including images with transparency (ARGB). And it will save it as a JPEG, even though JPEGs do not really support transparency. The problem comes when you try to view the JPEG. Java understands this hybrid image so it will load back correctly, but very few other tools do.
Because it has 4 bands (RGB are three and then transparency makes the fourth), most JPEG tools assume it must be in the CMYK colorspace and interpret the colors as CMYK. CMYK works completely differently to RGB so the image comes out looking wrong (often with a nasty red tint). Unfortunately, there is no way that a JPEG Decoder can spot this has happened – it is a bug in the way ImageIO works. You can manually fix it by removing any alpha component from your BufferedImage before you save it in ImageIO with the following code
private static BufferedImage convertARGBToRGB(BufferedImage image) {
int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
int[] pixelsOut = ((DataBufferInt) result.getRaster().getDataBuffer()).getData();
System.arraycopy(pixels, 0, pixelsOut, 0, pixels.length);
return result;
}
In our JDeli image library (which provides a complete JPG Converter), we have actually added a flag to read the JPEG correctly for you if you know the file is broken
try { Map map = new HashMap(); map.put("DECODE_4_COMPONENTS_AS_ARGB","TRUE"); BufferedImage image = JDeli.read(new File(“path/file.jpeg”), map); } catch (IOException e) { // Deal with read error here. }
So while Java makes life much easier most of the time, there are some interesting gotchas out there…