Although a Paint object is used to generate the colors used when drawing and filling with Java 2D, these colors are not always the final colors that end up displayed on the screen. Another Java 2D attribute, the Composite object, controls the way in which the colors being drawn combine with the colors that are already visible on the drawing surface.
A compositing operation combines the pixels of your drawing (the source pixels) with the pixels of the drawing surface (the destination pixels) to produce a new, composite set of pixels. Prior to Java 2D, you could use the setXORMode() method of a Graphics object to produce a simple and very specialized kind of compositing operation. Java 2D supports generalized compositing through the java.awt.Composite interface and its implementation, java.awt.AlphaComposite.
AlphaComposite performs compositing based on alpha-transparency, letting you paint with partially transparent colors that allow some of the background color to show through. It also supports various Porter-Duff compositing rules, as we'll discuss shortly.
Before we can discuss the AlphaComposite class, you need to understand a bit about the notion of transparent colors. With most low-end graphics systems, such as the AWT before Java 2D, colors and images are opaque. When a line is drawn, a shape is filled, or an image is rendered, that item totally obscures whatever pixels it is drawn on top of. An image is always represented as a rectangular array of pixels. Sometimes, however, we want to use an image to display a nonrectangular graphic. To allow this, some image formats support the notion of a transparent color. When the image is drawn, the background shows through whatever pixels are marked as transparent. Transparency is indicated with a bit mask: for each pixel in the image, the graphics system uses one extra bit of information to specify whether the pixel is transparent or opaque.
Bit mask transparency is an on-or-off thing: a pixel is either fully transparent or fully opaque. The notion of transparency can be generalized, however, to include translucent pixels. Instead of simply associating 1 extra bit of data with each pixel, the graphics system can associate 4, 8, 16, or some other number of bits with each pixel. This leads to 16, 256, or 65,536 possible levels of translucency, ranging from fully transparent (0) to fully opaque (16, 256, or 65536). When you think about it, these transparency bits are really no different than the bits we use to represent the red, green, and blue components of each pixel. The transparency bits are called the alpha channel, while the color bits are called the red, green, and blue channels. When you are working with pixels represented by red, green, and blue components, you are said to be using the RGB color space.
Transparent and translucent pixels do not actually exist. Inside a monitor, there are red, green, and blue electron guns (or red, green, and blue LCD elements), but there is no electron gun for the alpha channel. At the hardware level, a pixel is on or it is off; it cannot be partially on. In order to give the appearance of transparency, the graphics system (Java 2D, in this case) has to blend (or composite) transparent pixels with the pixels that are beneath them. When a source color Cs that has a transparency of α is painted over a destination color Cd, the two colors are combined to produce a new destination color Cd' with an equation like the following:
Cd' = Cs*α + Cd*(1 - α)
For the purposes of this computation, the alpha value and the values of the red, green, and blue channels are treated as floating-point numbers between 0.0 and 1.0, rather than 8- or 16-bit integers. The equation is shorthand: the computation is actually performed independently on each of the red, green, and blue channels. If Cs is fully opaque, α is 1 and Cd' is simply Cs. On the other hand, if Cs is fully transparent, α is 0 and Cd' is simply Cd. If α is somewhere between fully opaque and fully transparent, the resulting color Cd' is a combination of the source and destination colors. The remarkable fact about combining colors with this simple mathematical formula is that the resulting blended color is actually a visually convincing simulation of translucent colors.
You create an AlphaComposite object by calling its static getInstance() factory method. (A factory method is provided instead of a constructor so that the AlphaComposite class can cache and share immutable AlphaComposite objects.) getInstance() takes two arguments: a compositing mode and a float value between 0.0 and 1.0 that specifies an alpha-transparency value. The default Composite object used by a Graphics2D object is an AlphaComposite created like this:
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f);
The AlphaComposite.SrcOver constant also refers to this default AlphaComposite object.
The SRC_OVER compositing rule places the source color over the destination color and blends them based on the transparency of the source, using the formula shown in the previous section. I'll explain this rule and the others supported by AlphaComposite in more detail shortly. For now, you just need to know that SRC_OVER is the most commonly used compositing rule and has the most intuitive behavior.
If you use the default AlphaComposite attribute of a Graphics2D object, you can achieve color-blending effects by drawing with a translucent color. In Java 2D, the Color class includes new constructors that allow you to create translucent colors by including an alpha channel. For example, you can create and use a 50% transparent red color with code like the following:
Graphics2D g; // Initialized elsewhere Color c = new Color(1.0f, 0.0f, 0.0f, 0.5f); // Red with alpha = 0.5 g.setPaint(c); // Use this translucent color g.fillRect(100, 100, 100, 100); // Draw something with it
This code draws a translucent red rectangle over whatever background previously existed on the drawing surface represented by the Graphics2D object. As an aside, it is worth noting that you can achieve interesting effects by using the GradientPaint class to define color gradients between colors with different levels of transparency.
Now suppose that you want to draw a complex, multicolor figure, and you want to make it translucent. While you could allocate a bunch of translucent colors and draw with them, there is an easier way. As we already discussed, when you create an AlphaComposite object, you specify an alpha value for it. The alpha value of any source pixel is multiplied by the alpha value associated with the AlphaComposite currently in effect. Since the default AlphaComposite object has an alpha value of 1.0, this object does not affect the transparency of colors. However, by setting the alpha value of an AlphaComposite object, we can draw using opaque colors and opaque images and still achieve the effect of translucency. For example, here's another way to draw a translucent red rectangle:
Graphics2D g; // Initialized elsewhere Color c = new Color(1.0f, 0.0f, 0.0f); // Opaque red; alpha = 1.0 g.setPaint(c); // Use this opaque color // Get and install an AlphaComposite to do transparent drawing g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); g.fillRect(100, 100, 100, 100); // Start drawing with it
The SRC_OVER compositing rule draws a possibly translucent source color over the destination color. This is what we typically want to happen when we perform a graphics operation. But the AlphaComposite object actually allows colors to be combined according to seven other rules as well.
Before we consider the compositing rules in detail, there is an important point you need to understand. Colors displayed on the screen never have an alpha channel. If you can see a color, it is an opaque color. The precise color value may have been chosen based on a transparency calculation, but, once that color is chosen, the color resides in the memory of a video card somewhere and does not have an alpha value associated with it. In other words, with on-screen drawing, destination pixels always have alpha values of 1.0.
The situation is different when you are drawing into an off-screen image, however. As you'll see when we consider the Java 2D BufferedImage class later in this chapter, you can specify the desired color representation when you create an off-screen image. By default, a BufferedImage object represents an image as an array of RGB colors, but you can also create an image that is an array of ARGB colors. Such an image has alpha values associated with it, and when you draw into the images, the alpha values remain associated with the pixels you draw.
This distinction between on-screen and off-screen drawing is important because some of the compositing rules perform compositing based on the alpha values of the destination pixels, rather than the alpha values of the source pixels. With on-screen drawing, the destination pixels are always opaque (with alpha values of 1.0), but with off-screen drawing, this need not be the case. Thus, some of the compositing rules only are useful when you are drawing into off-screen images that have an alpha channel.
To overgeneralize a bit, we can say that when you are drawing on-screen, you typically stick with the default SRC_OVER compositing rule, use opaque colors, and vary the alpha value used by the AlphaComposite object. When working with off-screen images that have alpha channels, however, you can make use of other compositing rules. In this case, you typically use translucent colors and translucent images and an AlphaComposite object with an alpha value of 1.0.
The compositing rules supported by AlphaComposite are a subset of the classic Porter-Duff compositing rules.[1] Each of the rules describes a way of creating a new destination color Cd' by combining a source color Cs with the existing destination color Cd. The colors are combined according to a general formula, which is applied independently to each of the red, green, and blue values of the color:
[1]These rules were described originally in the paper "Compositing Digital Images," by Porter and Duff, published in SIGGRAPH, vol. 84.
Cd' = Cs*Fs + Cd*Fd
In this formula, Fs and Fd are the fractions of the source and destination colors used in the compositing operation, respectively. Each of the eight compositing rules uses a different pair of values for Fs and Fd, which is what makes each rule unique.
As I already noted, with certain off-screen images, destination pixels can have alpha values. The new alpha value of a destination pixel is computed in the same way as the new color value of that pixel (i.e., using the same fractions). If Ad is the destination alpha value and As is the source alpha value, the resulting alpha value is computed like this:
Ad' = As*Fs + Ad*Fd
Table 4-8 lists the compositing rules supported by AlphaComposite, using the names defined by that class. Don't take the names of these constants at face value; they can be misleading. The rule names that include the words IN and OUT make the most sense if you consider the case of a 1-bit alpha channel. In this case, the alpha channel is simply a bit mask, and an image has an inside where it is fully opaque and an outside where it is fully transparent. In the more general case, with a multibit alpha channel, these compositing operations behave more generally than their names imply. These rules also make the most sense when copying one image into another; they typically are not useful for drawing lines, text, and so on.
Table 4-8 includes the Fs and Fd values, used in the preceding formulas that define each of the compositing rules. These values are specified in terms of As, the alpha value of the source pixel, and Ad, the alpha value of the destination pixel. Note that the values of Fs and Fd listed here are for illustrative purposes, to help you understand the compositing operations. The actual formulas for computing composited colors depends on whether the source and destination color values have been premultiplied by their alpha values.
Rule | Fs | Fd | Description |
---|---|---|---|
SRC_OVER | As | 1-As |
By far the most commonly used compositing rule. It draws the source on top of the destination. The source and destination are combined based on the transparency of the source. Where the source is opaque, it replaces the destination. Where the source is transparent, the destination is unchanged. Where the source is translucent, the source and destination colors are combined so that some of the destination color shows through the translucent source. |
DST_OVER | 1-Ad | Ad |
This rule draws the source based on the transparency of the destination, so that the source appears to be underneath the destination. Where the destination is opaque, it is left unchanged. Where the destination is fully transparent, the source is drawn. Where the destination is translucent, the source and destination colors are combined so that some of the source color shows through the translucent destination. |
SRC | 1.0 | 0.0 |
The source replaces the destination color and its alpha channel with the source color and its alpha channel. In other words, the rule does a simple replacement, ignoring the destination and doing no color blending at all. |
CLEAR | 0.0 | 0.0 |
This rule ignores both the source and the destination. It clears the destination by setting it to a fully transparent black. |
SRC_IN | Ad | 0 |
This rule draws the source color using the transparency of the destination. Where the destination is fully opaque, it is replaced with an opaque version of the source. Where the destination is fully transparent, it remains fully transparent. Where the destination is translucent, it is replaced with an equally translucent version of the source. The color of the destination is never blended with the color of the source. |
SRC_OUT | 1-Ad | 0 |
This is the inverse of the SRC_IN rule. It draws the source color using the inverse of the destination transparency. Where the destination is opaque, it becomes transparent. Where the destination is transparent, it is replaced with an opaque version of the source. Where the destination is translucent, it is replaced with an inversely translucent version of the source. |
DST_IN | 0 | As |
This rule ignores the color of the source, but modifies the destination based on the transparency of the source. Where the source is transparent, the destination becomes transparent. Where the source is opaque, the destination is unmodified. Where the source is translucent, the destination becomes correspondingly translucent. |
DST_OUT | 0 | 1-As |
This rule is the inverse of the DST_IN rule. It ignores the source color but modifies the destination based on the inverse of the source transparency. Where the source is opaque, the destination becomes transparent. Where the source is transparent, the destination is left unmodified. Where the source is translucent, the destination is given the inverse translucency. |
Finally, note that AlphaComposite also predefines constant AlphaComposite objects that use each of these rules along with a built-in alpha value of 1.0. For example, the AlphaComposite.DstOver object uses the AlphaComposite.DST_OVER compositing rule with an alpha value of 1.0. This object is the same as the object created by:
AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1.0f)
Remember that if you use an AlphaComposite object with an alpha value other than the default 1.0, that alpha value is used to make the source colors more transparent before the rest of the compositing operation occurs.
Copyright © 2001 O'Reilly & Associates. All rights reserved.