RMagick Web 2.0 Graphics
Alpha Compositing - Part 2

<< Back to page 1 <<

Annotation

Adding text to an image is called annotation and is handled by the aptly-named annotate method in the Draw class. To add "RMAGICK", create an instance of Draw, set a few style attributes, and then call annotate.

gc = Draw.new
gc.fill = 'white'
gc.stroke = 'none'
gc.pointsize = 42

gc.annotate(background, 0, 0, 70, HEIGHT, "RMAGICK")

The annotate arguments are easy. The first two numbers describe the x- and y-axis rotation of the text. Here I've set these to 0. The 3rd and 4th arguments are the x- and y-offsets of the text. In this case, draw the text starting 70 pixels from the left and with the baseline exactly in the middle of the background. The code above produces this image:

(You may want to use the font= attribute to specify a font other that the default. Here I've let it default so the typeface is rather ordinary looking.)

Composition with transparency

Drawing the "reflection" of RMAGICK is not as easy. To make it look like a reflection you must add some transparency. Look carefully at the finished image. Notice that the reflection is mostly transparent, but it's slightly less transparent at the top and gradually becomes more transparent toward the bottom? To get this effect you'll create an image that describes the desired transparency levels as levels of gray and then use that image to set the transparency in the reflected RMAGICK.

Transparency information about an image is described in the alpha channel. Like the red, green, and blue channels, the alpha channel contains a number between 0 and MaxRGB, where MaxRGB is 100% transparency (or 0% opacity) and 0 is 0% transparency (or 100% opacity). The copy-opacity composition operation copies the transparency data from the source image to the destination image. Copy-opacity has two modes of operation:

  1. If the source image has an alpha channel, then copy-opacity simply copies the values from the alpha channel of each pixel in the source image to the corresponding pixel in the destination image.
  2. If the source image doesn't have an alpha channel, then copy-opacity uses the intensity of each pixel in the source image to compute the value of the alpha channel of the corresponding pixel in the destination image. The intensity of a pixel is computed from the values of the RGB channels according to this formula: I = 0.299*R + 0.587*G + 0.114*B. Copy-opacity sets the alpha channel of the destination image to MaxRGB - I. That is, more intensity == more opacity.

You're going to take advantage of the 2nd mode to create a transparency mask for the reflection. Notice from the intensity formula that for gray images (where R=G=B) the intensity of a pixel corresponds to its blackness. An entirely black pixel (#000000) has intensity 0 and therefore represents 100% transparency. Pixels that are less than entirely black are less than 100% transparent, and pixels that are entirely white (#ffffff) are 0% transparent.

Begin by creating a copy of the bottom (dark-blue) stripe and adding the RMAGICK annotation. The reflection will end up upside-down, so use flip to turn the stripe upside-down now so that it will be right-side up when you're done.

reflection = stripes[1].flip
reflection.composite!(color, CenterGravity, ColorizeCompositeOp)
gc.annotate(reflection, 0, 0, 70, HEIGHT, "RMAGICK")

The next 2 statements create a gradient image that ranges from entirely black to dark gray. The blackest pixels are at the top. The lightest pixels are at the bottom.

grad = GradientFill.new(0, 0, WIDTH, 0, "black", "gray35")
opacity_mask = Image.new(WIDTH, HEIGHT, grad)

The last trick you need to know is that RMagick ignores the alpha channel if the image's matte attribute is false, and respects it if matte is true. The next three statements

  1. enable the alpha channel in the destination image.
  2. force copy-opacity to use mode #2 by setting matte to false,
  3. use the copy-opacity composite operation to turn the pixel intensities in the opacity mask into transparency levels in the reflection.
reflection.matte = true
opacity_mask.matte = false
reflection.composite!(opacity_mask, CenterGravity, CopyOpacityCompositeOp)

Now flip the resulting image and composite it over the bottom stripe in the background:

reflection.flip!
background.composite!(reflection, SouthWestGravity, OverCompositeOp)

Notice that the reflection effect wouldn't work if we used a font with descenders (descenders are parts of letters that drop below the baseline, like the hook in the letter "g".) because the descenders would cross the centerline. Mr. Maloney cleverly bypasses that problem with a font that uses small caps for lowercase letters. I cleverly bypassed it by using all caps.

Next, add the starburst.

>> Page 3: Add the starburst >>