|
Replies:
5
-
Last Post:
Jun 4, 2006 3:24 PM
by: seattledoug
|
|
|
|
|
|
|
Transparent pixels in PNG/Buffered Image
Posted:
May 31, 2006 2:54 PM
|
|
|
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
|
|
|
|
|
|
|
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted:
May 31, 2006 5:55 PM
in response to: seattledoug
|
|
|
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".
|
|
|
|
|
|
|
|
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted:
Jun 1, 2006 5:47 AM
in response to: Chris Campbell
|
|
|
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?
|
|
|
|
|
|
|
|
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted:
Jun 1, 2006 8:49 AM
in response to: swpalmer
|
|
|
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".
|
|
|
|
|
|
|
|
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted:
Jun 1, 2006 2:53 PM
in response to: Chris Campbell
|
|
|
>> 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".
|
|
|
|
|
|
|
|
Re: [JAVA2D] Transparent pixels in PNG/Buffered Image
Posted:
Jun 4, 2006 3:24 PM
in response to: Chris Campbell
|
|
|
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 >
|
|
|
|
|