Transparency and Composition


In the standard RGB color model, every color is described by its red, green, and blue components. However, it is also convenient to be able to describe areas of an image that are transparent or partially transparent. When you superimpose an image onto an existing drawing, the transparent pixels do not obscure the pixels under them at all, whereas partially transparent pixels are mixed with the pixels under them. Figure 7-23 shows the effect of overlaying a partially transparent rectangle on an image. You can still see the details of the image shine through from under the rectangle.

Figure 7-23. Overlaying a partially transparent rectangle on an image


In the Java 2D API, transparency is described by an alpha channel. Each pixel has, in addition to its red, green, and blue color components, an alpha value between 0 (fully transparent) and 1 (fully opaque). For example, the rectangle in Figure 7-23 was filled with a pale yellow color with 50% transparency:

 new Color(0.7F, 0.7F, 0.0F, 0.5F); 

Now let us look at what happens if you superimpose two shapes. You need to blend or compose the colors and alpha values of the source and destination pixels. Porter and Duff, two researchers in the field of computer graphics, have formulated 12 possible composition rules for this blending process. The Java 2D API implements all of these rules. Before we go any further, we want to point out that only two of these rules have practical significance. If you find the rules arcane or confusing, just use the SRC_OVER rule. It is the default rule for a Graphics2D object, and it gives the most intuitive results.

Here is the theory behind the rules. Suppose you have a source pixel with alpha value aS. In the image, there is already a destination pixel with alpha value aD. You want to compose the two. The diagram in Figure 7-24 shows how to design a composition rule.

Figure 7-24. Designing a composition rule


