The Source for Java Technology Collaboration

Home » java.net Forums » Java Desktop Technologies » Java 2D

Thread: Transparent pixels in PNG/Buffered Image

Welcome, Guest Help
Login Login
Guest Settings Guest Settings
Reply to this Thread Reply to this Thread Search Forum Search Forum Back to Thread List Back to Thread List

Permlink Replies: 5 - Last Post: Jun 4, 2006 3:24 PM by: seattledoug
seattledoug

Posts: 2
Transparent pixels in PNG/Buffered Image
Posted: May 31, 2006 2:54 PM
  Click to reply to this thread Reply

I am attempting to write a small utility to scale 8-bit PNG files. However, when I write out the scaled image the transparent field is getting mapped to a color rather than transparency.

The basic method is very simple:
BufferedImage bi = ImageIO.read(srcFile) ;
Image scaled = bi.getScaledInstance(targetWidth, targetHeight, Image.SCALE_FAST) ;
ColorModel cm = bi.getColorModel() ;
BufferedImage out = toBufferedImage1(bi, scaled, cm) ;
ImageIO.write(out, "png", destFile);

the tricky part is converting the scaled image to a buffered image in order to write out the PNG.

I'm doing that like this:
// Option 1 -- Using the screen as basis for gc
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc=gs.getDefaultConfiguration();

// Option 2 - using the original image as basis for gc
Graphics2D originalG = original.createGraphics() ;
GraphicsConfiguration origDC=originalG.getDeviceConfiguration() ;

BufferedImage bimage = origDC.createCompatibleImage(image.getWidth(null), image.getHeight(null), Transparency.BITMASK);

and then copy the scaled image into the new buffered image like this:
Graphics g = bimage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();

If I use option 1 (based on the screen) I get a 24-bit PNG that has correct transparency.
If I use option 2 (based on the original image) I get an 8-bit PNG that is correct except all transparent fields are filled with an arbitrary, incorrect color.

What I want is an 8-bit PNG with correct transparency.

So, can anyone tell me how to:
a) Write out an 8-bit PNG in another way that preserves transparency?
or b) Tell me how to make a buffered 8-bit image fully transparent (before I paint over with the image data)? I've tried various calls to setRGB and calling writableRaster w/o success.
or c) Give me a different way to write out an 8-bit PNG that doesn't rely on BufferedImage?

Thanks in advance for any suggestions.

Doug

Chris Campbell
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted: May 31, 2006 5:55 PM   in response to: seattledoug
  Click to reply to this thread Reply

Hi Doug,

First, it's best to avoid getScaledInstance(); there are better/faster
ways to do what you want (see the Java 2D FAQ [1]). Second, in order to
preserve the transparency from your original image, you'll need to call
g2d.setComposite(AlphaComposite.Src) before copying the original into
the new image.

Allow me to suggest some code that will resolve both of the above issues
in fewer steps:

