- The Basic Java 2D Recipe
- Set the Graphics2D Context...
- ...and Render Something
- Rendering on Components
- Shape Primitives
- Graphics Stroking
- Fill Attributes and Painting
- Transparency and Compositing
- Text
- Clipping
- Coordinate Space Transformations
- Techniques for Graphical User Input
- Double Buffering
- Comprehensive Example: Kspace Visualization
- Summary
Transparency and Compositing
Many visually impressive effects can be made using transparency and composite overlay.
Composite attributes are generally set in an AlphaComposite object and added to the Graphics2D context with the Graphics2D.setComposite() method. There is no direct way to instantiate an AlphaComposite object. Instead, a so-called factory method, AlphaComposite. getInstance(), is called. There are two variations of the getInstance() method; the first has one argument specifying the mixing rule to use (discussed next). The second version of the getInstance() method has both the argument for specifying the mixing rule as well as an argument specifying the transparency value to use. The transparency value ranges from 01 (in floating point) with 0.f being totally opaque and 1.f being totally transparent.
The mixing rules conform to a set of rules formalized by Porter and Duff and hence known as the Porter-Duff compositing rules. [ref 1] A key element for understanding the Porter-Duff rules is to appreciate the difference between the source and destination objects. The source refers to the new graphic that is to be rendered over existing graphics, called the destination. The process, then, is to first create the destination graphics, build an AlphaComposite object with the appropriate rules set, and add it to the attributes of the Graphics2D context. The last step is to create the source shape and render it.
The application PDExamples.java is used to demonstrate the Porter-Duff rules. Portions of the class are shown in Listing 3.6. You should run the example and experiment with different rules and transparency values. A sample screen from this program is shown in Figure 3.11.
Listing 3.6 PDExamples.java
class myCustomCanvas extends Canvas { float srcalpha, dstalpha; PDExamples2 pd; public myCustomCanvas(PDExamples2 pd) { this.pd = pd; } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; float xcenter = this.getSize().width/2; float ycenter = this.getSize().height/2; float srcalpha = 1-((float)pd.srcalpha.getValue()/100); float dstalpha = 1-((float)pd.dstalpha.getValue()/100); Shape dstRectangle = new Rectangle2D.Float(xcenter, ycenter-110, 80, 300); Shape srcRectangle = new Rectangle2D.Float(xcenter-110, ycenter, 300, 80); //create a BufferedImage to put destination and source BufferedImage bi = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_ARGB); Graphics2D big = bi.createGraphics(); big.setColor(new Color(1.f,0.f,0.f,dstalpha)); //setting context big.fill(dstRectangle); big.setColor(Color.blue); //check all radio buttons if (pd.clear_rb.isSelected() == true) { big.setComposite(AlphaComposite.getInstance _(AlphaComposite.CLEAR,srcalpha)); } else if (pd.dstin_rb.isSelected() == true) { big.setComposite(AlphaComposite.getInstance _(AlphaComposite.DST_IN,srcalpha)); } else if (pd.dstout_rb.isSelected() == true) { big.setComposite(AlphaComposite.getInstance _(AlphaComposite.DST_OUT,srcalpha)); } else if (pd.dstover_rb.isSelected() == true) { big.setComposite(AlphaComposite.getInstance _(AlphaComposite.DST_OVER,srcalpha)); } else if (pd.src_rb.isSelected() == true) { big.setComposite(AlphaComposite.getInstance _(AlphaComposite.SRC,srcalpha)); } else if (pd.srcin_rb.isSelected() == true) { big.setComposite(AlphaComposite.getInstance _(AlphaComposite.SRC_IN,srcalpha)); } else if (pd.srcout_rb.isSelected() == true) { big.setComposite(AlphaComposite.getInstance _(AlphaComposite.SRC_OUT,srcalpha)); } else if (pd.srcover_rb.isSelected() == true) { big.setComposite(AlphaComposite.getInstance _(AlphaComposite.SRC_OVER,srcalpha)); } big.fill(srcRectangle); g2d.drawImage(bi, null, 0, 0); } } class RadioListener implements ActionListener { myCustomCanvas mc; public RadioListener(myCustomCanvas mc) { this.mc = mc; } public void actionPerformed(ActionEvent e) { mc.repaint(); } } class SliderListener implements ChangeListener { myCustomCanvas mc; public SliderListener(myCustomCanvas mc) { this.mc = mc; } public void stateChanged(ChangeEvent e) { mc.repaint(); }//End of stateChanged }//End of SliderListener
Figure 3.11 Output from the PDExample.java demonstrating the SRC_OUT rule with 0 transparency (full opacity).
As in all of our examples so far in this chapter, we created an inner class extending Canvas. Note again the use of the BufferedImage for storing the current graphics, as used in Listing 3.4. This time, the necessity of using the BufferedImage stems from a different reason than needing a BufferedImage for the TexturePaint() constructor. In this case, we need to use the BufferedImage because the destination graphics won't have transparency. Therefore, we create a BufferedImage with transparency to hold our destination graphics and to give them a transparency value.
Also notice that we use a method paintGraphics() to set up and fill the BufferedImage. Therefore, our paint() method is pretty simple; it draws the BufferedImage to the Canvas.