62

I have a BufferedImage I'm trying to write to a jpeg file, but my Java program throws an exception. I'm able to successfully save the same buffer to a gif and png. I've tried looking around on Google for solutions, but to no avail.

Code:

   File outputfile = new File("tiles/" + row + ":" + col + ".jpg");
   try {
       ImageIO.write(mapBufferTiles[row][col], "jpg", outputfile);
   } catch (IOException e) {
        outputfile.delete();
        throw new RuntimeException(e);
   }

Exception:

 Exception in thread "main" java.lang.RuntimeException: javax.imageio.IIOException: Invalid argument to native writeImage
 at MapServer.initMapBuffer(MapServer.java:90)
 at MapServer.<init>(MapServer.java:24)
 at MapServer.main(MapServer.java:118)
 Caused by: javax.imageio.IIOException: Invalid argument to native writeImage
 at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeImage(Native Method)
 at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeOnThread(JPEGImageWriter.java:1055)
 at com.sun.imageio.plugins.jpeg.JPEGImageWriter.write(JPEGImageWriter.java:357)
 at javax.imageio.ImageWriter.write(ImageWriter.java:615)
 at javax.imageio.ImageIO.doWrite(ImageIO.java:1602)
 at javax.imageio.ImageIO.write(ImageIO.java:1526)
 at MapServer.initMapBuffer(MapServer.java:87)
 ... 2 more
Karan
  • 1,636
  • 4
  • 19
  • 35
  • 1
    Are you on a platform that allows : in filenames? – mwittrock Aug 07 '10 at 23:09
  • 2
    Are you using OpenJDK? OpenJDK does not have a native JPEG encoder IIRC – Rui Vieira Aug 07 '10 at 23:11
  • @mwittrock, yep on linux (same filename works for png and gif) – Karan Aug 07 '10 at 23:25
  • @Rui - It seems in Eclipse's preferences that I have openjdk installed, but sun jdk is the default checked one. is there a way I can check for sure? – Karan Aug 07 '10 at 23:27
  • @Karan: If Sun's JDK is the default, that shouldn't be the problem. What kind of data is mapBufferTiles[row][col]? it should be a BufferedImage. – Rui Vieira Aug 07 '10 at 23:40
  • @Rui, yep it's a BufferedImage. I've now enabled Sun's JDK by default as it was indeed using OpenJDK. It's no longer throwing an exception but because the input buffer contains transparency, I think the jpeg is outputting pink. Anyways, for future reference, I'll put this link here: http://ianma.wordpress.com/2010/05/03/access-restriction-java-openjdk-rt-jar/ – Karan Aug 07 '10 at 23:47
  • @Karan, cool. I'll put as an answer? – Rui Vieira Aug 07 '10 at 23:57

6 Answers6

47

OpenJDK does not have a native JPEG encoder, try using Sun's JDK, or using a library (such as JAI

AFAIK, regarding the "pinkish tint", Java saves the JPEG as ARGB (still with transparency information). Most viewers, when opening, assume the four channels must correspond to a CMYK (not ARGB) and thus the red tint.

If you import the image back to Java, the transparency is still there, though.

Rui Vieira
  • 5,253
  • 5
  • 42
  • 55
  • 1
    As for the pink tint issue, i just converted the transparent pixels to white ones as per: http://stackoverflow.com/questions/464825/converting-transparent-gif-png-to-jpeg-using-java/1545417#1545417 – Karan Aug 08 '10 at 00:12
  • 1
    End of 2nd paragraph - shouldn't *"..as thus the red tint."* be *"..and thus the red tint."*? – Andrew Thompson Aug 21 '12 at 00:05
  • 9
    BTW, OpenJDK does have a native JPEG encoder. And if you try to save 32-bit-color file - it fails. And Sun JDK does not fail, but form tinted file. Not sure what is better. – Andrey Regentov Apr 25 '13 at 11:02
  • @AndreyRegentov Possibly now, but I'm pretty sure at the time of writing (2010), it didn't. – Rui Vieira Sep 30 '13 at 13:48
  • Thanks for this information! This is slightly tangential, but I'm running Arch Linux and trying to use Apophysis-J + OpenJDK v7 (a fun fractal creation app), but I couldn't get the render function output a valid .jpg file -- I couldn't find any documentation either (which is why I'm commenting here for posterity sake), but turns out the fix is easy -- just output the rendered file as a .png and it works fine. Hope this helps someone out there. Thanks again for this info, saved me some frustration! – hypervisor666 Dec 30 '13 at 05:40
40

