Drawing a Drop Shadow

The white text also needs highlighting, so let's try a drop shadow. Drop shadows are done by printing the text in black first, and then again in white, offset a little bit horizontally and vertically.

Since the window is just big enough for the message, you should use the window manager to make it a bit bigger so it can accommodate both the message and its shadow. Then go ahead and draw the drop shadow.

     | ?- win(Window),
          clear_window(Window),
          get_screen_attributes([black_pixel(Black),
                                 white_pixel(White)]),
          put_graphics_attributes(Window, [foreground(Black)]),
          draw_string(Window, 7, 27, 'Hello, world!!'),
          put_graphics_attributes(Window, [foreground(White)]),
          draw_string(Window, 2, 19, 'Hello, world!!').
     
     Window = window(2376568),
     Black = 1,
     White = 0
     

Note that we've determined both black_pixel/1 and white_pixel/1 of the screen at the same time here. You can put as many attributes in this list, and in fact in any attribute list, as you like. Other than that, this example is pretty straightforward. We drew the message first in black, then again in white five pixels to the left and eight pixels above the shadow.

The resulting displacement for the shadow seems a bit too much, at least vertically. The displacement should probably be relative to the size of the characters in the font, so let's find out how big the characters in the font are.

     | ?- win(Window),
          get_font_attributes(Window, [height(H), max_width(W)]).
     
     Window = window(2376568),
     H = 25,
     W = 23
     

The attribute height/1 is the nominal height of the font, that is, its declared height. Individual characters can be taller than this, but they usually are not. The attribute max_width/1 represents the width of the widest character in this font. Remember, different characters can have different widths. Notice that we've asked for the font attributes of a window. Once again, we take advantage of ProXL's ability to infer a font from a window.

Given these numbers, let's say that one fifth of the font's max_width and height are about the right horizontal and vertical offset for the drop shadow. So now recalculate the size of the window:

     | ?- win(Window),
          get_font_attributes(Window, [height(Fh), max_width(Fw)]),
          Xdisplacement is Fw//5,
          Ydisplacement is Fh//5,
          text_extents(Window, 'Hello, world!!', L, R, W, A, D),
          Width is W+Xdisplacement+4,
          Height is A+D+Ydisplacement+4,
          put_window_attributes(Window, [size(Width,Height)]).
     
     Window = window(2376568),
     Fh = 25,
     Fw = 23,
     Xdisplacement = 4,
     Ydisplacement = 5,
     L = 0,
     R = 145,
     W = 146,
     A = 17,
     D = 3,
     Width = 154,
     Height = 29
     

This looks a bit complicated, but is actually just a combination of things we've done before. First we find the font's max_width and height, as above, and compute the X and Y displacement to be one fifth of those values. Then we find the size of the string, as before. The width and height of the window should be just as before, with the X and Y displacement added in. Finally, we resize the window. Not surprisingly, we lose the message again; let's put it back.

     | ?- win(Window),
          Xdisplacement = 4,
          Ydisplacement = 5,
          A = 20,
          get_screen_attributes([black_pixel(Black),
                     white_pixel(White)]),
          X0 is 2+Xdisplacement,
          Y0 is 2+A+Ydisplacement,
          put_graphics_attributes(Window, [foreground(Black)]),
          draw_string(Window, X0, Y0, 'Hello, world!!'),
          X1 is 2,
          Y1 is 2+A,
          put_graphics_attributes(Window, [foreground(White)]),
          draw_string(Window, X1, Y1, 'Hello, world!!').
     
     Window = window(2376568),
     Xdisplacement = 4,
     Ydisplacement = 5,
     A = 20,
     Black = 1,
     White = 0,
     X0 = 6,
     Y0 = 27,
     X1 = 2,
     Y1 = 22
     

Xdisplacement, Ydisplacement, and A come from previous goals, so we don't have to recompute them. We're also assuming the the left bearing of our message is 0. So the X position of our message is just 2, and the Y position is 2 plus the ascent of the message (A). And the X and Y position of the shadow message is offset from this by the X and Y displacement. The rest of this example is just like what we've seen before.