RMagick Web 2.0 Graphics
Alpha Compositing - Part 2

<< Back to page 2 <<

Shadows

Before adding the starburst you need to add a bit of shadow underneath the background and enlarge the image enough to contain the starburst. Adding a shadow is easy. Simply create a gray rectangle slightly larger than the background and blur it a bit. When you composite the background over the blurry rectangle it will make the background look like it's casting a shadow.

After blurring, the rectangle will be a bit larger so create an image that is 10 pixels larger on each side. Draw the gray rectangle and then use blur_image to blur it.

shadow = Image.new(background.columns+10, background.rows+10)

gc = Draw.new
gc.fill "gray30"
gc.rectangle 5, 5, background.columns+5, background.rows+5
gc.draw(shadow)
shadow = shadow.blur_image(0, 2)

Here's what the shadow looks like:

Composite the background over the shadow.

shadowed_background = shadow.composite(background, CenterGravity, OverCompositeOp)

Here's what the background looks like after adding the shadow.

A larger background

Now you need to put everything on a larger canvas so you can add the starburst. Create a large white image and composite the background over it. The NorthGravity argument puts the background at the top. The result becomes the new background.

big_canvas = Image.new(shadowed_background.columns, HEIGHT*5)
big_background = big_canvas.composite(shadowed_background, NorthGravity, OverCompositeOp)

The image below has a gray border so you can see the dimensions of the new background.

The starburst's shadow

Creating the finished starburst uses the same techniques I've already covered: alpha compositing, opacity masks, and annotation. Before starting, examine the starburst closely so that you'll understand what the next few steps are for. Now is probably a good time to go back and review the image you're working toward. The starburst has 25 points and is filled with a yellow-green gradient. The words "Ruby + Magick" appear in yellow within the starburst. The starburst has a very subtle shadow underneath. The starburst and its shadow are rotated 20° counter-clockwise.

Start by drawing a black version of the starburst on a white background. Rotate the starburst -20°. We'll use this image as a starting point for 2 different images.

black_star = Image.new(170,170)
black_star.star(25, 60, 80, 'none', 'black')
black_star.rotate!(-20)

(Don't go looking for the star method in the Image class because you won't find it. I've added it especially for this tutorial. The algorithm is not relevant to this tutorial so I'm not going to discuss it here. If you want to see the code, go on to page 4.)

Make a copy of the black starburst and blur it to form the shadow.

star_shadow = black_star.copy.blur_image(3, 2)

Now you need to make the area outside of the shadow's perimeter transparent while leaving the inside opaque. Obviously this is a job for the copy-opacity composition operator, but what should you use for the opacity mask? The perimeter is blurry, so the blur pixels must be partially transparent. The solution is actually quite easy: use the inverse of the shadow itself:

shadow_mask = star_shadow.negate

Remember, the black pixels in the mask indicate transparent pixels in the destination image. The white pixels in the mask indicate opaque pixels. Use the mask to add transparency to the shadow:

shadow_mask.matte = false
star_shadow.matte = true
star_shadow.composite!(shadow_mask, CenterGravity, CopyOpacityCompositeOp)

Here's a piece of the shadow after compositing. This piece has been scaled up 400% and composited on a pale blue background. You can easily see where it's fully transparent, partially transparent, and fully opaque:

The starburst itself

Set the shadow aside for a moment and make the green starburst itself. Start by making an image with a green gradient background. Add the "Ruby + Magick" text with annotate. Finally, rotate the background -20° to match the shadow.

grad = GradientFill.new(0, 0, black_star.columns, 0, "#99eb24", "#3c7f05}")
green_grad = Image.new(black_star.columns, black_star.rows, grad)

gc = Draw.new
gc.annotate(green_grad, 0, 0, 0, 0, "Ruby\n+\nMagick!") do
    gc.gravity = CenterGravity
    gc.stroke = 'none'
    gc.fill = 'yellow'
    gc.pointsize = 24
    gc.font_weight = BoldWeight
end
green_grad.rotate!(-20)

Use the black star image to make another opacity mask. Negate it so that the outside of the star is black and the inside is white.

inverse_black_star = black_star.negate

Use the mask as the source image in a copy-opacity composite operation. This makes the center of the green gradient opaque in the shape of the starburst and transparent outside of the starburst.

inverse_black_star.matte = false
green_grad.matte = true
green_star = green_grad.composite(inverse_black_star, CenterGravity, CopyOpacityCompositeOp)

Composite the green starburst over its shadow.1

shadowed_green_star = star_shadow.composite(green_star, CenterGravity, OverCompositeOp)

Here's what the green starburst looks like before and after adding the shadow. The pale blue background indicates the transparent portions.

Without shadow With shadow

At this point you may be asking yourself "Why did I rotate the images before adding transparency?" The answer is that rotate produces a completely opaque image, so if you were to add the transparency and then rotate, the rotated image loses all your carefully-added transparency.

The final result

Finally, pick a spot on the background image (I chose x=400, y=0) and composite the starburst on the background.

result = big_background.composite(shadowed_green_star, 400, 0, OverCompositeOp)

Summary

This tutorial showed you how to produce some nice-looking effects with just a handful of RMagick methods. The most useful technique is the copy-opacity composite operation for making parts of an image transparent. The annotate method is an easy and versatile method for adding text. We covered using blur_image for shadows and negate for masks. Rotate and append are also handy tools in your RMagick toolbox.

The entire program for this tutorial is here. If you want to see just the code for Image#star, go on to the next page.

>> Page 4: Image#star >>


1 Glenn Rempe pointed out that this call to composite tickles a bug that is present in versions of RMagick prior to 1.10.1, including RMagick 1.9.2 on Windows. If you're using one of these versions of RMagick, add the following statement: [go back]

# Add this line if you're using RMagick < 1.10.1
green_star.crop!(CenterGravity, star_shadow.columns, star_shadow.rows)
shadowed_green_star = star_shadow.composite(green_star, CenterGravity, OverCompositeOp)

Tim - June 18, 2006