BufferedImage in = ImageIO.read(srcFile);
GraphicsConfiguration gc =
in.createGraphics().getDeviceConfiguration();
BufferedImage out =
gc.createCompatibleImage(targetWidth, targetHeight, BITMASK);
Graphics2D g2d = out.createGraphics();
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(in, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
ImageIO.write(out, "png", destFile);

Thanks,
Chris

[1]
http://java.sun.com/products/java-media/2D/reference/faqs/index.html#Q_How_do_I_create_a_resized_copy

java2d@javadesktop.org wrote:
> I am attempting to write a small utility to scale 8-bit PNG files. However, when I write out the scaled image the transparent field is getting mapped to a color rather than transparency.
>
> The basic method is very simple:
> BufferedImage bi = ImageIO.read(srcFile) ;
> Image scaled = bi.getScaledInstance(targetWidth, targetHeight, Image.SCALE_FAST) ;
> ColorModel cm = bi.getColorModel() ;
> BufferedImage out = toBufferedImage1(bi, scaled, cm) ;
> ImageIO.write(out, "png", destFile);
>
> the tricky part is converting the scaled image to a buffered image in order to write out the PNG.
>
> I'm doing that like this:
> // Option 1 -- Using the screen as basis for gc
> GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
> GraphicsDevice gs = ge.getDefaultScreenDevice();
> GraphicsConfiguration gc=gs.getDefaultConfiguration();
>
> // Option 2 - using the original image as basis for gc
> Graphics2D originalG = original.createGraphics() ;
> GraphicsConfiguration origDC=originalG.getDeviceConfiguration() ;
>
> BufferedImage bimage = origDC.createCompatibleImage(image.getWidth(null), image.getHeight(null), Transparency.BITMASK);
>
> and then copy the scaled image into the new buffered image like this:
> Graphics g = bimage.createGraphics();
> g.drawImage(image, 0, 0, null);
> g.dispose();
>
> If I use option 1 (based on the screen) I get a 24-bit PNG that has correct transparency.
> If I use option 2 (based on the original image) I get an 8-bit PNG that is correct except all transparent fields are filled with an arbitrary, incorrect color.
>
> What I want is an 8-bit PNG with correct transparency.
>
> So, can anyone tell me how to:
> a) Write out an 8-bit PNG in another way that preserves transparency?
> or b) Tell me how to make a buffered 8-bit image fully transparent (before I paint over with the image data)? I've tried various calls to setRGB and calling writableRaster w/o success.
> or c) Give me a different way to write out an 8-bit PNG that doesn't rely on BufferedImage?
>
> Thanks in advance for any suggestions.
>
> Doug
> [Message sent by forum member 'seattledoug' (seattledoug)]
>
> http://forums.java.net/jive/thread.jspa?messageID=117956
>
> ===========================================================================
> 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".


swpalmer

Posts: 244
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted: Jun 1, 2006 5:47 AM   in response to: Chris Campbell
  Click to reply to this thread Reply

Sorry for this off-topic question:

> in.createGraphics().getDeviceConfiguration();

The Graphics2D returned from in.createGraphics() can't be dispose()'d. How do we know that it's safe to do this without leaking something?
I mention this because I see that you were careful to do it for the output image...

> Graphics2D g2d = out.createGraphics();
> g2d.setComposite(AlphaComposite.Src);
> g2d.drawImage(in, 0, 0, targetWidth,
> idth, targetHeight, null);
> g2d.dispose();

Does the dispose() call potentially flush some drawing operations? Is it simply not needed if you don't "write" to the graphics context? Should you always dispose() every Graphics object that you create just to be safe?

Chris Campbell
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted: Jun 1, 2006 8:49 AM   in response to: swpalmer
  Click to reply to this thread Reply

Hi Scott,

On Jun 1, 2006, at 5:47 AM, java2d@javadesktop.org wrote:
> Sorry for this off-topic question:
>
>> in.createGraphics().getDeviceConfiguration();
>
> The Graphics2D returned from in.createGraphics() can't be dispose
> ()'d. How do we know that it's safe to do this without leaking
> something?
> I mention this because I see that you were careful to do it for the
> output image...
>> Graphics2D g2d = out.createGraphics();
>> g2d.setComposite(AlphaComposite.Src);
>> g2d.drawImage(in, 0, 0, targetWidth,
>> idth, targetHeight, null);
>> g2d.dispose();
>
> Does the dispose() call potentially flush some drawing operations?
> Is it simply not needed if you don't "write" to the graphics context?

Jim would probably be a better person to answer this question, but
since JDK 1.4 or so, Graphics.dispose() doesn't really do anything
heavyweight; we have other internal mechanisms for ensuring that
associated resources are disposed properly. It doesn't (currently)
flush pending operations or anything like that, and nowadays it
doesn't really matter whether you call it or not (it won't leak if
you don't). That said...

> Should you always dispose() every Graphics object that you create
> just to be safe?

Yes, I think it is good practice to always call g.dispose() for every
Graphics object that you create. Even though it is now effectively a
no-op, it serves as a useful marker in your code to say "I'm done
with this Graphics object now", which helps improve readability
especially when dealing with multiple Graphics objects in the same
block of code.

So tasting my own medicine, the code probably should've looked like
this:

BufferedImage in = ImageIO.read(srcFile);
Graphics2D gin = in.createGraphics();
GraphicsConfiguration gc = gin.getDeviceConfiguration();
gin.dispose();

