The glow effect over a coloured background. What if you want non-white glowing background? |
I asked a question on Stack Overflow to see if it was possible to change the background glow colour – after all, the text colour and glow size are both options in the DTTOPTS structure; perhaps there was a way to make the border or shadow options affect the glow. But no-one answered, and from my own research / experiments it appears it’s not possible through the API.
Instead, I’ve coded a method to do this into my TTransparentCanvas open-source library.
If you haven’t seen the previous blog posts about it, TTransparentCanvas is a class (or set of classes) to draw transparent, blended shapes and text onto a bitmap or device context using TCanvas-like functions and normal VCL objects like TPen, TBrush, TFont etc. Shapes and text can be composed and blended over each other, and the final result then blended over an existing image. The code aims to be easy to pick up and use if you are familiar with drawing with TCanvas, and is implemented with pure GDI – that is, no GDI+ or other external libraries are required.
How it’s done
The following assumes you have read my previous articles about drawing transparent graphics with pure GDI (Part 1, Part 1½, and Part 2.) Specifically, you should know what premultiplied alpha is and be familiar with the record I use to represent a single transparent pixel, TQuadColor.
A quick explanatory note if you didn’t go and meticulously pore over those links – which of course you did :) – is that premultiplied alpha changes the RGB representations of a pixel from 0-255 to that value scaled, or pre-multiplied, by the alpha channel, so with an alpha of 64, a full-white (normally (255, 255, 255)) pixel will have RGB values of (64, 64, 64). A grey pixel of (128, 128, 128) with an alpha value of 64 would have premultiplied RGB values of (32, 32, 32), which still represent the same colour. It’s one of the steps done when blending transparent images together, and storing bitmaps in this format saves some calculations. TQuadColor is a simple record representing a 32-bit pixel as union or variant record of four bytes in ABGR order or a 32-bit Cardinal, along with some helpful methods.
TTransparentCanvas draws every shape or text onto an intermediate bitmap. This allows processing of the alpha of that shape before the result is blended onto the ‘result’ bitmap. This design allows every item drawn to have different transparencies, but also allows other intermediate processing before the drawn item is composed onto the final alpha-aware bitmap result.
It’s this intermediate step we can take advantage of. If there’s no way to change the background colour in the API, we can do it, effectively, as a post-processing stage by tinting the glowing pixels.
Examining the bitmap
Examining the glowing pixels in the debugger shows that the white transparent pixels are represented as pure white until they are completely transparent, but are of course in premultiplied alpha form. Thus, a pixel right at the edge of the glow may have the ABGR value (2, 2, 2, 2): that is, an alpha of 2, with a full-100%-white value (normally (255, 255, 255) premultiplied by the alpha to give each channel a value of 2.
What about the text? If drawn as black text, they will have full-alpha but black pixels, e.g. ABGR (255, 0, 0, 0.) However, text is anti-aliased: most parts of the text will not be fully black, but a colour between black and white, and it’s not even guaranteed the text pixels will have full alpha.
Tinting these pixels
I initially considered tinting the pixels by drawing black text on the white glow, figuring out the relative “whiteness” and “blackness” of each pixel, and using that to interpolate between the user-specified glow and text colours. However, not only is this probably more processing than is required but sub-pixel antialiasing might result in non-uniformly-grey pixels.
A simpler solution is to tint all pixels, both glow and text, to the one colour, and then draw the text over again. Visually, this gives almost the same result with less per-pixel processing – a win.
(Note: the following quotes heavily from my Stack Overflow answer explaining the same material.)
To tint, one can ignore the existing colour of the pixel and instead set any non-zero-alpha pixels to a premultiplied alpha value using the user-specified background colour, based on the existing alpha of the pixel. Loop through your temporary bitmap and set the colour using the existing alpha as an intensity:
1 2 3 4 5 6 7 | // 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:
1 2 3 4 5 6 7 8 9 10 | 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 to have the glow effect lessened by being partially transparent at its strongest point if you pass in a non-full-alpha color to tint with. The example application has a slider allowing you to see this in action.
SetAlpha is an existing method that converts to premultiplied alpha:
1 2 3 4 5 6 7 | 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; |
Example of the result
What does this give you?
This image is the text ‘Test glowing text with background color’ tinted clLime:
A clLime-tinted glow |
…and with text drawn over the glow. |
clRed text with a clSkyBlue glow |
And of course, since TTransparentCanvas can draw to glass as easily as it can to a normal bitmap or DC, you can draw anything else on the title bar too.
Download
TTransparentCanvas is a MPL-licensed open source project hosted on Google Code. Get the source here. If you use it, have feature requests (such as drawing more shapes than are implemented so far) or if you have patches, please let me know. It has been tested with Delphi 2010, XE2 and XE4 and works when compiled to either 32 and 64-bit. Have fun!