In order to be displayed correctly CMYK images should contain color space information as ICC Profile. So the best way is to use that ICC Profile which can be easily extracted with Sanselan:
ICC_Profile iccProfile = Sanselan.getICCProfile(new File("filename.jpg"));
ColorSpace cs = new ICC_ColorSpace(iccProfile);
In case there is no ICC Profile attached to the image, I would use Adobe profiles as default.
Now the problem is that you cannot just load JPEG file with custom color space using ImageIO as it will fail throwing an exception complaining that it does not support some color space or sthing like that. Hense you will have to work with rasters:
JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(data));
Raster srcRaster = decoder.decodeAsRaster();
BufferedImage result = new BufferedImage(srcRaster.getWidth(), srcRaster.getHeight(), BufferedImage.TYPE_INT_RGB);
WritableRaster resultRaster = result.getRaster();
ColorConvertOp cmykToRgb = new ColorConvertOp(cs, result.getColorModel().getColorSpace(), null);
cmykToRgb.filter(srcRaster, resultRaster);
You can then use result
wherever you need and it will have converted colors.
In practice, however I've come across some images (taken with camera and processed with Photoshop) that had somehow inverted color values so the resulting image was always inverted and even after inverting them once again they were too bright. Although I still have no idea how to find out when exactly to use it (when I need to invert pixel values), I have an algorithm that corrects these values and convert color pixel by pixel:
JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(data));
Raster srcRaster = decoder.decodeAsRaster();
BufferedImage ret = new BufferedImage(srcRaster.getWidth(), srcRaster.getHeight(), BufferedImage.TYPE_INT_RGB);
WritableRaster resultRaster = ret.getRaster();
for (int x = srcRaster.getMinX(); x < srcRaster.getWidth(); ++x)
for (int y = srcRaster.getMinY(); y < srcRaster.getHeight(); ++y) {
float[] p = srcRaster.getPixel(x, y, (float[])null);
for (int i = 0; i < p.length; ++i)
p[i] = 1 - p[i] / 255f;
p = cs.toRGB(p);
for (int i = 0; i < p.length; ++i)
p[i] = p[i] * 255f;
resultRaster.setPixel(x, y, p);
}
I'm pretty sure RasterOp or ColorConvertOp could be used to make conversation more efficient, but this was enough for me.
Seriously, there is no need to use these simplified CMYK to RGB conversion algorithms as you can use ICC Profile that is embedded into image or available for free from Adobe. Resulting image is going to look better if not perfect (with embedded profile).