Question

Windows XP draws icon text with a nice shadow, which helps to read the text on various backgrounds. The font color is white and the shadow is black (if desktop background is white) or there is no shadow at all (if desktop background is black).

So there are two sub-tasks:

  1. How the shadow gets drawn? It is not a simple x,y offset of the text; the shadow looks more like a blur to me.

  2. How to make shadow to behave the way it becomes more visible on white backgrounds and less visible on dark?

I need a solution for GDI (not GDI+).

Was it helpful?

OTHER TIPS

Inspired by Chris Becke's answer in this thread

If you need quick and not-so dirty solution, you may also want to do the following:

Draw a text on a black bitmap then alpha blend it to the primary hdc but: shift the destination rectangle -1 .. 2 by x and -1 .. 3 by y (i.e. several blends in a loop). To achieve shadow fading effect modify SourceConstantAlpha accordingly for outer blends (|x| > 1 or |y| > 1).

This is a rough method, feel free to experiment with details.

I am not sure about performance and visual quality aspects of such solution but it may be sufficient in certain cases.

Also see the DrawShadowText function here: Writing Transparent Text on Image

The function prototype is this: procedure DrawShadowText(aCanvas: TCanvas; CONST Text: string; CONST X, Y, Opacity: Integer; TextColor, ShadowColor: TColor);
It is ready to be used.

Yet another DrawShadowText function, this time based on MS Win API. Needs Windows Vista and up.

Source: https://www.delphipraxis.net/66678-drawshadowtext-delphi-commctrl-h-comctl32-dll-3.html

Here is the english version:

{-------------------------------------------------------------------------------------------------------------
  Unit ShadowText by Matthias G. aka turboPASCAL
  Version 1.0 (VCL)
  Draws text with shadow (This unit provides the function DrawShadowText from the ComCtl32.dll)

  Minimum req:
     Windows Vista
     Comctl32.dll v6

     If running under XP or without Windows XP Manifest, a substitute function is used to display the shadow.
     From the user manual: To use DrawShadowText, specify Comctl32.dll version 6 in the manifest. For more information on manifests, see Enabling Visual Styles.

  Src: https://www.delphipraxis.net/66678-drawshadowtext-delphi-commctrl-h-comctl32-dll-3.html
  API docs: https://docs.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-drawshadowtext
  Tester: c:\Myprojects\Project Testers\gr cGraphUtil.pas\
-------------------------------------------------------------------------------------------------------------}

UNIT ShadowText;

INTERFACE

USES
  Windows, SysUtils, System.Classes, Graphics;

VAR
   // determines whether the alternative shadow should be used if the function from ComCtl32.dll is not available
   UseLQShadow : Boolean = True;

  // Output of a text with shadow by specifying X and Y coordinates
  function DrawShadowText(ACanvas: TCanvas; x, y: Integer; AText: string; TextColor, ShadowColor: TColor; ShadowSpaceX, ShadowSpaceY: Integer): Integer;

  // Output of a text with shadow over a TRect structure with specification of the text formatting (text flags see Delphi help "DrawText")
  function DrawShadowTextR(ACanvas: TCanvas; TextRect: TRect; x, y: Integer; AText: string; TextColor, ShadowColor: TColor; ShadowSpaceX, ShadowSpaceY: Integer; TextFlags: DWord): Integer;


IMPLEMENTATION

TYPE
  TDrawShadowTextI = function(hdc: HDC;
    pszText: LPCWSTR;
    cch: UINT;
    const pRect: PRect;
    dwFlags: DWORD;
    crText: COLORREF;
    crShadow: COLORREF;
    ixOffset: Integer;
    iyOffset: Integer): Integer; stdcall;

VAR
  hComCtl32DLL: THandle = 0;
  DrawShadowTextI: TDrawShadowTextI;
  OldCanvasColor: TColor;
  OldBkModeColor: Integer;


