Home > Articles > Programming > Windows Programming

This chapter is from the book

Raster Graphics

We define raster graphics as those functions that operate on an array of pixels. The simplest raster operation is to fill a rectangle with a single color. On a display screen, this is one of the most common operations. If you study any window on any display screen, you are likely to see that the background is a single color, often white or sometimes gray. You can use three methods in the Graphics class to fill a rectangular area:

  • Clear: fills a window with a specified color

  • FillRectangle: fills a specified rectangle using a brush

  • FillRegion: fills a specified region using a brush

The Clear method accepts a single parameter, a structure of type Color.17 The other two methods accept a Brush18 object as the parameter that identifies the color to use to fill the area. Before we can fill an area with any of these functions, then, we need to know how to define colors and how to create brushes.

Specifying Colors

The most basic type of drawing attribute is color, yet few drawing methods accept colors directly as parameters. Most drawing methods require other drawing attributes that have a built-in color. For example, for filling areas, the color to use for filling the area is the color that is part of a brush. When drawing lines, the line color is the color that is part of a pen. Brushes are also used to specify text color. So even though color parameters are not directly used as parameters to methods, they are indirectly specified through a pen or brush.

There are three ways to specify a color in a .NET Compact Framework program:

  • With a system color

  • With a named color

  • With an RGB value

System colors are a set of colors used to draw the elements of the user interface. The use of system colors helps create consistency between different programs. For example, a given system might be set up with black text on a white background, or it could be set up with the opposite, white text on a black background. System colors are made available as properties of the SystemColors19 class. Available system colors are listed in Table 15.7 in the System Colors subsection.

Named colors provide access to colors that use human-readable names like Red, Green, Blue, White, and Black. There are also a large number of colors with less common names like SeaShell and PeachPuff. Whether or not you like all the names of the colors you encounter, they provide a way to specify colors that is easy to remember. Color names are made available as static properties of the Color structure.

When you specify a color using an RGB value, you specify an amount of red, an amount of green, and an amount of blue. Each is defined with a byte, meaning that values can range from 0 to 255. It is sometimes helpful to remember that RGB is a video-oriented color scheme often used for display screens and televisions. When the energy for all three colors is 0, the color you see is black; when all the energy for all three colors is at 100% (255), the resulting color is white.

System Colors

System colors let you connect your program's graphical output to current system settings. This allows a program to blend in with the current system configuration. On some platforms, users can change system colors from the system control panel (such as on desktop version of Microsoft Windows). Other platforms, like the Pocket PC, do not provide the user with an easy way to modify system color settings. A custom embedded smart device could easily be created with a unique system color scheme—say, to match corporate logo colors or to meet unique environmental requirements such as usage in low-light conditions or in sunlight. For all of these cases, the safest approach to selecting text colors involves using system colors.

System colors are available as read-only properties in the SystemColors class, the contents of which are summarized in Table 15.7. If you study this table, you may notice that several entries have the word Text in the name—such as ControlText and WindowText. There are, after all, many uses of text in the user interface. When specifying the color for drawing text, these system colors provide your best choice.

Table 15.7. System Colors in the .NET Compact Framework

Color

Description

ActiveBorder

Border color of a window when the window is active

ActiveCaption

Color of the background in the caption when a window is active

ActiveCaptionText

Color of the text in the caption when a window is active

AppWorkspace

Color of the unused area in an MDIa application

Control

Background color for a three-dimensional control

ControlDark

Color of the middle of the shadow for a three-dimensional control

ControlDarkDark

Color of the darkest shadow for a three-dimensional control

ControlLight

Color of the lightest element in a three-dimensional control

ControlLightLight

Color of the lightest edge for a three-dimensional control

ControlText

Color for drawing text in controls

Desktop

Color of the desktop background

GrayText

Color for drawing grayed text (e.g., for disabled controls)

Highlight

Background color of highlighted areas for menus, ListBox controls, and TextBox controls

HighlightText

Text color for highlighted text

HotTrack

Color of hot-tracked items

InactiveBorder

Border color of a top-level window when the window is inactive

InactiveCaption

Color of the background in the caption when a window is inactive

InactiveCaptionText

Color of the text in the caption when a window is inactive

Info

Background color of a tool tip

InfoText

Text color of a tool tip

Menu

Menu background color

MenuText

Menu text color

ScrollBar

Background color of a scroll bar

Window

Background color of a window

WindowFrame

Color of a window border

WindowText

Color of text in a window

In some cases, there is a pair of system color names: one with and one without the word Text in the name (e.g., Control and ControlText, Window and WindowText). One color in the pair defines a system color for text, and the other defines the color of the background. For example, when drawing in a form or dialog box, use the Window color for the background and WindowText for the text color. When you create a custom control, use the Control color for the control's background area and ControlText for the color of text drawn in a control. In the following code, the background is filled with the default window background color.

private void FormMain_Paint(
object sender, PaintEventArgs e)
{
   Graphics g = e.Graphics;
   g.Clear(SystemColors.Window);
}

Named Colors

The System.Drawing.Color class defines 142 named colors as read-only properties. The names include old favorites like Red, Green, Blue, Cyan, Magenta, Yellow, Brown, and Black. It also includes some new colors like AliceBlue, AntiqueWhite, Aqua, and Aquamarine. With names like Chocolate, Honeydew, and PapayaWhip, you may get hungry just picking a color. The named colors appear in Table 15.8.

The following code draws in the background of a window with the color PapayaWhip.

private void FormMain_Paint(
object sender, PaintEventArgs e)
{
   Graphics g = e.Graphics;
   g.Clear(Color.PapayaWhip);
}

Colors from RGB Values