Porter and Duff consider the alpha value as the probability that the pixel color should be used. From the perspective of the source, there is a probability aS that it wants to use the source color and a probability of 1 aS that it doesn't care. The same holds for the destination. When composing the colors, let us assume that the probabilities are independent. Then there are four cases, as shown in Figure 7-24. If the source wants to use the source color and the destination doesn't care, then it seems reasonable to let the source have its way. That's why the upper-right corner of the diagram is labeled "S." The probability for that event is aS·(1 aD). Similarly, the lower-left corner is labeled "D." What should one do if both destination and source would like to select their color? That's where the Porter-Duff rules come in. If we decide that the source is more important, then we label the lower-right corner with an "S" as well. That rule is called SRC_OVER. In that rule, you combine the source colors with a weight of aS and the destination colors with a weight of (1 aSaD.

The visual effect is a blending of the source and destination, with preference given to the source. In particular, if aS is 1, then the destination color is not taken into account at all. If aS is 0, then the source pixel is completely transparent and the destination color is unchanged.

The other rules depend on what letters you put in the boxes of the probability diagram. Table 7-1 and Figure 7-25 show all rules that are supported by the Java 2D API. The images in the figure show the results of the rules when a rectangular source region with an alpha of 0.75 is combined with an elliptical destination region with an alpha of 1.0.

Table 7-1. The Porter-Duff Composition Rules

CLEAR

Source clears destination.

SRC

Source overwrites destination and empty pixels.

DST

Source does not affect destination.

SRC_OVER

Source blends with destination and overwrites empty pixels.

DST_OVER

Source does not affect destination and overwrites empty pixels.

SRC_IN

Source overwrites destination.

SRC_OUT

Source clears destination and overwrites empty pixels.

DST_IN

Source alpha modifies destination.

DST_OUT

Source alpha complement modifies destination.

SRC_ATOP

Source blends with destination.

DST_ATOP

Source alpha modifies destination. Source overwrites empty pixels.

XOR

Source alpha complement modifies destination. Source overwrites empty pixels.


Figure 7-25. Porter-Duff composition rules


As you can see, most of the rules aren't very useful. Consider, as an extreme case, the DST_IN rule. It doesn't take the source color into account at all, but it uses the alpha of the source to affect the destination. The SRC rule is potentially usefulit forces the source color to be used, turning off blending with the destination.

The DST, SRC_ATOP, DST_ATOP, and XOR rules have been added in JDK 1.4.

For more information on the Porter-Duff rules, see, for example, Foley, van Dam, Feiner, et al., Section 17.6.1.

You use the setComposite method of the Graphics2D class to install an object of a class that implements the Composite interface. The Java 2D API supplies one such class, AlphaComposite, that implements all the Porter-Duff rules in Figure 7-25.

The factory method getInstance of the AlphaComposite class yields an AlphaComposite object. You supply the rule and the alpha value to be used for source pixels. For example, consider the following code:

 int rule = AlphaComposite.SRC_OVER; float alpha = 0.5f; g2.setComposite(AlphaComposite.getInstance(rule, alpha)); g2.setPaint(Color.blue); g2.fill(rectangle); 

The rectangle is then painted with blue color and an alpha value of 0.5. Because the composition rule is SRC_OVER, it is transparently overlaid on the existing image.

The program in Example 7-7 lets you explore these composition rules. Pick a rule from the combo box and use the slider to set the alpha value of the AlphaComposite object.

Furthermore, the program displays a verbal description of each rule. Note that the descriptions are computed from the composition rule diagrams. For example, a "DS" in the second row stands for "blends with destination."

The program has one important twist. There is no guarantee that the graphics context that corresponds to the screen has an alpha channel. (In fact, it generally does not.) When pixels are deposited to a destination without an alpha channel, then the pixel colors are multiplied with the alpha value and the alpha value is discarded. Because several of the Porter-Duff rules use the alpha values of the destination, a destination alpha channel is important. For that reason, we use a buffered image with the ARGB color model to compose the shapes. After the images have been composed, we draw the resulting image to the screen.

 BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D gImage = image.createGraphics(); // now draw to gImage g2.drawImage(image, null, 0, 0); 

Here is the complete code for the program. Figure 7-26 shows the screen display. As you run the program, move the alpha slider from left to right to see the effect on the composed shapes. In particular, note that the only difference between the DST_IN and DST_OUT rules is how the destination (!) color changes when you change the source alpha.

Example 7-7. CompositeTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.image.*;   4. import java.awt.geom.*;   5. import java.util.*;   6. import javax.swing.*;   7. import javax.swing.event.*;   8.   9. /**  10.    This program demonstrates the Porter-Duff composition rules.  11. */  12. public class CompositeTest  13. {  14.    public static void main(String[] args)  15.    {  16.       JFrame frame = new CompositeTestFrame();  17.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  18.       frame.setVisible(true);  19.    }  20. }  21.  22. /**  23.    This frame contains a combo box to choose a composition  24.    rule, a slider to change the source alpha channel,  25.    and a panel that shows the composition.  26. */  27. class CompositeTestFrame extends JFrame  28. {  29.    public CompositeTestFrame()  30.    {  31.       setTitle("CompositeTest");  32.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  33.  34.       canvas = new CompositePanel();  35.       add(canvas, BorderLayout.CENTER);  36.  37.       ruleCombo = new JComboBox(new  38.          Object[]  39.          {  40.             new Rule("CLEAR", "  ", "  "),  41.             new Rule("SRC", " S", " S"),  42.             new Rule("DST", "  ", "DD"),  43.             new Rule("SRC_OVER", " S", "DS"),  44.             new Rule("DST_OVER", " S", "DD"),  45.             new Rule("SRC_IN", "  ", " S"),  46.             new Rule("SRC_OUT", " S", "  "),  47.             new Rule("DST_IN", "  ", " D"),  48.             new Rule("DST_OUT", "  ", "D "),  49.             new Rule("SRC_ATOP", "  ", "DS"),  50.             new Rule("DST_ATOP", " S", " D"),  51.             new Rule("XOR", " S", "D "),  52.          });  53.       ruleCombo.addActionListener(new  54.          ActionListener()  55.          {  56.             public void actionPerformed(ActionEvent event)  57.             {  58.                Rule r = (Rule)ruleCombo.getSelectedItem();  59.                canvas.setRule(r.getValue());  60.                explanation.setText(r.getExplanation());  61.             }  62.          });  63.  64.       alphaSlider = new JSlider(0, 100, 75);  65.       alphaSlider.addChangeListener(new  66.          ChangeListener()  67.          {  68.             public void stateChanged(ChangeEvent event)  69.             {  70.                canvas.setAlpha(alphaSlider.getValue());  71.             }  72.          });  73.       JPanel panel = new JPanel();  74.       panel.add(ruleCombo);  75.       panel.add(new JLabel("Alpha"));  76.       panel.add(alphaSlider);  77.       add(panel, BorderLayout.NORTH);  78.  79.       explanation = new JTextField();  80.       add(explanation, BorderLayout.SOUTH);  81.  82.       canvas.setAlpha(alphaSlider.getValue());  83.       Rule r = (Rule) ruleCombo.getSelectedItem();  84.       canvas.setRule(r.getValue());  85.       explanation.setText(r.getExplanation());  86.    }  87.  88.    private CompositePanel canvas;  89.    private JComboBox ruleCombo;  90.    private JSlider alphaSlider;  91.    private JTextField explanation;  92.    private static final int DEFAULT_WIDTH = 400;  93.    private static final int DEFAULT_HEIGHT = 400;  94. }  95.  96. /**  97.    This class describes a Porter-Duff rule.  98. */  99. class Rule 100. { 101.    /** 102.       Constructs a Porter-Duff rule 103.       @param n the rule name 104.       @param pd1 the first row of the Porter-Duff square 105.       @param pd2 the second row of the Porter-Duff square 106.    */ 107.    public Rule(String n, String pd1, String pd2) 108.    { 109.       name = n; 110.       porterDuff1 = pd1; 111.       porterDuff2 = pd2; 112.    } 113. 114.    /** 115.       Gets an explanation of the behavior of this rule. 116.       @return the explanation 117.    */ 118.    public String getExplanation() 119.    { 120.       StringBuilder r = new StringBuilder("Source "); 121.       if (porterDuff2.equals("  ")) r.append("clears"); 122.       if (porterDuff2.equals(" S")) r.append("overwrites"); 123.       if (porterDuff2.equals("DS")) r.append("blends with"); 124.       if (porterDuff2.equals(" D")) r.append("alpha modifies"); 125.       if (porterDuff2.equals("D ")) r.append("alpha complement modifies"); 126.       if (porterDuff2.equals("DD")) r.append("does not affect"); 127.       r.append(" destination"); 128.       if (porterDuff1.equals(" S")) r.append(" and overwrites empty pixels"); 129.       r.append("."); 130.       return r.toString(); 131.    } 132. 133.    public String toString() { return name; } 134. 135.    /** 136.       Gets the value of this rule in the AlphaComposite class 137.       @return the AlphaComposite constant value, or -1 if 138.       there is no matching constant. 139.    */ 140.    public int getValue() 141.    { 142.       try 143.       { 144.          return (Integer) AlphaComposite.class.getField(name).get(null); 145.       } 146.       catch (Exception e) 147.       { 148.          return -1; 149.       } 150.    } 151. 152.    private String name; 153.    private String porterDuff1; 154.    private String porterDuff2; 155. } 156. 157. /** 158.    This panel draws two shapes, composed with a 159.    composition rule. 160. */ 161. class CompositePanel extends JPanel 162. { 163.    public CompositePanel() 164.    { 165.       shape1 = new Ellipse2D.Double(100, 100, 150, 100); 166.       shape2 = new Rectangle2D.Double(150, 150, 150, 100); 167.    } 168. 169.    public void paintComponent(Graphics g) 170.    { 171.       super.paintComponent(g); 172.       Graphics2D g2 = (Graphics2D)g; 173. 174.       BufferedImage image = new BufferedImage(getWidth(), getHeight(), 175.          BufferedImage.TYPE_INT_ARGB); 176.       Graphics2D gImage = image.createGraphics(); 177.       gImage.setPaint(Color.red); 178.       gImage.fill(shape1); 179.       AlphaComposite composite = AlphaComposite.getInstance(rule, alpha); 180.       gImage.setComposite(composite); 181.       gImage.setPaint(Color.blue); 182.       gImage.fill(shape2); 183.       g2.drawImage(image, null, 0, 0); 184.    } 185. 186.    /** 187.       Sets the composition rule. 188.       @param r the rule (as an AlphaComposite constant) 189.    */ 190.    public void setRule(int r) 191.    { 192.       rule = r; 193.       repaint(); 194.    } 195. 196.    /** 197.       Sets the alpha of the source 198.       @param a the alpha value between 0 and 100 199.    */ 200.    public void setAlpha(int a) 201.    { 202.       alpha = (float) a / 100.0F; 203.       repaint(); 204.    } 205. 206.    private int rule; 207.    private Shape shape1; 208.    private Shape shape2; 209.    private float alpha; 210. } 

Figure 7-26. The CompositeTest program



 java.awt.Graphics2D 1.2 

  • void setComposite(Composite s)

    sets the composite of this graphics context to the given object that implements the Composite interface.


 java.awt.AlphaComposite 1.2 

  • static AlphaComposite getInstance(int rule)

  • static AlphaComposite getInstance(int rule, float alpha)

    construct an alpha composite object.

    Parameters:

    rule

    One of CLEAR, SRC, SRC_OVER, DST_OVER, SRC_IN, SRC_OUT, DST_IN, DST_OUT, DST,DST_ATOP,SRC_ATOP,XOR

     

    alpha

    The alpha value for the source pixels




    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net