BufferedImage out =
gc.createCompatibleImage(targetWidth, targetHeight, BITMASK);
Graphics2D gout = out.createGraphics();
gout.setComposite(AlphaComposite.Src);
gout.drawImage(in, 0, 0, targetWidth, targetHeight, null);
gout.dispose();
ImageIO.write(out, "png", destFile);

Thanks,
Chris

> [Message sent by forum member 'swpalmer' (swpalmer)]
>
> http://forums.java.net/jive/thread.jspa?messageID=118149
>
> ======================================================================
> =====
> 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".


Jim Graham
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted: Jun 1, 2006 2:53 PM   in response to: Chris Campbell
  Click to reply to this thread Reply

>> Does the dispose() call potentially flush some drawing operations?
>> Is it simply not needed if you don't "write" to the graphics context?
>
> Jim would probably be a better person to answer this question, but
> since JDK 1.4 or so, Graphics.dispose() doesn't really do anything
> heavyweight; we have other internal mechanisms for ensuring that
> associated resources are disposed properly. It doesn't (currently)
> flush pending operations or anything like that, and nowadays it
> doesn't really matter whether you call it or not (it won't leak if
> you don't). That said...

dispose is used to free internal resources associated with the contexts
manually. As its documentation states, the finalizer will take care of
those resources anyway, but more aggressive disposing will keep the
resource usage minimal. Graphics, like most objects that might
potentially include native data that needs to be disposed, protects
itself with a finalizer to free the resources even if you forget to
manually dispose it. Thus, dispose() is simply a more aggressive
technique to keep overall resource usage down when you know that you are
done with an object.

So, from the two pieces of code, the one inside your paint routine which
is probably going to be called a lot is probably the one to worry about
disposing more than one you incidentally create during initialization,
though ideally you would dispose both. It's just much more critical in
code that might expect to be called hundreds of times per second (during
a flurry of updates for instance).

On a side note, in 1.4 we removed the need to dispose our regular
graphics objects by moving all of their data into Java structures which
can be garbage collected normally without any need for a finalizer. I'm
not entirely sure about the Graphics objects used in printing, though,
but all of the ones on the screen and on offscreen, volatile, and
buffered images are all "finalizer free". You shouldn't make a habit of
relying on that, though, as I have no idea if any of the ports that
other vendors make might not need have finalizable state in their
graphics and so the dispose() method for them may have non-trivial
meaning...

...jim

===========================================================================
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".


seattledoug

Posts: 2
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted: Jun 4, 2006 3:24 PM   in response to: Chris Campbell
  Click to reply to this thread Reply

Hi Chris,

Very sorry I wasn't able to get back online for the last few days and respond to this.

I just wanted to let you know that your solution works perfectly and in as little code as I had expected to have to write before I ran into the BufferedImage mess.

Many thanks for your help. I wonder why all my googling failed to turn up the Java2D faq as the answer is plainly there? Oh well.

Thanks,

Doug

> Hi Doug,
>
> First, it's best to avoid getScaledInstance(); there
> are better/faster
> ways to do what you want (see the Java 2D FAQ [1]).
> Second, in order to
> preserve the transparency from your original image,
> you'll need to call
> g2d.setComposite(AlphaComposite.Src) before copying
> the original into
> the new image.
>
> Allow me to suggest some code that will resolve both
> of the above issues
> in fewer steps:
>
> BufferedImage in = ImageIO.read(srcFile);
> GraphicsConfiguration gc =
>
> in.createGraphics().getDeviceConfiguration();
> BufferedImage out =
> gc.createCompatibleImage(targetWidth,
> getWidth, targetHeight, BITMASK);
> Graphics2D g2d = out.createGraphics();
> g2d.setComposite(AlphaComposite.Src);
> g2d.drawImage(in, 0, 0, targetWidth,
> idth, targetHeight, null);
> g2d.dispose();
> ImageIO.write(out, "png", destFile);
>
> Thanks,
> Chris
>
> [1]
> http://java.sun.com/products/java-media/2D/reference/f
> aqs/index.html#Q_How_do_I_create_a_resized_copy
>




 XML java.net RSS