|
Replies:
2
-
Last Post:
Oct 19, 2006 11:50 AM
by: tompalmer
|
|
|
|
|
|
|
Proposal for compound Composites
Posted:
Oct 6, 2006 6:19 AM
|
|
|
This is just a thought that I had yesterday when I used the AlphaComposite class, but I thought it may be worth sharing with you:
Setting a composite when painting on java.awt.Graphics2D object is usually done like this:
Composite myComposite = createMyComposite(); Composite originalComposite = g2D.getComposite(); g2D.setComposite(myComposite); paintALot(g2D); g2D.setComposite(originalComposite);
Just yesterday I decided that this will not work in some cases. Imagine you want to create a fancy effect by making a translucent panel. To achieve this you set an AlphaComposite.SrcOver with 0.5f alpha parameter in the panel's paintComponent() method . On that panel you have a bunch of labels with html text. You decided that if a label is disabled, it should be translucent, using an AlphaComposite.SrcOver with 0.5f alpha. This works well on a normal panel, but with our fancy translucent panel the disabled text will suddenly look exactly the same as the enabled text (both will be translucent with alpha 0.5). The reason is simply that the composites are replaced, while they should be combined to get the desired effect.
If you look at Romain Guy's recent blog entries, you will see that using Composites will be used more frequently, and sooner or later someone will run into a problem similar to the one I described. The solution would be to have a class CompoundComposite, that simply takes two Composite objects in the constructor. The code from the beginning would then look like this:
Composite myComposite = createMyComposite(); Composite originalComposite = g2D.getComposite(); if (originalComposite != null) { CompoundComposite compoundComposite = new CompoundComposite(originalComposite, myComposite); g2D.setComposite(compoundComposite); } else { g2D.setComposite(myComposite); } paintALot(g2D); g2D.setComposite(originalComposite);
Since I do not know too much about the inner workings of Java2D, but I would like to know if it is possible at all to create a class that can combine two arbitrary Composites. Maybe it is very simple. What if its createContext() method creates a CompundCompositeContext with a CompositeContext from each of the simple Composites (created by calling the simple Composites' createContext() method). The CompundCompositeContext's compose() method would then call the compose() method of each of the simple CompositeContexts.
What do you think? Am I on the right track? And if so, should this always be used when working with Composites? And if so, should CompoundComposite maybe be added to the java.awt package?
Just for the record, a similar problem exists for the AffineTransform, but here the concatenate() method in the AffineTransform does what my proposed CompoundComposite class would do.
Cheers, Jan
=========================================================================== 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] Proposal for compound Composites
Posted:
Oct 12, 2006 12:01 AM
in response to: "Jan Bösenberg...
|
|
|
Hi Jan,
This is an interesting proposal. It's also timely in that we've recently been discussing the possibility of adding a JComponent.setAlpha() (or similarly named method), and you touch upon some issues that came up in that discussion.
Swing and Java2D developers have certainly run into this issue before, and our advice has typically been to use "intermediate images" [1] to achieve your effect. Using this approach, you can isolate parts of your rendering routine that might use a different composite or alpha value than other parts of your routine. Taking your example, you could make this work by doing the following: Composite myComposite = createMyComposite(); Composite originalComposite = g2D.getComposite(); BufferedImage img = ...; Graphics2D gimg = img.createGraphics(); paintALot(gimg); gimg.dispose(); g2D.setComposite(myComposite); g2D.drawImage(img, ...); g2D.setComposite(originalComposite);
This works well most obviously in the SrcOver case (which I'd say is the 95% scenario), but it can work for other types of compositing as well. The downside of course is that it requires a lot of manual intervention on the part of the developer, and what if paintALot() is in some library that isn't under the developer's control? Before you know it, tin-hat paranoia takes hold and you're applying this technique all over your code, even when it's not necessary 
The SVG spec has something called "group opacity" [2], which is very similar to what I describe above, and probably very similar to the functionality that (I think) you're looking for. Here's a quote from section 14.5 of the SVG spec:
"Object/group opacity can be thought of conceptually as a postprocessing operation. Conceptually, after the object/group is rendered into an RGBA offscreen image, the object/group opacity setting specifies how to blend the offscreen image into the current background."
The SVG authors were going for that 95% scenario that I described earlier, in that the blending used is essentially SrcOver with a varying "opacity" value. Again, the nice thing here is that you can have lots of subelements within a group that each have different (individual) opacity settings, and then the "group opacity" setting is applied to that entire group (the conceptual image). This is the combination effect that (I believe) you are seeking.
This also happens to be the approach that we're leaning towards if/ when we implement JComponent.setAlpha(). The benefit here is that Swing's RepaintManager will essentially be doing all that dirty work that I showed earlier (with intermediate images behind the scenes as necessary). Going back to your original example, it should be as simple as calling label.setAlpha(0.5f), which will work equally well whether the content of the label is painted full opaque, or with translucent elements, as in your example. Either way, you'll get the results you're looking for, with minimal fuss. We'll need to specify this method, hopefully with pictures, so that developers understand what it really means to "set the opacity/alpha of a component", but right now we're aiming in this direction since it seems to solve the 95% case.
As an aside, JXPanel today uses the naïve approach, and basically calls g.setComposite(SrcOver(alpha) at the top of its paint() method. This of course will work for the most simple scenarios, but it won't work for anything more complicated, like your example. You can achieve some "interesting" effects using JXPanel, most of which are incorrect, but anyway... 
Okay, so I'm rambling, and I haven't even gotten to your CompoundComposite proposal yet. I think that CompoundComposite could probably work as you describe, where the compose() methods are chained together, and you'd probably end up with the desired effect. My main concern with this approach is performance: we almost never use Composite.createContext() directly, but instead bypass it altogether with accelerated loops. If we had to fall back on createContext() and compose() for each operation, performance would suffer. I suppose we could find a way around this, maybe analyze the individual Composites within a CompoundComposite, and maybe collapse them if they're simple (like if you're trying to chain two SrcOver's together). Regardless, even if you could get all of this working and spec'd out properly, the conceptual model may be difficult for developers to understand. At least, it's not quite as straightforward as comp.setAlpha().
Anyway, I hope that somewhere in here I've answered your questions sufficiently. I also hope that the setAlpha() method that's on the table for JDK 7 will solve the most common cases. Going forward, if we decide to add SVG and/or a "layers" API (for which Romain pines), it seems that both would fit in well conceptually with the "group opacity" and setAlpha() approaches. Thoughts?
Thanks, Chris
[1] Chet doesn't necessarily talk about the compositing issues here, but he does describe the general intermediate image approach: http://java.sun.com/developer/technicalArticles/Media/intimages/
[2] http://www.w3.org/TR/SVG/ masking.html#ObjectAndGroupOpacityProperties
On Oct 6, 2006, at 6:19 AM, Jan Bösenberg (INCORS GmbH) wrote: > This is just a thought that I had yesterday when I used the > AlphaComposite class, but I thought it may be worth sharing with you: > > Setting a composite when painting on java.awt.Graphics2D object is > usually done like this: > > Composite myComposite = createMyComposite(); > Composite originalComposite = g2D.getComposite(); > g2D.setComposite(myComposite); > paintALot(g2D); > g2D.setComposite(originalComposite); > > Just yesterday I decided that this will not work in some cases. > Imagine > you want to create a fancy effect by making a translucent panel. To > achieve this you set an AlphaComposite.SrcOver with 0.5f alpha > parameter > in the panel's paintComponent() method . On that panel you have a > bunch > of labels with html text. You decided that if a label is disabled, it > should be translucent, using an AlphaComposite.SrcOver with 0.5f > alpha. > This works well on a normal panel, but with our fancy translucent > panel > the disabled text will suddenly look exactly the same as the enabled > text (both will be translucent with alpha 0.5). The reason is simply > that the composites are replaced, while they should be combined to get > the desired effect. > > If you look at Romain Guy's recent blog entries, you will see that > using > Composites will be used more frequently, and sooner or later someone > will run into a problem similar to the one I described. The solution > would be to have a class CompoundComposite, that simply takes two > Composite objects in the constructor. The code from the beginning > would > then look like this: > > Composite myComposite = createMyComposite(); > Composite originalComposite = g2D.getComposite(); > if (originalComposite != null) { > CompoundComposite compoundComposite = new > CompoundComposite(originalComposite, myComposite); > g2D.setComposite(compoundComposite); > } else { > g2D.setComposite(myComposite); > } > paintALot(g2D); > g2D.setComposite(originalComposite); > > Since I do not know too much about the inner workings of Java2D, but I > would like to know if it is possible at all to create a class that can > combine two arbitrary Composites. Maybe it is very simple. What if its > createContext() method creates a CompundCompositeContext with a > CompositeContext from each of the simple Composites (created by > calling > the simple Composites' createContext() method). The > CompundCompositeContext's compose() method would then call the > compose() > method of each of the simple CompositeContexts. > > What do you think? Am I on the right track? And if so, should this > always be used when working with Composites? And if so, should > CompoundComposite maybe be added to the java.awt package? > > Just for the record, a similar problem exists for the AffineTransform, > but here the concatenate() method in the AffineTransform does what my > proposed CompoundComposite class would do. > > > Cheers, > Jan > > ====================================================================== > ===== > 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] Proposal for compound Composites
Posted:
Oct 19, 2006 11:50 AM
in response to: Chris Campbell
|
|
|
Ah, your optimization discussion here probably also explains my "cheating composite" question (posted separately). Thanks much.
|
|
|
|
|