Drawing+text+with+a+translucent+dropshadow

//by Richard Russell, October 2007//

It is straightforward to draw text with an //opaque// dropshadow: you simply draw the 'shadow' text first (probably in a suitable shade of grey) then draw the foreground text on top, offset by a few pixels, in whatever colour you want. The effect can be acceptable, particularly when drawn on a uniformly-coloured background, but isn't very realistic when drawn over something like a picture. Ideally in that case you should be able to see the picture 'through' the shadow, in other words the shadow should be //translucent//.

The **PROCdrawshadowedtext** procedure listed below allows you to draw text with a realistic translucent dropshadow:



Note that you can see the clouds through the shadow, and the shadow is 'darker' over the hillside than it is over the sky. When run under Windows XP or Windows Vista both the text and the dropshadow are antialiased (if your display properties are set appropriately).

To display text similar to that shown above you would call **PROCdrawshadowedtext** as follows:

code format="bb4w" PROCdrawshadowedtext("Shadow", 0, xpos%, ypos%, colour%, 5, -5, 0.4) code Here **xpos%** and **ypos%** are the position in which the text should be drawn (in BBC BASIC graphics coordinates) and **colour%** is the required foreground text colour represented by the hexadecimal value (where  is the amount of blue,  the amount of green and  the amount of red, in each case in the range 00 to FF). **colour%** must not be zero (black); if it is you will receive a //Division by zero// error. To ensure that the dropshadow is effectively antialiased one of, or  must be at least 32 (hex 20).

The final three parameters are the horizontal and vertical //offsets// between the text and the shadow (in pixels), and the //opacity// of the shadow from 0.0 (transparent) to 1.0 (opaque). In the normal case of the dropshadow appearing to the //right// and //below// the text, the horizontal (x) offset should be positive and the vertical (y) offset negative, as in the example.

The second **flags** parameter may be zero or include one or more of the folowing values:


 * DT_EXPANDTABS (&40) Expands tab characters; the default column width is 8.
 * DT_NOPREFIX (&800) Disables special processing of the **&** character.

The text can consist of two or more lines, separated by CRLF (**CHR$13+CHR$10**) sequences: code format="bb4w" text$ = "First line"+CHR$13+CHR$10+"Second line" PROCdrawshadowedtext(text$, 0, xpos%, ypos%, colour%, 5, -5, 0.4) code Here is the procedure. It requires Windows 98 or later (it will not run under Windows 95):

code format="bb4w" DEF PROCdrawshadowedtext(text$,flags%,xpos%,ypos%,colr%,xoff%,yoff%,opacity) LOCAL msimg32%, hang%, hdc%, hbm%, old%, bits%, delta%, max%, P%, f, b     LOCAL rect{}, bmi{}, abc{} SYS "LoadLibrary", "MSIMG32.DLL" TO msimg32% SYS "GetProcAddress", msimg32%, "AlphaBlend" TO `AlphaBlend` _DT_NOCLIP = 256 _DT_CALCRECT = 1024 REM. Convert BASIC coordinates to GDI coordinates: xpos% = (xpos%+@vdu%!0) DIV 2 : ypos% = @vdu%!212-1-(ypos%+@vdu%!4) DIV 2 REM. Calculate size of rectangle required: DIM rect{l%,t%,r%,b%} SYS "DrawText", @memhdc%, text$, LEN(text$), rect{}, flags% OR _DT_CALCRECT rect.r% += ABS(xoff%) rect.b% += ABS(yoff%) REM. Adjust rectangle width for italic text overhang: DIM abc{(255)abcA%, abcB%, abcC%} SYS "GetCharABCWidths", @memhdc%, 0, 255, abc{(0)} hang% = abc{(ASCRIGHT$(text$))}.abcC% : IF hang% < 0 rect.r% -= hang% hang% = abc{(ASCtext$)}.abcA% : IF hang% < 0 rect.r% -= hang% REM. Create a 32-bpp off-screen bitmap: DIM bmi{Size%, Width%, Height%, Planes{l&,h&}, BitCount{l&,h&}, \ \      Compression%, SizeImage%, XPelsPerMeter%, YPelsPerMeter%, \ \      ClrUsed%, biClrImportant%} bmi.Size% = DIM(bmi{}) bmi.Width% = rect.r%     bmi.Height% = rect.b%      bmi.Planes.l& = 1 bmi.BitCount.l& = 32 SYS "CreateCompatibleDC", @memhdc% TO hdc% SYS "CreateDIBSection", @memhdc%, bmi{}, 0, ^bits%, 0, 0 TO hbm% SYS "SelectObject", hdc%, hbm% TO old% SYS "SelectObject", hdc%, @vdu%!56 SYS "SetBkColor", hdc%, 0 SYS "SetTextColor", hdc%, colr% REM. Draw the text string into the bitmap: IF hang% < 0 rect.l% -= hang% : xpos% += hang% IF xoff% < 0 rect.l% -= xoff% : xpos% += xoff% IF yoff% > 0 rect.t% += yoff% : ypos% -= yoff% SYS "DrawText", hdc%, text$, LEN(text$), rect{}, flags% OR _DT_NOCLIP rect.l% = 0 : rect.t% = 0 REM. Create the translucent drop-shadow: delta% = -(rect.r%*yoff% + xoff%) * 4 max% = bits% + rect.b%*rect.r%*4 - delta% SWAP ?^colr%,?(^colr%+2) FOR P% = bits% TO bits%+rect.r%*rect.b%*4-4 STEP 4 f = !P%/colr% IF P% < bits%-delta% OR P% >= max% THEN b = 0 ELSE b = opacity*(P%!delta%AND&FFFFFF)/colr% ENDIF P%?3 = 255*(f+b-f*b) NEXT REM. Blend the bitmap onto the output DC: SYS `AlphaBlend`, @memhdc%, xpos%, ypos%, rect.r%, rect.b%, \ \                hdc%, 0, 0, rect.r%, rect.b%, &1FF0000 SYS "OffsetRect", rect{}, xpos%, ypos% SYS "InvalidateRect", @hwnd%, rect{}, 0 REM. Clean up: SYS "SelectObject", hdc%, old% SYS "DeleteObject", hbm% SYS "DeleteDC", hdc% SYS "FreeLibrary", msimg32% ENDPROC code