User Tools

Site Tools


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:

        PROCdrawshadowedtext("Shadow", 0, xpos%, ypos%, colour%, 5, -5, 0.4)

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 “&bbggrr” (where “bb” is the amount of blue, “gg” the amount of green and “rr” 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 “rr”, “gg” or “bb” 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:

        text$ = "First line"+CHR$13+CHR$10+"Second line"
        PROCdrawshadowedtext(text$, 0, xpos%, ypos%, colour%, 5, -5, 0.4)

Here is the procedure. It requires Windows 98 or later (it will not run under Windows 95):

        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
            b = opacity*(P%!delta%AND&FFFFFF)/colr%
          P%?3 = 255*(f+b-f*b)
        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%
This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information
drawing_20text_20with_20a_20translucent_20dropshadow.txt · Last modified: 2018/04/13 15:40 by richardrussell