I had the same issue in OpenJDK 7 and I managed to get around this exception by using an imageType of TYPE_3BYTE_BGR instead of TYPE_4BYTE_ABGR using the same OpenJDK.

Thunder
  • 2,994
  • 1
  • 24
  • 19
  • 3
    You're right!!! I created a new BufferedImage of TYPE_3BYTE_BGR and used getRGB() from BufferedImage of TYPE_INT_ARGB and setRGB() on new BufferedImage and then called ImageIO.write() and it works on Linux. – Peter Quiring Sep 10 '13 at 14:32
  • Issue still exists in OpenSDK 8 but this solution solves the problem. – Johann Mar 08 '18 at 16:06
  • 1
    Thanks. Changing from TYPE_4BYTE_ABGR to TYPE_3BYTE_BGR, works for me. – IBRAR AHMAD Apr 14 '18 at 16:56
  • 1
    can some please tell me what he means? I read the image like `ImageIO.read(new File(path));` and theres no image type arguments? – centenond Aug 18 '18 at 18:00
  • @PeterQuiring could you share how did you do that? As centenond mentions, there's no arguments to specify the image type, and according to javadoc the image type is automatically determined from a system registry. How can you affect this? – jotadepicas Aug 29 '19 at 19:47
  • @IBRARAHMAD can you shared the code on how did you achieved this? – jotadepicas Aug 29 '19 at 19:47
  • Create java.awt.image.BufferedImage with ctor (int width, int height, int imageType) The imageType parameter is what you are looking for. see https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html – Peter Quiring Sep 16 '19 at 16:50
  • I've posted some code in a new answer to illustrate the conversion process. – Peter Quiring Sep 16 '19 at 17:05
25

2019 answer: Make sure your BufferedImage does not have alpha transparency. JPEG does not support alpha, so if your image has alpha then ImageIO cannot write it to JPEG.

Use the following code to ensure your image does not have alpha transparancy:

static BufferedImage ensureOpaque(BufferedImage bi) {
    if (bi.getTransparency() == BufferedImage.OPAQUE)
        return bi;
    int w = bi.getWidth();
    int h = bi.getHeight();
    int[] pixels = new int[w * h];
    bi.getRGB(0, 0, w, h, pixels, 0, w);
    BufferedImage bi2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    bi2.setRGB(0, 0, w, h, pixels, 0, w);
    return bi2;
}
Adam Gawne-Cain
  • 1,347
  • 14
  • 14
  • I'm hitting this issue because I'm reading PNGs with alpha, and trying to write to JPG (well, not me, I'm dealing with a legacy code that does this...). Can you suggest a workaround for this scenario? Thanks! – jotadepicas Aug 29 '19 at 19:45
  • 1
    Using ```BufferedImage.TYPE_INT_RGB``` solved the issue. [Full example](https://docs.oracle.com/javase/tutorial/essential/concurrency/examples/ForkBlur.java) (provided by Oracle) – Cempoalxóchitl Jan 20 '20 at 06:29
8

Here is some code to illustrate @Thunder idea to change the image type to TYPE_3BYTE_BGR

try {
  BufferedImage input = ImageIO.read(new File("input.png"));
  System.out.println("input image type=" + input.getType());
  int width = input.getWidth();
  int height = input.getHeight();
  BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
  int px[] = new int[width * height];
  input.getRGB(0, 0, width, height, px, 0, width);
  output.setRGB(0, 0, width, height, px, 0, width);
  ImageIO.write(output, "jpg", new File("output.jpg"));
} catch (Exception e) {
  e.printStackTrace();
}
Peter Quiring
  • 1,648
  • 1
  • 16
  • 21
1

You get the same error

Caused by: javax.imageio.IIOException: Invalid argument to native writeImage
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeImage(Native Method)
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeOnThread(JPEGImageWriter.java:1055)

if you are using a not supported Color Space (in my case CYMK). See How to convert from CMYK to RGB in Java correctly? how to solve this.

Community
  • 1
  • 1
rmuller
  • 12,062
  • 4
  • 64
  • 92
0

I had a variation when the image I got was a RenderedImage, and instead of copying the byte array, I'm using the Raster which is a rectangular image representation.

var newBufferedImage = new BufferedImage(
    renderImg.getWidth(),
    renderImg.getHeight(),
    BufferedImage.TYPE_INT_RGB
);
renderImg.copyData(newBufferedImage.getRaster());

Depending on your needs it might be interesting to get the raster from the ColorModel.

bric3
  • 40,072
  • 9
  • 91
  • 111