سؤال

DrawThemeTextEx is the API to draw Windows Vista and 7's Aero text, that is, text with a glowing halo.

To change the text colour used when drawing with DrawThemeTextEx, you set the crText field of the DTTOPTS structure and specify DTT_TEXTCOLOR in the dwFlags field, to indicate the crText field contains a valid value. But the blurred glow effect behind the text always draws white. Setting the shadow or border colours, and the appropriate flags, has no effect on the glow colour. There is no apparent setting for the glow colour in the DTTOPTS structure.

Glowing text with a custom text color, but white glow color

Here you can see a custom text color (red) but it is still rendering with a white blurred glow behind the text. I would like, say, a black glow instead, since for light text colors it will make the text easier to read.

How do you change the color of this white halo / glow? Is there, for example, a Dwm* API that has an effect, or an undocumented flag?

هل كانت مفيدة؟

المحلول

The short answer is, you can't using the DrawThemeTextEx API: it only allows you to change the text color, not the background glow colour.

The long answer is that you can achieve the same effect by:

  • Drawing text with a glow;
  • Tinting the resulting image using the alpha as a colour intensity, to get a single-colour image of the text plus the glow;
  • Drawing the text over the top with no glow effect.

Black text with a red glow background

Example image showing the result of the technique

The second step, tinting the image, is the main one that requires explanation.

Draw black text with a glow to a temporary bitmap. You will then have an area with white and black colours (white is the only glow colour Windows renders), varying alpha, and in between black and white on the edge of the text antialiased pixels which may (due to the algorithm being used) be coloured slightly, ie not be a pure shade of grey.

Glowing text on a coloured background, zoomed in

Glowing text drawn over a coloured background, so you can see the white glow and the text antialiasing

There are two options. The first is to use the colour ("whiteness" vs "blackness") and tint to change the white area to the background colour and the black area to the text colour. This will work, but may give antialiasing errors, especially with coloured text, compared to how that text would have been rendered and antialiased by Windows. A better approach is to realise that the text will be overlaid, and antialiased to, the background glow anyway: tint everything (glow and text) to the one colour - whatever is 100% alpha text can be treated as 100% white glow underneath the text - then simply draw the text over again antialiasing to the background.

To tint is then quite simple. Pixels will have an alpha value and premultiplied alpha colour values - for example, ABGR of (2, 2, 2, 2) is an alpha of 2, and BGR of white, premultiplied by the alpha. Ignore the existing colour and set any non-zero alpha pixels to a premultiplied alpha value of the background colour, based on the existing alpha of the pixel.

Using a TQuadColor struct to represent four bytes of an alpha-aware 32-bit pixel, loop through your temporary bitmap and set the colour using the existing alpha as an intensity:

// PQuad is a pointer to the first pixel, a TQuadColor (see link, basically a packed struct of ABGR bytes)
for Loop := 0 to FWidth * FHeight - 1 do begin
  if PQuad.Alpha <> 0 then begin
    PQuad.SetFromColorMultAlpha(Color); // Sets the colour, and multiplies the alphas together
  end;
  Inc(PQuad);
end;

The key is PQuad.SetFromColorMultAlpha:

procedure TQuadColor.SetFromColorMultAlpha(const Color: TQuadColor);
var
  MultAlpha : Byte;
begin
  Red := Color.Red;
  Green := Color.Green;
  Blue := Color.Blue;
  MultAlpha := Round(Integer(Alpha) * Integer(Color.Alpha) / 255.0);
  SetAlpha(MultAlpha, MultAlpha / 255.0);
end;

This takes a quad colour (that is, alpha with RGB) and multiplies the two alphas together to get a resulting alpha. This lets you tint by a transparent colour. SetAlpha then converts to premultiplied alpha:

procedure TQuadColor.SetAlpha(const Transparency: Byte; const PreMult: Single);
begin
  Alpha := Transparency;
  Blue := Trunc(Blue * PreMult);
  Green := Trunc(Green * PreMult);
  Red := Trunc(Red * PreMult);
end;

The result is a tinted glow:

Tinted glow

Draw the text over the top (using the same API to keep the same text rendering) with no glow:

Tinted glow behind text

And you have glowing text with a coloured, non-white glow colour.

You can find full source code at my MPL-licensed TTransparentCanvas project on Google Code.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top