Wednesday, November 30, 2011

Embossing Effects On-The-Fly

Embossing is an effect used to give an object the appearance of being sunk into or raised above a background. They are very easy to create in Photoshop, but today I'm going to show you how you can start with nothing but a shape and build the effect in code. This has the advantage of allowing you to adjust the effect on-they-fly based on parameters you choose.

We've done exactly this in our new IDE. In fact, I've been given permission to show a very small glimpse of the tray area of our new layout editor. The same object in our current IDE is on the left, the new version is on the right.


That version on the right is produced in code, starting only with an 8-bit mask to describe the shape:


The embossing effect is created using 3 layers. The bottom layer is the drop shadow, the middle layer is the fill, and the top layer is the inner shadow. I created this simple animation to demonstrate how the individual layers build together to create the effect:


Now, let's get to some actual code. First, you need an image to use as the shape. This image should have no color, only shades of grey. The background can be either white or translucent. We first need to create a method used to draw this shape at any color. For simplicity's sake, we'll call this method IconAtColor:

Private Function IconAtColor(Icon As Picture, Fill As Color) As Picture
  Dim P As New Picture(Icon.Width,Icon.Height,32)
  P.Graphics.ForeColor = Fill
  P.Graphics.FillRect(0,0,P.Width,P.Height)
  P.Mask.Graphics.ClearRect(0,0,P.Width,P.Height)
  P.Mask.Graphics.DrawPicture(Icon,0,0)
  Return P
End Function

Next, the actual embossing. We'll start with the bottom layer, the drop shadow. In our effect, the drop shadow will be used to create a bottom-edge highlight. So we're going to create a picture and fill its contents with white. Purists will argue that a new picture starts white, and they'd be right, but I like to be explicit. By filling it with white, there is no doubt you have a white picture. Next, we're going to clear the mask. A mask defaults to filled black, but we want the opposite, so a simple ClearRect call will handle that. Into the mask, we're going to draw the icon twice. First, we're going to draw the icon using some shade of grey between black (opaque) and white (transparent). This creates the opacity of the shadow. In the example code, we're going to use 75% white (25% opaque, &cBFBFBF). The shadow must be drawn 1 pixel from the top. Then we draw the icon a second time at 100% white (&cFFFFFF) at 0 pixels from the top. This "cuts" the shadow away from where we will actually fill. We do this because the fill may be translucent, and we don't want the shadow to show through. Here's the code to what we just did:

Dim DropShadow As New Picture(Icon.Width,Icon.Height,32)
DropShadow.Graphics.ForeColor = &cFFFFFF
DropShadow.Graphics.FillRect(0,0,DropShadow.Width,DropShadow.Height)
DropShadow.Mask.Graphics.ClearRect(0,0,DropShadow.Width,DropShadow.Height)
DropShadow.Mask.Graphics.DrawPicture(Self.IconAtColor(Icon,&cBFBFBF),0,1)
DropShadow.Mask.Graphics.DrawPicture(Self.IconAtColor(Icon,&cFFFFFF),0,0)

Now we're going to create the fill layer. Because we want the fill to darken the background, we're going to fill a new picture with black, and once again, clear the mask. Then all we have to do is draw the icon into the mask, again at some color between black and white to describe the opacity. In our example, the fill will be 50% opaque (&c7F7F7F):

Dim Fill As New Picture(Icon.Width,Icon.Height,32)
Fill.Graphics.ForeColor = &c000000
Fill.Graphics.FillRect(0,0,Fill.Width,Fill.Height)
Fill.Mask.Graphics.ClearRect(0,0,Fill.Width,Fill.Height)
Fill.Mask.Graphics.DrawPicture(Self.IconAtColor(Icon,&c7F7F7F),0,0)

And of course, the final layer is the inner shadow. Like the fill, we want this layer to darken, so we fill a new picture with black, and like the other two layers, we clear the mask. Like the drop shadow, we need to draw the icon twice. Once for the opacity, and once to "cut" the shape away. In this example, the shadow is 75% opaque (&c7F7F7F). This time, the first drawing should be 0 pixels from the top, and the second drawing should be 1 pixel from the top.

Dim InnerShadow As New Picture(Icon.Width,Icon.Height,32)
InnerShadow.Graphics.ForeColor = &c000000
InnerShadow.Graphics.FillRect(0,0,InnerShadow.Width,InnerShadow.Height)
InnerShadow.Mask.Graphics.ClearRect(0,0,InnerShadow.Width,InnerShadow.Height)
InnerShadow.Mask.Graphics.DrawPicture(Self.IconAtColor(Icon,&c3F3F3F),0,0)
InnerShadow.Mask.Graphics.DrawPicture(Self.IconAtColor(Icon,&cFFFFFF),0,1)

And there we have it, the three layers needed to create the effect. Draw them all directly on top of each other, starting with the drop shadow, then the fill, and finishing with the inner shadow. You can adjust some of the numbers, such as the distances from the top to draw the icon on the drop shadow and inner shadow layers, and choose different colors to create different opacities. Your fill color does not need to be black. You can reverse the inner and drop shadows to create a raised effect. Experiment with this code to achieve new effects.

A sample project can be downloaded here which includes the artwork and code ready to run.

3 comments:

Jason said...

Very nice, and easily expanded upon. I'd probably turn this into a container control with properties for Icon and Caption, to be set in the Property List.

Any idea when the new IDE may show up? :D

Thom McGrath said...

Why yes, I do have an idea. But of course, I can't share it.

Jason said...

Hah! Next time I'll be sure to phrase myself more carefully. :)