17

I'm having problems converting a simple PNG into a JPEG format. I'm using the following code:

...

    File png = new File(filePath);
    try {
        SeekableStream s = new FileSeekableStream(png);
        PNGDecodeParam pngParams = new PNGDecodeParam();
        ImageDecoder dec = ImageCodec.createImageDecoder("png", s, pngParams);
        RenderedImage pngImage = dec.decodeAsRenderedImage();
        JPEGEncodeParam jparam = new JPEGEncodeParam();
        jparam.setQuality(0.50f); // e.g. 0.25f
        File jpeg = new File("jpeg.jpeg");
        FileOutputStream out = new FileOutputStream(jpeg);

        ImageEncoder encoder = ImageCodec.createImageEncoder("JPEG", out, jparam); 

        encoder.encode(pngImage);

        s.close();

    } catch (IOException e) {
        ok = false;
        e.printStackTrace();
    }

    return ok;
}

...

I end up with an JAI exception -> java.lang.RuntimeException: Only 1, or 3-band byte data may be written. at com.sun.media.jai.codecimpl.JPEGImageEncoder.encode(JPEGImageEncoder.java:148) ...

Ran out of options. Any suggestion?

Hannes Ovrén
  • 21,229
  • 9
  • 65
  • 75
Norberto
  • 185
  • 1
  • 2
  • 7

7 Answers7

27

It might be easier to use ImageIO to read the PNG into a BufferedImage and write the image out in JPEG format.

Addendum: In this approach, the conversion is handled transparently by the writer's ImageTranscoder.

BufferedImage img = ImageIO.read(new File("image.png"));
ImageIO.write(img, "jpg", new File("image.jpg"));
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • I guess the your solution is the same as the above suggested. It also works. – Norberto Feb 22 '10 at 11:45
  • 1
    `ImageIO`'s read/write conversion is implicit; the conversion in Trevor Harrison's example is informatively explicit. – trashgod Feb 22 '10 at 12:55
  • 2
    This solution is not going to work well for all PNGs. See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4836466. In case link breaks, the gist is that PNGs with alpha converted with ImageIO write JPEGs with a valid color space that generally isn't supported. To quote: "Developers who want the capability of writing/reading an image containing an alpha channel in the JPEG format can do so using the Image I/O API, but need to be aware that many native applications out there are not quite compliant with the YCbCrA and RGBA formats." Native applications here means all browsers. – peerless-phreap Feb 26 '20 at 19:54
25

you probably have alpha channel in the png that you need to get rid of before trying to write the jpg.

Create a new BufferedImage with type TYPE_INT_RGB (not TYPE_INT_ARGB), and then write your source image (pngImage) onto the new blank image.

Something like this (warning, not tested code):

BufferedImage newImage = new BufferedImage( pngImage.getWidth(), pngImage.getHeight(), BufferedImage.TYPE_INT_RGB);
newImage.createGraphics().drawImage( pngImage, 0, 0, Color.BLACK, null);
Trevor Harrison
  • 1,744
  • 1
  • 14
  • 20
7

I also found that reading a PNG image into a BufferedImage with ImageIO (Java 6) and writing it out to a JPG "format name" corrupted the image. The image was there, but the colors looked "solarized" and almost inverted. The JPG file was much smaller than the PNG file for sure, so a lot of compression was done. I don't see how you might control the compression or color depth.

sb4
  • 169
  • 1
  • 6
6

I had corrupted file after conversion with other solutions but this method worked for me:

    public static void formatConverter(String pngFile, String jpgFile) {
        try {

            File input = new File(pngFile);
            File output = new File(jpgFile);

            BufferedImage image = ImageIO.read(input);
            BufferedImage result = new BufferedImage(
                    image.getWidth(),
                    image.getHeight(),
                    BufferedImage.TYPE_INT_RGB);
            result.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
            ImageIO.write(result, "jpg", output);

        }  catch (IOException e) {
            e.printStackTrace();
        }

    }
Kostiantyn
  • 81
  • 1
  • 4
5

I suppse that JAI reads the PNG image with an indexed colour model and is only able to write 8-bit grayscale or 24-bit colour images as JPEG files.

If you are not required to use JAI for this task, you should be able to use ImageIO instead:

ImageIO.write(ImageIO.read(new File("in.png")), "JPEG", new File("out.jpg"));
jarnbjo
  • 33,923
  • 7
  • 70
  • 94
  • +1 @Norberto: Can you elaborate? Looking closer, this is similar to what I've used before. – trashgod Feb 26 '10 at 01:50
  • The above implementation doesn't perform as expected. I'm not implying that this does not work at all. In my case (images that I'm working with) this doesn't work. Fails to create the expected file and the output is a false value. Apparently it can't find any suitable writer for the JPEG. I find it rather strange but I have no time to try to figure out why exactly it isn't working. I'm going to have closer look in the future but for know I'm keeping with what works. – Norberto Mar 01 '10 at 11:34
  • ImageIO.write(ImageIO.read(new File("in.png")), "jpg", new File("out.jpg")); is working. Thanks. – VISHAL VIRADIA Nov 25 '12 at 03:27
2

I was getting the following message in a slightly different context. Getting rid of the alpha channel solved the problem

javax.imageio.IIOException: Sample size must be <= 8
    at com.sun.imageio.plugins.jpeg.JPEGImageWriter.write(JPEGImageWriter.java:435)
    at javax.imageio.ImageWriter.write(ImageWriter.java:580)
    at com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageWriter.write(Unknown Source)
    at net.sf.basedb.util.ImageTools.tiffToJpg(ImageTools.java:98)
    at net.sf.basedb.util.ImageTools.main(ImageTools.java:118)
dwaring
  • 71
  • 5
2

see Converting transparent gif / png to jpeg using java

Have a look at the solution that redraws with the graphics environment posted by harmanjd. The solution with the DirectColorModel doesn't compile and should be wiped away. I don't have enough rep points to comment directly there.