Although displaying images is very simple, it still pays to have some understanding of what is going on behind the scenes.
The-most important point to understand about images is that they are always rectangular, That is not just a convenience, it is because of the underlying technology, AIl modern graphics cards have hardware built in that can efficiently copy blocks of pixels from one area of memory to another area of memory, provided that the block of pixels represents a rectangular region. This hardware-accelerated operation can occur virtually as one single operation, and as such, is extremely fast. Indeed, it is that key to modern high-performance graphics. This operation is known as a bitmap block transfer (or BiIBlt), Graphics DrawlmageUnscaled () internally uses a BitBlt, which is why you can see a huge image, perhaps containing as many, as a million pixels, appearing afmost instantly. If the computer had to copy the image to the screen pixel by pixel, you would see the image gradual! y being drawn over a period of up to several Seconds.
BitBlts are very efficient therefore, almost all drawing and manipulation of images is carried out using them, Even some editing of images will be done by manipulating portions of Images With BitBlts between DCs that represent areas of memory. In the days of GDl, the Windows 32 API function BitBlt () was arguably the most important and-widely used function for image manipulation, although with GDI+,. the BitBlt operations are largely hidden by the GDl+ object model.
It’s not possible to BitBlt areas of images that are not rectangular, although Similar effects can be easily simulated. One way is mark a certain color as transparent for the purpose of a BitBlt, so that areas of that color source in image will not overwrite the existing color of the corresponding pixel in the destination device.
It is also possible to specify that in the process of a BitBlt, each pixel of the resultant image will be formed by some logical operation (such as a bitwise AND) on the colors of that pixel in the source image, and in the destination device before the BitBlt, Such operations are supported by hardware acceleration and can be used to give a variety of subtle effects, Note that the Graphics object implements another method, Drawlmage (). This is similar to Draw image Unscaled () but comes in a large number of overload that allow you to specify more complex forms of BitBlt to be used in the drawing process.
Drawlmage() i also allows you to draw (using BitBlt) only a specified part of the image, or to perform certain other operations on it,such as scaling it (expanding or reducing it in size) as it is drawn.
We have chosen to cover the very important topic of displaying text late in this chapter because drawing text to the screen is (in general) more complex than drawing simple graphics. Although displaying a line or two of text when you don’t care about the appearance is extremely easy (it takes one single call to the Graphics- DrawString () method), if you are trying to display a document that has a fair amount of text in it, then you will rapidly find that things become a lot more complex. This is for two reasons:
- If you are concerned about getting the appearance just right, then you must understand fonts, Whereas shape drawing requires brushes and pens as helper objects, the process of drawing text requires fonts as helper objects. Moreover, understanding fonts is not a trivial undertaking.
- Text needs to be very carefully laid out in the window Users generally expect words to follow naturally from one word to another and to be lined up With clear spaces in between, Doing that is harder than you might think, For starters, you do not usually know in advance how much space on the screen a word is going to take up, That has to be calculated (using the Graphics . MeasureString () method) In addition, the space a word occupies on the screen affects where in the document every subsequent word is placed, If your application does any line wrapping, then it will need to assess word sizes carefully before deciding where to place the line break, The next time you run Microsoft Word, look carefully at the way Word is continually repositioning text as you do your work; there is a lot of complex processing going on there. Chances are that any GDl+ application you work on will not be nearly as complex as Word, However if you need to display any text, many of the same considerations apply.
In short, high-quality text processing is tricky to get right. However, putting a line of text on the screen, assuming that you know the font and where you want it to go, is actually very simple, Therefore the next section presents a quick example that shows you how to display some text, followed by a short review of the principles of fonts and font families and a more realistic (and involved) text-processing example, CapsEditor.
Simple Text Example
This example, DisplayText, is your usual Windows Forms effort, This time you override On Paint () and add member fields as follows:
Figure 33-15 shows the result of running this example.
The example demonstrates the use of the Graphics. DrawString () method to draw items of text, The method DrawString () comes in a number of overloads, three of which are demonstrated here, The different overloads require parameters that indicate the text to be displayed, the font that the string should be drawn in, and the brush that should be used to construct the various lines and curves that make up each character of text. A few alternatives exist for the remaining parameters. In general, however, it is possible to specify either a Point (or equivalently, two numbers) or a Rectangle.
If you specify a Point, then the text will start with its top-left comer at that Point and simply stretch out to the right, If you specify a Rectangle, then the Graphics instance will lay out the string inside that rectangle, If the text does not fit within the boundaries of the rectangle, it will be cut off (see the fourth line of text in Figure 33-15). Passing a rectangle to DrawString () means that the drawing process will take longer because DrawString () will need to figure out where to put line breaks, but the result may look nicer – provided the string fits in the rectangle!
This example also shows a few ways to construct fonts. You always need to include the name of the font and its size (height), You can also optionally pass in various styles that modify how the text is to be drawn (bold, underline, and so on).
Fonts and Font Families
A font describes exactly how each letter should be displayed. Selection of the appropriate font and providing a reasonable variety of fonts within a document are important factors in improving readability.
Most people, if asked to name a font, might mention Arial or Times New Roman (if they are Windows users) or Times or Helvetica (if they are Mac as users), In fact these are not fonts at all- they are font families. The font family tells you, in generic terms, the visual style of the text and is a key factor in the overall appearance of your application, Most of us recognize the styles of the most common font ,families, even if we are not consciously aware of it.
An actual font would be something like Anal 9-point italic. In other words, the size and other modifications to the text are specified as well as the font family, These modifications might include whether text is bold, italic, underlined. or displayed in SMALL CAPS or as a subscript this is technically referred to as the style, although in some ways, the term is misleading because the visual appearance is determined as much by the font family.
The size of the text is measured by specifying its height. The height is measured in points – a traditional unit that represents 172 of an inch (0.351 mm). So letters in a 10-point font are roughly 1/7″, or 3.5 mm high, However, you will not get seven lines of 10-point text into one inch of vertical screen or paper space, as you need to allow for the spacing between the lines as well.
Strictly speaking, measuring the height is not quite as simple as that because there are several different heights that you must consider, For example there is the height of tall letters such as the A or F (this is the measurement that we are referring to when we talk about the height), the additional height occupied by any accents on letters such as A or N(the internal leading), and the extra height below the baseline needed for the tails of letters such as y and g (the descent), However, for this chapter we will not worry about that. Once you specify the font family and the main height, these subsidiary heights are determined automatically.
When you are dealing with fonts, you might also encounter some other terms commonly used to describe certain font families:
- Serif font families have feet at the ends of many of the lines that make up the characters (these ticks are known as serifs), Times New Roman is a classic example of this.
- Sans serif font families, by contrast, do not have these feet. Good examples of sans serif fonts are Arial and Verdana, The lack of feet often gives text a blunt, in-your-face appearance, so sans serif fonts are often used for important text.
- A True Type font family is one that is defined by expressing the shapes of the curves that make up the characters in a precise mathematical manner, This means that the same definition can be used to calculate how to draw fonts of any size within the family, These days, virtually all the fonts you might use are TrueType fonts. Some older font families from the days of Windows 3.1 were defined by individually specifying the bitmap for each character separately for each font size, but the use of these fonts is now discouraged.
Microsoft has provided two main classes that you need to deal with when selecting or manipulating fonts:
- System. Drawing. Font
- System. Drawing. FontFamily
You have already seen the main use of the Font class. When you want to draw text, you instantiate an instance of Font and pass it to the DrawString () method to indicate how the text should be drawn, A FontFamily instance is used to represent a family of fonts.
You can use the FontFamily class, for example, if you know you want a font of a particular type (serif, sans serif, or true type), but do not have a preference for which font. The static properties GenericSerif,GenericSansSerif, and GenericMohospace return default fonts that satisfy these criteria.
FontFamily sansSerifFont = FontFamily.GenericSansSerif;
However, if you are writing a professional application, then you will want to choose your font in a more sophisticated way. Most likely, you will implement your drawing code so that it checks the font families available and selects the appropriate one, perhaps by taking the first available one on a list of preferred fonts, Moreover, if you want your application to be very user-friendly, then the first choice on the list will probably be the one that users selected the last time they ran your software, Usually, if you are dealing with the most popular font families, such as Arial and Times New Roman, you will be safe. However, if you do try to display text using a font that does not exist, the results aren’t always predictable. You are quite likely to find that Windows just substitutes the standard system font, which is very easy for the system to draw, but that it does not look very pleasant – and if it does appear in your document, then it is likely to give the impression of software that is of poor quality.
You can find out what fonts are available on your system using a class called
InstalledFontCollection, which is in the System. Drawing. Text namespace. This class implements a property, Families, which is an array of all the fonts that are available to use on your system:
Example: Enumerating Font Families
In this section, you work through a quick example, EnumFontFamilies, which lists all the font families available on the system and illustrates them by displaying the name of each family using an appropriate font (the 12-point regular version of that font family).
Figure 33-16 shows the result of running EnumFontFamilies.
Of course, the results that you get will depend on the fonts you have installed on your computer.
For this example, you create a standard C# Windows application, EnumFontFamilies, You start by adding an extra namespace to be searched, You will be using the InstalledFontCollection class, which is defined in System. Drawing . Text.
You then add the following constant to the Forml class:
private const int margin = 10;
margin is the size of the left and top margin between the text and the edge of the document – it stops the text from appearing right at the edge of the client area.
This is designed as a quick-and-easy way of showing off font families; therefore, the code is crude and in many instances does not do things the way you ought to in a real application, For example, here you hard-code an estimated value for the document size of (200,1500)and set the AutoScrollMinSize property to this value using the Visual Studio 2008 properties window, Typically you would have to examine the text to be displayed to work out the document size, You do that in the next section.
Here is the On Paint () method:
In this code, you start by using an InstalledFontCollection object to obtain an array that contains details of all the available font families, For each family, you instantiate a 12-point Font, You use a simple constructor for Font – there are many more that allow additional options to be specified. The constructor takes two parameters, the name of the family and the size of the font:
Font f = new Font (family.Name, 12);
This constructor builds a font that has the regular style. To be on the safe side, however, you first check that this style is available for each font family before attempting to display anything using that font, This is done using the FontFamily, is Style Available () method. This check is important because not all fonts are available in all styles:
if (family isStyleAvailable(FontStyle.Regular))
FontFamily is StyleAvailable () takes one parameter, a FontStyle enumeration, This enumeration contains a number of flags that might be combined with the bitwise or operator, The possible flags are Bold, Italic, Regular, Strikeout, and Underline.
Finally, note that you use a property of the Font class, Height, which returns the height needed to display text of that font, to work out the line spacing:
Font f = new Font(family.Name, 12);
Point topLeftCorner = new Point(margin, verticalCoordinate);
verticalCoordinate += f.Height;
Again, to keep things simple, this version of On Paint () reveals some bad programming practices, For example, you have not bothered to check what area of the document actually needs drawing – you just tried to display everything, Also, instantiating a Font is, as remarked earlier, a computationally intensive process, so you really ought to save the fonts rather than instantiating new copies every time On Paint () is called. Because of the way the code has been designed, you might note that this example actually takes a noticeable amount of time to paint itself. To try to conserve memory and help the garbage collector you do, however, call Dispose () on each font instance after you have finished with it, If you did not, after 10 or 20 paint operations, there would be a lot of wasted memory storing fonts that are no longer needed.