Managed C++: Use GDI+ to Render Reflected Text, Page 2
You need the reflected text to be drawn starting below the original text. Although you got the text's height from the Measurestring method, that value includes spacing for descenders and whitespace. In this case, you want only the height from the baseline. This involves getting the cell ascent of the font family being used. (You can retrieve the font family from the Font object via it's FontFamily property.) However, the cell ascent value is in design units, which must be converted to pixels and then scaled for the font size. After much testing and research, this is the best formula I've found for doing this, where the final value (represented by cy) is the height of the text to be drawn).
This formula is optimized for characters that are drawn above the baseline. If you use characters that are drawn below the baseline (i.e., characters with descents), you'll see overlap in the descending parts of the characters between the original and reflected text. To remedy this, tweak the calculation by incorporating the cell descent (retrieved via the FontFamily::GetCellDescent method):
int lineAscent = font->FontFamily->GetCellAscent(font->Style); int lineSpacing = font->FontFamily->GetLineSpacing(font->Style); Single lineHeight = font->GetHeight(g); Single cy = lineHeight * lineAscent / lineSpacing;
Note that the x and y positions are at 0 and 0. This is due to your having called TranslateTransform as described earlier:
g->DrawString(textToDisplay, font, Brushes::Black, 0, 0);
To reflect text, you use the ScaleTransform method with a value of -1 in order to invert the text without distorting it:
Once the scaling factor has been established, you simply draw the reflected text. Note the y value is set to the negative of the text height multiplied by two so that it displays below the original text:
g->DrawString(textToDisplay, font, Brushes::Gray, 0, -(cy*2));
Saving and Restoring the GraphicsStateThe previous section showed how to use the ScaleTransform method to produce reflected text. In terms of the order in which the text was drawn, the code first drew the original string, scaled the text in the exact opposite direction (in the y direction), and then drew the text's "reflection". However, what if you needed to perform these steps in reverse? In other words, draw the reflection first. You could scale the text twice—once to draw the reflection and then back to the normal scaling. Or you could save the current state of the Graphics object, set the scaling factor, and then when finished drawing on that scale, restore the original graphics state to draw the normal text. Here's how that would look:
// Save the graphics state GraphicsState* prevGraphicsState = g->Save(); // Scale to draw reflected text g->ScaleTransform(1.0F, -1.0F); // Draw the reflected text // Restore the saved graphics state g->Restore(prevGraphicsState); // Draw the normal text
Download the Code
To download the accompanying source code for the demo, click here.
About the Author
The founder of the Archer Consulting Group (ACG), Tom Archer has been the project lead on three award-winning applications and is a best-selling author of 10 programming books as well as countless magazine and online articles.