// DrawShadowText as declared in ComCtl32.dll ( requires WindowsXP-Manifest! )
// function DrawShadowText_(hdc: HDC; pszText: LPCWSTR; cch: UINT; const pRect: PRect; dwFlags: DWORD; crText: COLORREF; crShadow: COLORREF; ixOffset: Integer; iyOffset: Integer): Integer; stdcall; external 'ComCtl32.dll' name 'DrawShadowText';



// Determines whether a Windows version from WinXP on is available.
function IsWindowsXPAndUp: Boolean;
begin
  Result := ((Win32MajorVersion = 5) and (Win32MinorVersion >= 1)) or (Win32MajorVersion > 5);
end;


// DrawShadowTextL (L - Low Quality)
function DrawShadowTextL(ACanvas: TCanvas; TextRect: TRect; AText: string;
  TextColor, ShadowColor: TColor; ShadowSpaceX,
  ShadowSpaceY: Integer; TextFlags: DWORD): Integer;
begin
  OldBkModeColor := SetBKMode(ACanvas.Handle, TRANSPARENT);
  OldCanvasColor := ACanvas.Font.Color;

  if UseLQShadow
  then
   begin
    inc(TextRect.Left, ShadowSpaceX);
    inc(TextRect.Top, ShadowSpaceY);
    inc(TextRect.Right, ShadowSpaceX);
    inc(TextRect.Bottom, ShadowSpaceY);

    ACanvas.Font.Color := ShadowColor;

    Result := DrawText(ACanvas.Handle, PChar(AText), length(AText), TextRect, TextFlags);

    dec(TextRect.Left, ShadowSpaceX);
    dec(TextRect.Top, ShadowSpaceY);
    dec(TextRect.Right, ShadowSpaceX);
    dec(TextRect.Bottom, ShadowSpaceY);
   end
  else
    Result := 1;

  ACanvas.Font.Color := TextColor;
  Result := Result AND DrawText(ACanvas.Handle, PChar(AText), length(AText), TextRect, TextFlags);

  ACanvas.Font.Color := OldCanvasColor;
  SetBKMode(ACanvas.Handle, OldBkModeColor);
end;


// DrawShadowText: für einfache Ausgabe
function DrawShadowText(ACanvas: TCanvas; x, y: Integer; AText: string; TextColor, ShadowColor: TColor; ShadowSpaceX, ShadowSpaceY: Integer): Integer;
VAR TextRect: TRect;
begin
  TextRect := RECT(x, y, x + ACanvas.TextWidth(AText), y + ACanvas.TextHeight(AText));

  if @DrawShadowTextI <> nil
  then Result := DrawShadowTextI(ACanvas.Handle, PWideChar(WideString(AText)), length(AText), @TextRect, 0, COLORREF(TextColor), COLORREF(ShadowColor), ShadowSpaceX, ShadowSpaceY)
  else Result := DrawShadowTextL(ACanvas, TextRect, AText, TextColor, ShadowColor, ShadowSpaceX, ShadowSpaceY, 0);
end;


// DrawShadowTextR: for formatted output (R - Text[R]ect)
function DrawShadowTextR(ACanvas: TCanvas; TextRect: TRect; x, y: Integer; AText: string; TextColor, ShadowColor: TColor; ShadowSpaceX, ShadowSpaceY: Integer; TextFlags: DWord): Integer;
begin
  if @DrawShadowTextI <> NIL
  then Result := DrawShadowTextI(ACanvas.Handle, PWideChar(WideString(AText)), length(AText), @TextRect, TextFlags, COLORREF(TextColor), COLORREF(ShadowColor), ShadowSpaceX, ShadowSpaceY)
  else Result := DrawShadowTextL(ACanvas, TextRect, AText, TextColor, ShadowColor, ShadowSpaceX, ShadowSpaceY, TextFlags);
end;



initialization
  if IsWindowsXPAndUp then
   begin
    hComCtl32DLL := LoadLibrary('ComCtl32.dll');
    @DrawShadowTextI := GetProcAddress(hComCtl32DLL, 'DrawShadowText');
   end;

finalization
  if hComCtl32DLL <> 0
  then FreeLibrary(hComCtl32DLL);

end.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top