Skip to main content

[JAVA2D] Saving ARGB BufferedImages as JPEG with ImageIO

5 replies [Last post]
Anonymous

I have problems with saving ARGB BufferedImages as JPEG with ImageIO:

It seems that ImageIO thinks to have a CMYK image due to the 4 channels
and does it all wrong. I tried to find a way of telling imageio to take
only the 3 RGB channels and I also found something like that, but it
still generates very strange files that are too big (about 3 times as
big as they should be) and don't display correctly on some viewers.

Here's my code:

-----
File file = new File(filename);
ios = ImageIO.createImageOutputStream(file);
writer.setOutput(ios);
// Set some parameters
ImageWriteParam param = writer.getDefaultWriteParam();
if (param.canWriteCompressed() && quality >= 0.0 && quality <= 1.0) {
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
}
// if bi has type ARGB and alpha is false, we have to tell the writer
to not use the alpha channel:
// this is especially needed for jpeg files where imageio seems to
produce wrong jpeg files right now...
if (bi.getType() == BufferedImage.TYPE_INT_ARGB) {
// create a new ColorModel with OPAQUE transparency and no alpha
channel.
ColorModel cm = new
ComponentColorModel(bi.getColorModel().getColorSpace(), false, false,
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
// tell the writer to only use the first 3 bands (skip alpha)
param.setSourceBands(new int[] {0, 1, 2});
// although the java documentation says that SampleModel can be null,
an exception is thrown in that case
// therefore a 1*1 SampleModel that is compatible to cm is created:
param.setDestinationType(new ImageTypeSpecifier(cm,
cm.createCompatibleSampleModel(1, 1)));
}
// Write the image
writer.write(null, new IIOImage(bi, null, null), param);
ios.flush();
-----

The interesting part happens in here:
-----
// create a new ColorModel with OPAQUE transparency and no alpha
channel.
ColorModel cm = new
ComponentColorModel(bi.getColorModel().getColorSpace(), false, false,
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
// tell the writer to only use the first 3 bands (skip alpha)
param.setSourceBands(new int[] {0, 1, 2});
// although the java documentation says that SampleModel can be null,
an exception is thrown in that case
// therefore a 1*1 SampleModel that is compatible to cm is created:
param.setDestinationType(new ImageTypeSpecifier(cm,
cm.createCompatibleSampleModel(1, 1)));
-----

If I replace this with the following it all works well, but this uses
far too much memory due to the allocation of another BufferedImage:

-----
BufferedImage buffered = new BufferedImage(bi.getWidth(null),
bi.getHeight(null), BufferedImage.TYPE_INT_RGB);
buffered.createGraphics().drawImage(bi, 0, 0, null);
bi = buffered;
-----

Does anyone have experiences with this? What exactly am I doing wrong?

Best

J�rg Lehni

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Jim Graham

An image of TYPE_INT_ARGB uses a PackedColorModel with a Packed
Raster with a TYPE_INT DataBuffer. It is incompatible with the
Byte-based ComponentColorModel (and SampleModel) that you are
creating. I'm not sure how these two pieces of data would
interact in this case...

...jim

--On 03/15/04 22:40:12 +0100 J�rg Lehni wrote:
> if (bi.getType() == BufferedImage.TYPE_INT_ARGB) {
> // create a new ColorModel with OPAQUE transparency and no
> alpha channel.
> ColorModel cm = new
> ComponentColorModel(bi.getColorModel().getColorSpace(), false, false,
> Transparency.OPAQUE, DataBuffer.TYPE_BYTE);

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

J�rg Lehni

Thanks Jim!

That's how my latest code looks like, i simplified it a lot:

param.setSourceBands(new int[] {0, 1, 2});
param.setDestinationType(

ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRG
B),
0x00ff0000,
0x0000ff00,
0x000000ff,
0x0,
DataBuffer.TYPE_INT,
false
)
);

But it still produces the same problems. The resulting jpeg files are
strange, don't display correctly in some viewers and get much bigger in
size than necessary.

If I use the workaround of copying the TYPE_INT_ARGB BufferedImage into
a TYPE_INT_RGB BufferedImage and then generate the JPEG by just using
the default settings, everything works fine.

It looks to me as if there is a bug in the JPEG codec.

What do you think?

J�rg

Am 17.03.2004 um 01:55 schrieb Jim Graham:

> An image of TYPE_INT_ARGB uses a PackedColorModel with a Packed
> Raster with a TYPE_INT DataBuffer. It is incompatible with the
> Byte-based ComponentColorModel (and SampleModel) that you are
> creating. I'm not sure how these two pieces of data would
> interact in this case...
>
> ...jim
>
> --On 03/15/04 22:40:12 +0100 J�rg Lehni wrote:
>> if (bi.getType() == BufferedImage.TYPE_INT_ARGB) {
>> // create a new ColorModel with OPAQUE transparency and no
>> alpha channel.
>> ColorModel cm = new
>> ComponentColorModel(bi.getColorModel().getColorSpace(), false, false,
>> Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
>
> =======================================================================
> ====
> To unsubscribe, send email to listserv@java.sun.com and include in the
> body
> of the message "signoff JAVA2D-INTEREST". For general help, send
> email to
> listserv@java.sun.com and include in the body of the message "help".
>

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Chris Campbell

Hi J�rg,

This sounds a lot like 4836466, which we closed as "not a bug". See
the report for more details:
http://developer.java.sun.com/developer/bugParade/bugs/4836466.html

Basically, it appears that many native apps have poor support for JPEG
images with an alpha channel. If you can read back one of these images
using IIO (ImageIO.read()) and everything displays properly, I'd say
your issue is related to 4836466. If not, send me a small testcase
containing your sample code (off the list) and I can take a look.
Maybe there's a separate issue lurking out there...

Thanks,
Chris

On Mar 22, 2004, at 12:39 PM, J�rg Lehni wrote:
> Thanks Jim!
>
> That's how my latest code looks like, i simplified it a lot:
>
> param.setSourceBands(new int[] {0, 1, 2});
> param.setDestinationType(
>
> ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sR
> GB),
> 0x00ff0000,
> 0x0000ff00,
> 0x000000ff,
> 0x0,
> DataBuffer.TYPE_INT,
> false
> )
> );
>
> But it still produces the same problems. The resulting jpeg files are
> strange, don't display correctly in some viewers and get much bigger
> in size than necessary.
>
> If I use the workaround of copying the TYPE_INT_ARGB BufferedImage
> into a TYPE_INT_RGB BufferedImage and then generate the JPEG by just
> using the default settings, everything works fine.
>
> It looks to me as if there is a bug in the JPEG codec.
>
> What do you think?
>
> J�rg
>
> Am 17.03.2004 um 01:55 schrieb Jim Graham:
>
>> An image of TYPE_INT_ARGB uses a PackedColorModel with a Packed
>> Raster with a TYPE_INT DataBuffer. It is incompatible with the
>> Byte-based ComponentColorModel (and SampleModel) that you are
>> creating. I'm not sure how these two pieces of data would
>> interact in this case...
>>
>> ...jim
>>
>> --On 03/15/04 22:40:12 +0100 J�rg Lehni wrote:
>>> if (bi.getType() == BufferedImage.TYPE_INT_ARGB) {
>>> // create a new ColorModel with OPAQUE transparency and no
>>> alpha channel.
>>> ColorModel cm = new
>>> ComponentColorModel(bi.getColorModel().getColorSpace(), false, false,
>>> Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
>>
>> ======================================================================
>> =====
>> To unsubscribe, send email to listserv@java.sun.com and include in
>> the body
>> of the message "signoff JAVA2D-INTEREST". For general help, send
>> email to
>> listserv@java.sun.com and include in the body of the message "help".
>>
>
> =======================================================================
> ====
> To unsubscribe, send email to listserv@java.sun.com and include in the
> body
> of the message "signoff JAVA2D-INTEREST". For general help, send
> email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

J�rg Lehni

I do not agree.

When I generate the image with an alpha channel, then it's another
story. Photoshop thinks that it's a CMYK image while it's actually a
ARGB. But when I force the codec to use only the 3 RGB bands with the
setSourceBands command (see code bellow), then I get the weird
behavior. The image displays correctly on most viewers but becomes very
heavy. I can't tell what exactly goes wrong.

If you're interested I can send you on of those images for analysis.

Best

J�rg

Am 23.03.2004 um 19:40 schrieb Chris Campbell:

> Hi J�rg,
>
> This sounds a lot like 4836466, which we closed as "not a bug". See
> the report for more details:
> http://developer.java.sun.com/developer/bugParade/bugs/4836466.html
>
> Basically, it appears that many native apps have poor support for JPEG
> images with an alpha channel. If you can read back one of these
> images using IIO (ImageIO.read()) and everything displays properly,
> I'd say your issue is related to 4836466. If not, send me a small
> testcase containing your sample code (off the list) and I can take a
> look. Maybe there's a separate issue lurking out there...
>
> Thanks,
> Chris
>
> On Mar 22, 2004, at 12:39 PM, J�rg Lehni wrote:
>> Thanks Jim!
>>
>> That's how my latest code looks like, i simplified it a lot:
>>
>> param.setSourceBands(new int[] {0, 1, 2});
>> param.setDestinationType(
>>
>> ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_s
>> RGB),
>> 0x00ff0000,
>> 0x0000ff00,
>> 0x000000ff,
>> 0x0,
>> DataBuffer.TYPE_INT,
>> false
>> )
>> );
>>
>> But it still produces the same problems. The resulting jpeg files are
>> strange, don't display correctly in some viewers and get much bigger
>> in size than necessary.
>>
>> If I use the workaround of copying the TYPE_INT_ARGB BufferedImage
>> into a TYPE_INT_RGB BufferedImage and then generate the JPEG by just
>> using the default settings, everything works fine.
>>
>> It looks to me as if there is a bug in the JPEG codec.
>>
>> What do you think?
>>
>> J�rg
>>
>> Am 17.03.2004 um 01:55 schrieb Jim Graham:
>>
>>> An image of TYPE_INT_ARGB uses a PackedColorModel with a Packed
>>> Raster with a TYPE_INT DataBuffer. It is incompatible with the
>>> Byte-based ComponentColorModel (and SampleModel) that you are
>>> creating. I'm not sure how these two pieces of data would
>>> interact in this case...
>>>
>>> ...jim
>>>
>>> --On 03/15/04 22:40:12 +0100 J�rg Lehni wrote:
>>>> if (bi.getType() == BufferedImage.TYPE_INT_ARGB) {
>>>> // create a new ColorModel with OPAQUE transparency and no
>>>> alpha channel.
>>>> ColorModel cm = new
>>>> ComponentColorModel(bi.getColorModel().getColorSpace(), false,
>>>> false,
>>>> Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
>>>
>>> =====================================================================
>>> ======
>>> To unsubscribe, send email to listserv@java.sun.com and include in
>>> the body
>>> of the message "signoff JAVA2D-INTEREST". For general help, send
>>> email to
>>> listserv@java.sun.com and include in the body of the message "help".
>>>
>>
>> ======================================================================
>> =====
>> To unsubscribe, send email to listserv@java.sun.com and include in
>> the body
>> of the message "signoff JAVA2D-INTEREST". For general help, send
>> email to
>> listserv@java.sun.com and include in the body of the message "help".
>
> =======================================================================
> ====
> To unsubscribe, send email to listserv@java.sun.com and include in the
> body
> of the message "signoff JAVA2D-INTEREST". For general help, send
> email to
> listserv@java.sun.com and include in the body of the message "help".
>

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Ho Han Keng

i wrote me own JpegIt util that outputs jpeg images.
It has an Object which is a variable depending on commandline input.
i would Constructor a JComponent instance out of it.
Going from JComponent, thru Graphics2D paint method to BufferedImage is just java like "intuition".
Finally, ImageIO.write() the image out as jpeg :)
Very handy JpegIt utility...

As to ARGB, BufferedImage have support for it and many other types; my favorite is INT_RGB.

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".