The third approach that the .NET Compact Framework supports for specifying colors is to specify the three components—red, green, and blue—that make up a color. These three components are packed together into a 32-bit integer with one byte for each. The range for each component is from 0 to 255 (FF in hexadecimal). Table 15.9 summarizes color triplet values for common colors.

To create a color from an RGB triplet, use the Color.FromArgb method. There are two overloaded versions for this method. We find the following one easier to use.

public static Color FromArgb(
   int red,
   int green,
   int blue);

When you read the online documentation for this method, you see a reference to a fourth element in a color, the alpha value. The .NET Compact Framework does not support this, so you can safely ignore it. (In the desktop .NET Framework, the alpha value defines the transparency of a color, where a value of 0 is entirely transparent and 255 is entirely opaque. In a .NET Compact Framework program, all colors have an alpha value of 255, which means that all colors are 100% opaque.

Table 15.8. Named Colors in the .NET Compact Framework

Click to view table

Table 15.9. Color Triplets for Common Colors

Color Name

RGB Triplet (Decimal)

RGB Triplet (Hexadecimal)

Black

(0, 0, 0)

(0, 0, 0)

White

(255, 255, 255)

(0xFF, 0xFF, 0xFF)

Red

(255, 0, 0)

(0xFF, 0, 0)

Green

(0, 255, 0)

(0, 0xFF, 0)

Blue

(0, 0, 255)

(0, 0, 0xFF)

Cyan

(0, 255, 255)

(0, 0xFF, 0xFF)

Magenta

(255, 0, 255)

(0xFF, 0, 0xFF)

Yellow

(255, 255, 0)

(0xFF, 0xFF, 0)

Dark Gray

(68, 68, 68)

(0x44, 0x44, 0x44)

Medium Gray

(128, 128, 128)

(0x80, 0x80, 0x80)

Light Gray

(204, 204, 204)

(0xCC, 0xCC, 0xCC)

Knowing Black from White

We give programmers in our training classes the following tip to help remember the correct RGB for black (0, 0, 0) and white (255, 255, 255). The RGB color encoding is a light-based scheme, which in a computer CRT is often used to correlate the power to apply to the electron guns in the monitor. Turn the power off, which causes the power to go to zero, and you see black. When the power to the red, green, and blue is turned up all the way, you get white.

In Table 15.9, notice the different shades of gray. By studying the color triplets, you can observe what makes the color gray: equal parts of red, green, and blue.

The following code draws the window background using a light gray color.

private void FormMain_Paint(
object sender, PaintEventArgs e)
{
   Graphics g = e.Graphics;
   g.Clear(Color.FromArgb(204,204,204));
}

Creating Brushes

A brush specifies the color and pattern to use for area-filling methods, such as FillRectangle. The .NET Compact Framework does not support patterns in brushes, however, so a brush just specifies the color when filling areas. Brushes also specify the color to use when drawing text. The second parameter to the DrawString method, for example, is a brush.

The desktop .NET Framework supports five different kinds of brushes, including solid brushes, bitmap brushes, and hatch brushes. Windows CE supports solid brushes and bitmap brushes but not hatch brushes. And in the .NET Compact Framework, things are even simpler: only solid brushes are supported, by the SolidBrush20 class. This class has a single constructor, which takes a single parameter—Color. The SolidBrush constructor is defined as follows.

public SolidBrush(
   Color color);

With one constructor, it is natural to assume that there is one way to create a solid brush. But because there are three ways to define a color, there are three ways to create a brush:

  • Using the system colors

  • Using a named color

  • Using an RGB value

The following subsections discuss each of these briefly.

Creating Brushes with System Colors

The following code creates a brush from a system color. This brush is suitable for drawing text within a program's main form or in a dialog box.

Brush brText = new SolidBrush(SystemColors.WindowText);

The resulting brush provides the same color used by the operating system to draw text. You are not required to select this color, but in doing so you help ensure that your application fits into the color scheme established by the user.

There might be reasons to design your own color scheme. For example, when dealing with financial figures you might display positive numbers in black and display negative numbers in red. Or perhaps when displaying certain types of documents you could highlight keywords in different colors, in the same way that Visual Studio .NET highlights language keywords in blue. To handle these situations, you need to specify the brush color with one of the two other color-defining schemes: using either named colors or RGB colors.

Creating Brushes with Named Colors

Here are examples of creating brushes using named colors.

Brush brRed = new SolidBrush(Color.Red);
Brush brPaleGreen = new SolidBrush(Color.PaleGreen);
Brush brLightBlue = new SolidBrush(Color.LightBlue);

You might wonder where these color names come from. Some—like Red—are, of course, names for common colors. But when you read through the list of names, you see colors like AliceBlue, GhostWhite, and WhiteSmoke. The colors are sometimes called HTML Color Names because the more exotic names were first supported as color names in HTML by various browsers. Officially, however, HTML 4.0 includes only 16 color names, not the 140+ names defined in the Color structure.

Creating Brushes with RGB Values

To create a brush using an RGB value, call the FromArgb method in the Color class and pass the return value to the SolidBrush constructor. This method accepts three integer parameters, one each for red, green, and blue. Here is how to create three brushes from RGB triplets.

Brush brRed = new SolidBrush(Color.FromArgb(255, 0, 0));
Brush brGreen = new SolidBrush(Color.FromArgb(0, 255, 0));
Brush brBlue = new SolidBrush(Color.FromArgb(0, 0, 255));

Creating Bitmaps

A bitmap is a two-dimensional array of pixels with a fixed height and a fixed width. Bitmaps have many uses. One is to hold scanned images, such as a company logo. Photographs are stored as bitmaps, commonly in the highly compressed format of JPEG21 files. Bitmaps can be used to create interesting effects on a display screen, such as smooth scrolling and seamless animation.

Bitmaps are often used to store complex images that a program can easily draw in one or more locations by making a single method call. As useful as this approach can be, it is important to always remember that bitmaps require a lot of room—both in memory and in the file system. If you plan to include any bitmaps with your program, give some thought to the format of those bitmaps. We address this issue later in this chapter.

Bitmaps are sometimes referred to as off-screen-bitmaps because of the important role bitmaps have historically played in supporting display screen graphics. The Bitmaps on the Desktop sidebar discusses how bitmaps are used on desktop versions of Windows to support various user interface objects. That same support does not exist in Windows CE because of memory constraints. But bitmaps are still available to Windows CE programs for all of their other uses.

Bitmaps on the Desktop

On desktop versions of Windows, bitmaps support the quick appearance and disappearance of menus, dialog boxes, and various other user interface elements. For example, before a menu appears on the screen, a snapshot is taken of the area to be covered by the menu. When the menu disappears, the bitmap is used to redraw the affected part of the screen. This technique helps make the elements of the user interface appear and disappear very quickly. This technique is not employed in Windows CE because of the tight memory restrictions of mobile and embedded systems. But your program could use bitmaps in other ways to support the display of your program's user interface.

In our programming classes, we observe that programmers often get confused when first starting to work with bitmaps. The confusion seems to come from not grasping that bitmaps are inherently off-screen. Or it may arise from an understanding that display screens are supported by memory-mapped video devices and that the memory occupied by a bitmap must somehow be related to the memory used by the display adapter. After creating a bitmap and drawing into a bitmap, some programmers expect that bitmaps are going to appear somewhere on the screen. That does not happen, however, because bitmaps appear on a display screen only when your program explicitly causes them to appear.

Bitmaps: Drawing Surface or Drawing Object?

Bitmaps play two roles in every graphic library built for Microsoft Windows: (1) as drawing surfaces and (2) as drawing objects used to draw onto other surfaces. This is another reason why bitmaps can at first seem confusing for some programmers.

A bitmap is a drawing surface like other drawing surfaces. We say this because a program can obtain a Graphics object for a bitmap and then use the methods of that object to draw onto the surface of the bitmap. All of the drawing methods in the Graphics object are supported for bitmaps, including text, raster, and vector drawing methods.

The second role played by bitmaps is that of a drawing object. Like other drawing objects, such as pens, brushes, and fonts, a bitmap holds a pattern that can be applied to a drawing surface. Each drawing object has its particular uses, and each produces a different effect, as determined by the various drawing methods. The .NET Compact Framework supports four overloads for the bitmap drawing method, which is named DrawImage.

An example might clarify what we mean by each of these two roles. Using a Graphics object, a program can draw onto a bitmap by calling drawing methods. One such drawing method is DrawImage, which draws a bitmap onto a drawing surface. A program can call the DrawImage method to draw one bitmap (the drawing object) onto the surface of another bitmap (the drawing surface).

To push the example one step further, a bitmap can be both the drawing surface and also the drawing object. You could do this by using the DrawImage method to draw onto a bitmap while using the bitmap itself as the image source. This may sound like a snake eating its own tail, a seemingly impossible operation. It is possible, however, because it involves copying a rectangular array of pixels from one part of a bitmap to another part. The work required for this type of bitmap handling is well understood and has been part of Windows display drivers for more than a decade. The bitmap drawing code in Windows CE can easily—and correctly—handle cases where, for example, source and destination rectangles overlap. This describes what happens, for example, when a user picks up and moves a window.

The Bitmap Class

The .NET Compact Framework supports in-memory bitmaps with the Bitmap22 class. This class is derived from the Image class, which is a common base class for the Bitmap class and the Metafile class. As we mentioned earlier in this chapter, metafiles are not supported in the .NET Compact Framework. But because the .NET Compact Framework maintains consistency with the desktop .NET Framework, our bitmap drawing method is called DrawImage (instead of, for example, DrawBitmap). On the desktop, where metafiles are supported, the DrawImage method draws both bitmaps and metafiles.

The dimensions of a bitmap are available through two properties of the Bitmap object: Height and Width. The dimensions are also available through the Size property, which provides a convenient package for height and width. These are read-only properties because an image cannot change size once it has been created.

On the desktop, the Bitmap class supports 12 constructors, while in the .NET Compact Framework there are only 4 constructors. You can create a bitmap these ways:

  • By opening an image file

  • By reading an image from a stream

  • By starting from an existing bitmap

  • By specifying the width and height for an empty bitmap

Table 15.10 maps the constructors to six common sources you might use to create a bitmap.

Creating an Empty Bitmap

One way to create a bitmap is to specify the desired dimensions of the bitmap to the following constructor in the Bitmap class.

public Bitmap(
   int width,
   int height);

This constructor creates a bitmap in program memory with the specified size. This is the quickest and easiest way to create a bitmap, but the empty (i.e., all-black) image means that you must draw into the bitmap before displaying its contents. You might call this a scratch space or double-buffer bitmap because it provides an off-screen drawing surface for doodling, just like scratch paper. The term double-buffer refers to a technique of creating smooth graphic effects by doing complex drawing off-screen and sending the resulting output to the display screen with a single, fast drawing operation. Let's use a bitmap created with this constructor.

Table 15.10. Sources for Bitmaps and Associated Bitmap Class Constructors

Source

Constructor Parameters

Comments

An external image file

(String)

Provides the path to the bitmap in the file system

A portion of a file

(Stream)

Uses the FileStream class to open the file and move the seek position to the first byte of the bitmap

Data in memory

(Stream)

Uses the MemoryStream class to assemble the bitmap bits as a byte array

A resource

(Stream)

Reads bitmap data from a managed resource created as an untyped manifest resource

An existing bitmap

(Image)

Copies an existing bitmap

An empty bitmap

(int,int)

Specifies the width and height of the empty bitmap

After creating the bitmap itself, a program typically obtains a Graphics object for the bitmap. As we mentioned earlier in this chapter, we need a Graphics object for any type of drawing. We obtain a Graphics object for the bitmap by calling the FromImage method of the Bitmap class. Before drawing anything else in the bitmap, it makes sense to first erase the bitmap's background.

We need to think about cleanup. This is a subject that can often be ignored in managed code, but not when working with resource-intensive objects like bitmaps. So, when done working with a bitmap, your program must use the Dispose method to clean up two objects: the bitmap itself and the Graphics object. The code in Listing 15.1 shows the whole life cycle of our created bitmap: The code creates a bitmap, erases the bitmap's background, draws the bitmap to the display screen, and then cleans up the two objects that were created.

Example 15.1. Dynamic Bitmap Creation

private void
CreateAndDraw(int x, int y)
{
   // Create a bitmap and a Graphics object for the bitmap.
   Bitmap bmpNew = new Bitmap(100,100);
   Graphics gbmp = Graphics.FromImage(bmpNew);

   // Clear the bitmap background.
   gbmp.Clear(Color.LightGray);

   // Get a Graphics object for the form.
   Graphics g = CreateGraphics();

   // Copy the bitmap to the window at (x,y) location.
   g.DrawImage(bmpNew, x, y);

   // Clean up when we are done.
   g.Dispose();
   gbmp.Dispose();
   bmpNew.Dispose();
}

Creating a Bitmap from an External File

Another way to create a bitmap is by specifying the path to an image file. This is accomplished with a constructor that accepts a single parameter, a string with the path to the candidate file. This second Bitmap class constructor is defined as follows.

public Bitmap(
   string filename);

This method has two important requirements. One is that there must be enough memory to accommodate the bitmap. If there is not, the call fails. A second requirement is that the specified file must have an image in a format that the constructor understands. We have been able to create bitmaps from the following file types:

  • Bitmap files (.bmp) with 1, 4, 8, or 24 bits per pixel

  • JPEG (.jpg) files

  • GIF (.gif) files

  • PNG (.png) files

Among the unsupported graphic file formats are TIFF (.tif) files.

This constructor throws an exception if the file name provided is not a recognized format or if it encounters other problems when attempting to open the file or create the bitmap. For that reason, it makes sense to wrap this constructor in a trycatch block. Listing 15.2 provides an example of calling this constructor, with a file name provided by the user in a File Open dialog box.

Example 15.2. Creating a Bitmap with a File Name

try
{
   bmpNew = new Bitmap(strFileName);
}
catch
{
   MessageBox.Show("Cannot create bitmap from " +
      "File: " + strFileName);
}

Creating a Bitmap from a Resource

When a program needs a bitmap for its normal operation, it makes sense to package the bitmap as a resource. Resources are read-only data objects that are bound into a program's executable file23 at program build time. The benefit of binding a bitmap to an executable file is that it is always available and cannot be accidentally deleted by a user.

Resources have been a part of Windows programming from the very first version of Windows. In a native-mode program, resources are used for bitmaps and icons and also to hold the definitions of dialog boxes and menus. In managed-code programs, resources are still used for bitmaps and icons, although some program elements—including dialog boxes and menus—are not defined in a resource but instead are defined using code in the InitializeComponent method by the Designer.

Where Do Resources Live?

Because memory is scarce on a Windows CE–powered device, it helps to know when and how memory gets used. When a resource gets added to a module, the resource occupies space in the module's file but uses no program memory until the resource is explicitly opened and used. This is true for both native resources and managed resources.

While resources are used in both native code and managed code, native resources can be used only from native mode code, and managed resources can be used only from managed code. The only exception is the program icon for a managed-code program, which is defined as a native icon. In managed code, there are two types of resources: typed resources and untyped resources.

Typed Resources

We like to use typed resources to hold literal strings, which aid in the localization of programs. To access typed resources, a program creates an instance of a ResourceManager24 class and then makes calls to methods like GetObject and GetString. We provided an example of using typed resources for literal strings in Chapter 3, in the sample project named StringResources.

Typed resources are defined using XML in files that have an extension of .resx. In a typed resource, an XML attribute provides type information, as shown in this example.

<data name="dlgFileOpen.Location" type="System.Drawing.Point,
 System.CF.Drawing, Version=7.0.5000.0, Culture=neutral,
 PublicKeyToken=b03f5f7f11d50a3a">
     <value>125, 17
     </value>
</data>

The Designer makes extensive use of typed resources. For each form created in the Designer, there is an associated file used to store a variety of details about the form. The Visual Studio .NET Solution Explorer does not normally display resource files, but you can make them appear by clicking the Show All Files icon.

For example, bitmaps in the image collection of an ImageList control on a form are stored as typed resources in the typed resource file of the form that contains the control. The bitmaps themselves are serialized into the XML and are not stored in their original, binary form. While a programmer could convert bitmap files into XML resources, we prefer to avoid this extra step and use untyped resources when we add bitmap resources to a project.

Untyped Resources

Untyped resources are also known as manifest resources because they are made available through an assembly's manifest (or table of contents). As the name implies, an untyped resource contains no type information and is made available as a raw stream of bytes. It does have a name, however, created by combining the default project namespace with the file name that contained the original resource data. You must know this name because you use the name to retrieve the resource. If you have trouble figuring out the resource name, the ildasm.exe utility can help. Open the program file and then click on the manifest. Listing 15.3 shows three bitmap resource names in a fragment from the manifest for the ShowBitmap sample program presented later in this chapter.

Example 15.3. Three Bitmap Resource Names from the ShowBitmap Manifest

.mresource public ShowBitmap.SPADE.BMP
{
}
.mresource public ShowBitmap.CUP.BMP
{
}
.mresource public ShowBitmap.HEART.BMP
{
}

Visual Studio .NET creates an untyped resource from an external file when you add the file to a project and assign a build action of Embedded Resource. The default build action for bitmap files, Content, allows the bitmap file to be downloaded with a program, but as a separate file and not as an embedded resource. Figure 15.2 shows the Visual Studio .NET settings to turn the file CUP.BMP into an embedded bitmap resource. The name of the resource is ShowBitmap.CUP.BMP, which we need to know to access the resource from our code.

15fig02.gifFigure 15.2 The settings to turn CUP.BMP into an embedded bitmap resource


You can access an embedded resource by calling a method in the Assembly class named GetManifestResourceStream.25 As suggested by the method name, the return value is a Stream object; more precisely, you are provided a MemoryStream26 object. You can use all of the elements associated with a Stream-derived class (including the ability to query the resource length, which is the same as the resource input file) to seek a location in the stream and to read bytes (the CanSeek and CanRead properties are both set to true). In keeping with the read-only nature of Windows resources, you cannot write to a resource stream27 (CanWrite returns false).

The code fragment in Listing 15.4 shows two methods from the ShowBitmap sample program. These methods are helper routines to handle the initialization and cleanup of resource-based bitmaps. The LoadBitmapResource method creates a bitmap from a resource; the DisposeBitmap method provides the cleanup.

What Can Go into an Untyped Resource?

This chapter provides an example of putting a bitmap into an untyped resource. But this is not the only type of resource you can create. You can put any custom data into untyped resources, which can then be used to access the data at runtime. When you request an untyped resource, you are provided with a Stream object that you can use as you wish. You might, for example, read a resource into an array of bytes and then parse those bytes in whatever way your application needs. Such resources can be any read-only data that your program needs: tax tables, sports scores, or—as we show in the ShowBitmap sample program—a set of bitmaps.

A benefit of using custom resources is that we have access to data we need at runtime. But when we are not using that data, it does not occupy scarce program memory. This makes custom resources a useful tool in our toolkit for building memory-wise programs.

Example 15.4. Creating Bitmaps from Untyped Manifest Resources

private Bitmap LoadBitmapResource(string strName)
{
   Assembly assembly = Assembly.GetExecutingAssembly();
   string strRes = "ShowBitmap." + strName;
   Stream stream = assembly.GetManifestResourceStream(strRes);
   Bitmap bmp = null;
   try
   {
      bmp = new Bitmap(stream);
   }
   catch { }
   stream.Close();

   return bmp;
}

private void DisposeBitmap(ref Bitmap bmp)
{
   if (bmp != null)
   {
      bmp.Dispose();
   }
   bmp = null;
}

The LoadBitmapResource method creates a bitmap by opening a resource stream and uses data read from that stream to create a bitmap. This method gets a reference to the program's assembly by calling a static method in the Assembly class named GetExecutingAssembly. After creating a bitmap, the stream can be closed. Once a bitmap has been created, it is self-contained and needs no external data. That is why we can close the stream once the Bitmap object has been created.

The DisposeBitmap method deletes the bitmap to free up its associated memory. It does this by calling the Dispose method for a Bitmap object. There are only a few situations in which it is mandatory to call the Dispose method.28 Sometimes, however, it is still a good idea—even if it is not, strictly speaking, required. Bitmaps can be large, so we suggest you consider explicitly deleting bitmaps, as we have done in our sample. Among the factors to consider are the size of your bitmaps and the number of bitmaps. We suggest that you explicitly delete bitmaps when you have either many small bitmaps or a few large bitmaps.

We call our two methods using code like the following.

private void
mitemResourceCup_Click(object sender, EventArgs e)
{
   DisposeBitmap(ref bmpDraw);
   bmpDraw = LoadBitmapResource("CUP.BMP");
   Invalidate();
}

After cleaning up the old bitmap, we create a new bitmap and request a Paint event by calling the Invalidate method. Next, we discuss image file size, and how to save memory by changing the format you use for your images.

Image File Sizes

Bitmaps can occupy a lot of memory, which can create problems in a memory-scarce environment like Windows CE. When placing bitmaps in resources, we recommend that you test different formats and use the smallest one. To provide a starting point, we conducted some tests with three 100 × 100 pixel images stored in different formats. Table 15.11 summarizes our results, which provide the size in bytes for each image file.

Four formats are uncompressed and three are compressed. The first four entries in the table are for DIB files. This well-known format is thoroughly documented in the MSDN Library and is the format that Visual Studio .NET provides for creating bitmap images. Notice that the size of these images is the same for a given number of bits per pixel. This reflects the fact that DIB files are uncompressed.

Table 15.11. Size Comparison for Three 100 x 100 Images in Various Image File Formats

Format

Bits per Pixel

Size of Single-Color Image (Bytes)

Size of Multicolor Image with Regular Data (Bytes)

Size of Multicolor Image with Irregular Data (Bytes)

Monochrome DIB

1

1,662

1,662

1,662

16-color DIB

4

5,318

5,318

5,318

256-color DIB

8

11,078

11,078

11,078

True-color DIB

24

30,054

30,054

30,054

GIF

8

964

3,102

7,493

PNG

8

999

616

5,973

JPEG

24

823

3,642

5,024

The last three formats are the compressed formats: GIF, PNG, and JPEG. To make sense of these formats, we must discuss the contents of the three images. The single-color image was a solid black rectangle. Each of the three compressed formats easily beat any of the uncompressed formats for the single-color image. The reason is that compressed formats look for a pattern and use that information to store details of the pattern. A single color is a pretty easy pattern to recognize and compress.

The second column, the multicolor image with regular data, shows the results for an image created with a solid background and vertical stripes. We used vertical stripes in an attempt to thwart the compression because run-length encoding of horizontal scan lines is an obvious type of compression. We were surprised (and pleased) to find that PNG compression was able to see through the fog we so carefully created—it created the smallest image in the table.

The third column, the multicolor image with irregular data, shows the sizes for images created with very random data. For this test, we copied text (.NET Compact Framework source code) into an image file. (We never want our work to be called “random,” but we wanted an irregular image to push the envelope for the three compression formats.) The result was more like a photograph than any of the other images, which is why JPEG—the compression scheme created for photographs—was able to provide the best compression. It provided the smallest file size with the least loss of information (the monochrome image was smaller, but the image was lost).

To summarize, the two compression schemes that created the smallest image files were PNG (for regular data) and JPEG (for irregular data). One problem is that Visual Studio .NET does not support either of these formats. But Microsoft Paint (mspaint.exe) supports both, so we recommend that you make sure your images have been compressed as much as possible prior to embedding your images as resources.

Drawing Bitmaps

The Graphics class supports four overloaded versions of the bitmap drawing method, DrawImage. These alternatives support the following types of bitmap drawing:

  • Drawing the entire bitmap at the original image size

  • Drawing part of a bitmap at the original image size

  • Drawing part of a bitmap with a change to the image size

  • Drawing part of a bitmap with a change to the image size and with transparency

We discuss these four methods in the sections that follow.

Drawing the Entire Bitmap at the Original Image Size

The simplest version of the DrawImage method copies an entire bitmap onto a device surface with no change in the image size, as shown here.

public void DrawImage(
   Image image,
   int x,
   int y);

Listing 15.5 shows an example of calling this method in a Paint event handler.

Example 15.5. Drawing an Entire Bitmap at the Original Size

private void
FormMain_Paint(object sender, PaintEventArgs e)
{
   Graphics g = e.Graphics;
   int x = 10;
   int y = 10;

   g.DrawImage(bmpDraw, x, y);
}

Drawing Part of a Bitmap at the Original Image Size

While we sometimes want to draw an entire bitmap, there are also times when we only want to see a portion of a bitmap. The second version of the DrawImage method provides the support we need to do just that, as shown on the next page.

public void DrawImage(
   Image image,
   int x,
   int y,
   Rectangle srcRect,
   GraphicsUnit srcUnit);

This version of the DrawImage method has five parameters, while the earlier one has only three. One of the extra parameters is useful, and the second is not so useful. The fourth parameter, srcRect, is the useful one, which identifies the rectangular area in the source bitmap that we wish to copy to the destination surface.

The fifth parameter, srcUnit, can be set to only one valid value in the .NET Compact Framework: GraphicsUnit.Pixel. On the desktop, the presence of this parameter gives the caller the freedom to select a convenient unit of measure for the source rectangle (e.g., inches or millimeters). But the .NET Compact Framework supports only pixel drawing units, which is why this parameter is not so useful in the context of a .NET Compact Framework program. The srcUnit parameter is present because of the high level of compatibility between the desktop .NET Framework and the .NET Compact Framework. As such, it represents a small price to pay for the convenience of allowing smart-device code to have binary compatibility with the desktop runtime.

Drawing Part of a Bitmap with a Change to the Image Size

The third overloaded version of the DrawImage method allows a portion of a bitmap to be selected for drawing, and that portion can be stretched (or shrunk) to match a specified size on the destination surface. Of course, nothing requires the image to change size: If the width and height of the destination rectangle is the same as the width and height of the source rectangle, no size change occurs. This version of the DrawImage method is defined as shown here.

public void DrawImage(
   Image image,
   Rectangle destRect,
   Rectangle srcRect,
   GraphicsUnit srcUnit);

Drawing Part of a Bitmap with a Change to the Image Size and with Transparency

The final version of the DrawImage method adds a new feature to the drawing of bitmaps. It enables transparency while drawing a bitmap. In some ways, this feature breaks our definition of raster graphics. You might recall that we refer to raster graphics as those operations that operate on arrays of pixels. Implicit in this definition is that all operations are rectangular.

The ability to draw a raster operation and touch only a nonrectangular set of pixels on a drawing surface is, therefore, something of a heresy (like having nonrectangular windows on a display screen or a late-night coding session without ordering large quantities of unhealthy food). We hope that readers can accept this change with little loss of sleep. We certainly are happy to break the shackles that have previously limited almost all raster graphics to the boring world of rectangular arrays of pixels. This amazing new feature is available through the following version of the DrawImage method.

public void DrawImage(
   Image image,
   Rectangle destRect,
   int srcX,
   int srcY,
   int srcWidth,
   int srcHeight,
   GraphicsUnit srcUnit,
   ImageAttributes imageAttr);

With its eight parameters, this version of the DrawImage method is the most complicated one that the .NET Compact Framework supports. Perhaps it is appropriate that this version matches the other versions in capabilities: It can draw an entire bitmap at its original size, draw a portion of a bitmap at its original size, and draw a portion of a bitmap at a different size.

What makes this version different is the final parameter, a reference to an ImageAttributes object. On the desktop, this class supports a variety of color adjustments that can be applied when drawing a bitmap onto a surface. The .NET Compact Framework version is much simpler, with what amounts to a single property: a color key. The color key defines the range of colors that represent transparent portions of an image. In other words, any color that matches the color key is a color that is not copied by the call to the DrawImage method. The color key settings are controlled through two methods: SetColorKey defines the transparency range, and ClearColorKey disables the transparency range.

Figure 15.3 shows an example of transparency at work. A 100 × 100 bitmap is first drawn without transparency at the window origin. That same bitmap is then drawn three times, using the version of the DrawImage method that supports transparency. The color key is set to light gray, which corresponds to the color outside the ellipse (the interior of the ellipse is set to yellow). Listing 15.6 shows the code, a handler for a MouseDown event, which we used to create the example.

Example 15.6. Event Handler That Draws a Bitmap with Transparency

bool bFirstTime = true;

private void
FormMain_MouseDown(object sender, MouseEventArgs e)
{
   // Get a Graphics object for the form.
   Graphics g = CreateGraphics();
   // Create a bitmap and a Graphics object for the bitmap.
   Bitmap bmpNew = new Bitmap(100,100);
   Graphics gbmp = Graphics.FromImage(bmpNew);

   // Clear the bitmap background.
   gbmp.Clear(Color.LightGray);

   // Create some drawing objects.
   Pen penBlack = new Pen(Color.Black);
   Brush brBlack = new SolidBrush(Color.Black);
   Brush brYellow = new SolidBrush(Color.Yellow);

   // Draw onto the bitmap.
   gbmp.FillEllipse(brYellow, 0, 0, 98, 98);
   gbmp.DrawEllipse(penBlack, 0, 0, 98, 98);
   gbmp.DrawString("At " + e.X.ToString() + "," + e.Y.ToString(),
      Font, brBlack, 40, 40);

   // Copy the bitmap to the window at the MouseDown location.
   if (bFirstTime)
   {
      // Copy without transparency.
      g.DrawImage(bmpNew, e.X, e.Y);
      bFirstTime = false;
   }
   else
   {
      // Copy the bitmap using transparency.
      Rectangle rectDest = new Rectangle(e.X, e.Y, 100, 100);
      ImageAttributes imgatt = new ImageAttributes();
      imgatt.SetColorKey(Color.LightGray, Color.LightGray);
      g.DrawImage(bmpNew, rectDest, 0, 0, 99, 99,
         GraphicsUnit.Pixel, imgatt);
   }

   // Clean up when we are done.
   g.Dispose();
   gbmp.Dispose();
   bmpNew.Dispose();
}

15fig03.gifFigure 15.3 Four calls to the DrawImage method, three with transparency enabled


A Sample Program: ShowBitmap

Our bitmap drawing sample program shows several features of bitmaps that we have been discussing. This program can open files and create a bitmap. Several formats are supported, including the standard Windows DIB (.bmp) files and also a few compressed image file formats such as GIF (.gif) files, JPEG (.jpg) files, and PNG (.png) files. Figure 15.4 shows the ShowBitmap program with a JPEG image of Chandler (the office beagle at The Paul Yao Company). This image is drawn scaled to 50%, an effect made possible by selecting the appropriate version of the DrawImage method.

15fig04.jpgFigure 15.4 ShowBitmap displaying a JPEG file scaled to 50%


Our sample program contains a set of bitmap files that are bound to the program files as embedded resources (see Listing 15.7). As with all types of resources, the resource data does not get loaded into memory until we explicitly load the resource. In this program, we load the resource when the user selects an item on the program's resource menu. Figure 15.5 shows the bitmap resource that was read from a resource identified as ShowBitmap.CUP.BMP, drawn at 400% of its original size.

Example 15.7. Source Code for ShowBitmap.cs

using System.Reflection; // Needed for Assembly
using System.IO;         // Needed for Stream
using System.Drawing.Imaging;  // Needed for ImageAttributes
// ...
private Bitmap bmpDraw;
bool bFirstTime = true;
bool bResource = false;
string strResName;

// Draw a bitmap using transparency where the MouseDown
// event is received.
private void
FormMain_MouseDown(object sender, MouseEventArgs e)
{
#if false
   CreateAndDraw(e.X, e.Y);
#endif
   // Get a Graphics object for the form.
   Graphics g = CreateGraphics();

   // Create a bitmap and a Graphics object for the bitmap.
   Bitmap bmpNew = new Bitmap(100,100);
   Graphics gbmp = Graphics.FromImage(bmpNew);

   // Clear the bitmap background.
   gbmp.Clear(Color.LightGray);

   // Create some drawing objects.
   Pen penBlack = new Pen(Color.Black);
   Brush brBlack = new SolidBrush(Color.Black);
   Brush brYellow = new SolidBrush(Color.Yellow);

   // Draw onto the bitmap.
   gbmp.FillEllipse(brYellow, 0, 0, 98, 98);
   gbmp.DrawEllipse(penBlack, 0, 0, 98, 98);
   gbmp.DrawString("At " + e.X.ToString() + "," + e.Y.ToString(),
      Font, brBlack, 40, 40);

   // Copy the bitmap to the window at the MouseDown location.
   if (bFirstTime)
   {
      // Copy without transparency.
      g.DrawImage(bmpNew, e.X, e.Y);
      bFirstTime = false;
   }
   else
   {
      // Copy the bitmap using transparency.
      Rectangle rectDest = new Rectangle(e.X, e.Y, 100, 100);
      ImageAttributes imgatt = new ImageAttributes();
      imgatt.SetColorKey(Color.LightGray, Color.LightGray);
      g.DrawImage(bmpNew, rectDest, 0, 0, 99, 99,
         GraphicsUnit.Pixel, imgatt);
   }

   // Clean up when we are done.
   g.Dispose();
   gbmp.Dispose();
   bmpNew.Dispose();
}

private void FormMain_Paint(object sender, PaintEventArgs e)
{
   Graphics g = e.Graphics;
   float sinX = 10.0F;
   float sinY = 10.0F;
   SizeF szfText = g.MeasureString("X", Font);
   float cyLine = szfText.Height;

   Brush brText = new SolidBrush(SystemColors.WindowText);
   if (bmpDraw != null)
   {
      if (bResource)
      {
         g.DrawString("Resource: " + strResName,
            Font, brText, sinX, sinY);
      }
      else
      {
         g.DrawString("File: " + dlgFileOpen.FileName,
            Font, brText, sinX, sinY);
      }
      sinY += cyLine;

      g.DrawString("Bitmap Height = " + bmpDraw.Height,
         Font, brText, sinX, sinY);
      sinY += cyLine;

      g.DrawString("Bitmap Width = " + bmpDraw.Width,
         Font, brText, sinX, sinY);
      sinY += cyLine;
      sinY += cyLine;

      if (mitemScale100.Checked)
      {
         g.DrawImage(bmpDraw, (int)sinX, (int)sinY);
      }
      else
      {
         Rectangle rectSrc = new Rectangle(0, 0,
            bmpDraw.Width, bmpDraw.Height);
         int xScaled = 0;
         int yScaled = 0;
         if (mitemScale50.Checked)
         {
            xScaled = bmpDraw.Width / 2;
            yScaled = bmpDraw.Height / 2;
         }
         else if (mitemScale200.Checked)
         {
            xScaled = bmpDraw.Width * 2;
            yScaled = bmpDraw.Height * 2;
         }
         else if (mitemScale400.Checked)
         {
            xScaled = bmpDraw.Width * 4;
            yScaled = bmpDraw.Height * 4;
         }

         Rectangle rectDest = new Rectangle((int)sinX,
            (int)sinY, xScaled, yScaled);
         g.DrawImage(bmpDraw, rectDest, rectSrc,
            GraphicsUnit.Pixel);
      }
   }
   else
   {
      g.DrawString("File: None", Font, brText, sinX, sinY);
   }
}

private void
mitemFileOpen_Click(object sender, EventArgs e)
{
   dlgFileOpen.Filter = "Bitmap (*.bmp)|*.bmp|" +
                        "Picture (*.jpg)|*.jpg|" +
                        "PNG Files (*.png)|*.png|" +
                        "TIF Files (*.tif)|*.tif|" +
                        "GIF Files (*.gif)|*.gif |" +
                        "All Files (*.*)|*.*";
   if (dlgFileOpen.ShowDialog() == DialogResult.OK)
   {
      Bitmap bmpNew = null;
      try
      {
         bmpNew = new Bitmap(dlgFileOpen.FileName);
         bResource = false;
      }
      catch
      {
         MessageBox.Show("Cannot create bitmap from " +
            "File: " + dlgFileOpen.FileName);
         return;
      }

      DisposeBitmap (ref bmpDraw);
      bmpDraw = bmpNew;
      Invalidate();
   }
}

private void
mitemScale_Click(object sender, EventArgs e)
{
   // Clear the checkmark on related items.
   mitemScale50.Checked = false;
   mitemScale100.Checked = false;
   mitemScale200.Checked = false;
   mitemScale400.Checked = false;

   // Set the checkmark on selected menu item.
   ((MenuItem)sender).Checked = true;
   // Request paint to redraw bitmap.
   Invalidate();
}


private void
mitemResourceCup_Click(object sender, EventArgs e)
{
   DisposeBitmap(ref bmpDraw);
   bmpDraw = LoadBitmapResource("CUP.BMP");
   Invalidate();
}

private void
mitemResourceBell_Click(object sender, EventArgs e)
{
   DisposeBitmap(ref bmpDraw);
   bmpDraw = LoadBitmapResource("BELL.BMP");
   Invalidate();
}

private void
mitemResourceSpade_Click(object sender, EventArgs e)
{
   DisposeBitmap(ref bmpDraw);
   bmpDraw = LoadBitmapResource("SPADE.BMP");
   Invalidate();
}

private void
mitemResourceHeart_Click(object sender, EventArgs e)
{
   DisposeBitmap(ref bmpDraw);
   bmpDraw = LoadBitmapResource("HEART.BMP");
   Invalidate();
}

private void
mitemResourceDiamond_Click(object sender, EventArgs e)
{
   DisposeBitmap(ref bmpDraw);
   bmpDraw = LoadBitmapResource("DIAMOND.BMP");
   Invalidate();
}

private void
mitemResourceClub_Click(object sender, EventArgs e)
{
   DisposeBitmap(ref bmpDraw);
   bmpDraw = LoadBitmapResource("CLUB.BMP");
   Invalidate();
}

private Bitmap LoadBitmapResource(string strName)
{
   Assembly assembly = Assembly.GetExecutingAssembly();
   string strRes = "ShowBitmap." + strName;
   Stream stream = assembly.GetManifestResourceStream(strRes);
   Bitmap bmp = null;
   try
   {
      bmp = new Bitmap(stream);
      strResName = strRes;
      bResource = true;
   }
   catch { }
   stream.Close();

   return bmp;
}

private void DisposeBitmap(ref Bitmap bmp)
{
   if (bmp != null)
   {
      bmp.Dispose();
   }
   bmp = null;
}

// Simplest possible bitmap: Create a bitmap, clear the
// bitmap background, draw the bitmap to the display screen.
private void
CreateAndDraw(int x, int y)
{
   // Create a bitmap and a Graphics object for the bitmap.
   Bitmap bmpNew = new Bitmap(100,100);
   Graphics gbmp = Graphics.FromImage(bmpNew);

   // Clear the bitmap background.
   gbmp.Clear(Color.LightGray);

   // Get a Graphics object for the form.
   Graphics g = CreateGraphics();
   // Copy the bitmap to the window at (x,y) location.
   g.DrawImage(bmpNew, x, y);

   // Clean up when we are done.
   g.Dispose();
   gbmp.Dispose();
   bmpNew.Dispose();
}

15fig05.gifFigure 15.5 ShowBitmap displaying a bitmap from a resource


InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020