Drawing Bitmaps

A bitmap is a 2D image, also known as a texture in the realm of 3D programming. Bitmap images can be created in memory, but they are more often loaded from an existing bitmap file. The most common file types are BMP, PNG, and JPG. For games, we want high-quality bitmaps without lossy compression (in which pixels are changed to shrink the file size). The BMP format does work well but it has fallen out of favor in recent years due to format incompatibilities (such as some BMP files supporting an alpha channel, and some not). A good compromise is the PNG format, which offers decent file size and 32-bit ARGB color. For the sake of discussion, images will just be referred to as “bitmaps” regardless of the file format. In this hour, we’ll learn to add bitmap files to the Content project, and then load and draw them. This is the basis for most games!

Adding a Bitmap File to an XNA Project

Bitmap programming is the precursor to sprite programming, which is where we really want to go in order to draw game characters, ships, or whatever the game calls for. We can’t draw a 3D shape with just a bitmap, although bitmaps are used as textures when rendering in 3D. A texture is an image wrapped around the 3D shape, such as a tree, a car, or a human figure. Texturing can get pretty complex! Fortunately, we’ll start with the basics of 3D in a later hour and learn how to draw with lights and special effects. But first we need to learn 2D or bitmap programming.

There’s so much we can do with bitmaps in XNA! I’m eager to jump right into the advanced rendering stuff, but we’ll start off at a steady pace and first learn the basics. In the next hour, we’ll do some really fascinating things with bitmaps, like scaling and rotation.

We need to create a new project for our explorations of bitmap programming. Let’s begin with a new project.

Adding a Bitmap Asset to an XNA Project

Follow these steps to create the Bitmap Demo project that we will use as an example while learning how to load and draw bitmaps in this hour:

  1. Create a new Windows Phone (4.0) project called Bitmap Demo.
  2. Create or find a bitmap file containing an image you want to draw. The example uses an image of a spaceship that you can borrow. The bitmap file is a 32-bit PNG with a transparent region (via alpha channel).
  3. Right-click the Content project in Solution Explorer (it is called Bitmap Demo- Content (Content)), as shown in Figure 5.1. From the context menu that comes up, choose Add, Existing Item.

    Adding an existing bitmap file to the project.
    FIGURE 5.1 Adding an existing bitmap file to the project.
  4. The Add Existing Item dialog should come up. Select the bitmap file you want to add to the project. The file is added to the project as shown in Figure 5.2.

    The ship.png file has been added.
    FIGURE 5.2 The ship.png file has been added.
  5. If the Properties window is not visible, open it with View, Properties Window. When you select an asset file in the Content project, the file’s properties will be shown. Note the properties in the case of ship.png. Both the Content Importer and the Content Processor properties are automatically set to Texture – XNA Framework. This means that XNA recognized the file type and will process it as a texture. If you look at the other items in the drop-down list, you should find the following:
    • Effect – XNA Framework
    • Autodesk FBX – XNA Framework
    • Sprite Font Description – XNA Framework
    • WAV Audio File – XNA Framework
    • WMA Audio File – XNA Framework
    • WMV Video File – XNA Framework
    • XACT Project – XNA Framework
    • X File – XNA Framework
    • XML Content – XNA Framework

    These are the types of asset files XNA recognizes automatically. You can also create your own custom content importer to convert data files of any type to work with XNA—although you still have to write the code to read the file.

Loading a Bitmap File as an Asset

The name of the bitmap file added to the project is important, but the extension is not. The ship.png file added in the preceding Try It Yourself tutorial will be converted into a file called ship.xnb when compiled, and will be recognized by the name “ship” in the content manager. Let’s see how to load the bitmap.

Writing Code to Load a Bitmap File

Follow these steps to write the code to load a bitmap file:

  1. Add two new variables near the top of the Game1 class definition (where other global variables are found) as shown. The first variable is of type Texture2D, which is a class for bitmaps. The second is a Vector2 to keep track of the position of the bitmap on the screen.
    public class Game1 : Microsoft.Xna.Framework.Game
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    //texture variable
    Texture2D shipImage;
    //position variable
    Vector2 shipPos;
  2. Load the “ship” bitmap in the LoadContent() method and set the position to (0,0).
    protected override void LoadContent()
    spriteBatch = new SpriteBatch(GraphicsDevice);
    //load the ship bitmap
    shipImage = Content.Load<Texture2D>(“ship”);
    shipPos = Vector2.Zero;

If XNA cannot find an asset file that your code tries to load with Content.Load() in the program’s main LoadContent() method, an exception error will be generated!

Drawing a Bitmap with SpriteBatch

After adding the bitmap file as an asset item to the Content project, adding the variables, and loading the bitmap as a Texture2D, we can now draw the bitmap onto the screen. For that, we need to jump down to the Draw() method. Here is the easiest way to draw a bitmap:

protected override void Draw(GameTime gameTime)
//begin drawing
//draw the bitmap
spriteBatch.Draw(shipImage, shipPos, Color.White);
//finish drawing

The output from this program is shown in Figure 5.3.

SpriteBatch is an XNA core class that can draw both bitmaps and text, with many variations of the Draw() and DrawString() methods. In the next hour, we will work with SpriteBatch.Draw() extensively, exploring all of its capabilities. Let’s look at all the overloaded variations of Draw() here to get a feel for these capabilities. We will study these in more detail in the next hour.

The first parameter is always a Texture2D. The second parameter is always the destination— either a Rectangle or a Vector2. Beyond those two are a variety of parameters that make it possible to perform the following actions:

  • Rotate
  • Scale
  • Animate
  • Flip horizontally
  • Flip vertically
The Bitmap Demo program.
FIGURE 5.3 The Bitmap Demo program.

We will learn to use these special effects in upcoming chapters, as well as take a peek under the hood of the SpriteBatch.Begin() and SpriteBatch.End() methods that have been so mysterious up to this point. We have used only the second version, although the first one is easy enough to follow as well. Overloads 3 to 7 will be examined in the next hour and others following in depth, so think of this as just a sneak peek! Some of this code might be hard to read, because these are the definitions of the methods. I’m only going into this much detail now for Draw() because we will use it a lot!

public void Draw(Texture2D texture, Rectangle destinationRectangle,
Color color);
public void Draw(Texture2D texture, Vector2 position, Color color);
public void Draw(Texture2D texture, Rectangle destinationRectangle,
Rectangle? sourceRectangle, Color color);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color);
public void Draw(Texture2D texture, Rectangle destinationRectangle,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, SpriteEffects effects, float layerDepth);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, float scale, SpriteEffects effects, float layerDepth);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, Vector2 scale, SpriteEffects effects,
float layerDepth);

Drawing Bitmaps with Transparency

The ship.png file used in the example in this hour has an alpha channel and draws without showing the background pixels around the shape of the actual ship inside the bitmap. Let’s learn how to create an alpha channel. I’ll use Gimp because it’s a free graphics editor and it has many features similar to the uber-powerful Adobe Photoshop (download Gimp from www.gimp.org).

XNA will draw an image transparently if it has an alpha channel, and we don’t need to do anything extra for this to happen. This really makes life easier for an XNA game programmer. If you supply XNA with a bitmap file containing transparent pixels, it will draw that image with transparency.

I have used many graphics editors, including Photoshop, PaintShop Pro, Corel- DRAW, and Gimp. Although each behaves somewhat differently, they all share a similar toolset, including the ability to work with alpha channels. The instructions here for Gimp will be basically similar for other graphics editors.

Creating an Alpha Channel

Let’s take a look at how to create an alpha channel. To add a transparency layer to an image, you need to locate the Magic Wand tool available in most graphics editors.

  1. After selecting the Magic Wand tool, click somewhere in the background (which should be a solid color). The Magic Wand locates the edges of the game object and highlights everything around it, as shown in Figure 5.4. Another, more precise way to select a background is with the Color Picker or the Select by Color tool.
  2. Now that you have a selection available, you can create a layer mask. In Gimp, open the Layer menu and choose Mask, Add Layer Mask, as shown in Figure 5.5.
  3. The Add Layer Mask dialog shown in Figure 5.6 comes up. Choose the Selection option and check the Invert Mask option; then click the Add button.
    Finding the edges of the game object with the Magic Wand tool.
    FIGURE 5.4 Finding the edges of the game object with the Magic Wand tool.
    Preparing to add a layer mask.
    FIGURE 5.5 Preparing to add a layer mask.

    The Add Layer Mask dialog is used to choose options for a layer mask.
    FIGURE 5.6 The Add Layer Mask dialog is used to choose options for a layer mask.
  4. The next step is to apply the layer mask. You can tell Gimp to apply the mask using the Layer menu, then Mask, Apply Layer Mask, as shown in Figure 5.7.

    Applying the new layer mask makes it permanent.
    FIGURE 5.7 Applying the new layer mask makes it permanent.
  5. In the final figure, shown in Figure 5.8, the alpha channel has been created based on the masked selection. The checkerboard background behind the asteroid image shows the transparent region. The result looks very nice! You can load this image into your XNA project, and it will draw with transparency so that the outer edges of the image (where the solid background is located) will not overwrite the background of the screen. Just be sure to save the file using a bitmap file format that supports transparency and works with XNA. The most common formats are PNG and TGA.

    The asteroid image now has a masked transparency layer.
    FIGURE 5.8 The asteroid image now has a masked transparency layer.

This hour began a journey that will last for essentially the rest of the book. Most of our attention will be on 2D graphics in upcoming chapters, with all the variations of the SpriteBatch.Draw() method and the special effects that are possible with it. Being able to add a bitmap file to the project (with transparency information), and then load and draw it, is essential for every XNA programmer.

Sprite Sheet and Blitting

Xerox PARC first created the bit-block transfer, Bit BLIT for short, for the Smalltalk system in 1975. Blitting is a computer graphics operation. It takes at least one bitmap and combines it with another. It has been a standard technique used since the beginning of the digital gaming industry.

Let’s see how we can use this technology for our purposes.


A sprite sheet, also called a tile sheet animation, is a collection of images arranged into a single image. Traditionally, each sprite represents one frame of an animation.

In AIR, especially on mobile devices, the sprite sheet has benefits beyond animation. It can be used for ease of production and to load a single large image rather than monitoring many small loading processes. Also, bitmap manipulation is very quick. Figure 18-3 shows a sprite sheet for a walk cycle.

Figure 18-3. A sprite sheet for a walk cycle
Figure 18-3. A sprite sheet for a walk cycle

The motion is created in code to copy various areas of the image to the screen. All images have the same predictable repetitive width and height, so copying a rectangular region can be automated. Use measurement with integers in powers of two for faster calculation. Verify that the element that is animating is in the same relative position throughout, preferably from the upper left corner or the baseline.

Use the BitmapData.copyPixels method to copy a defined area. Required parameters are the source bitmap, a rectangle, and a destination point. The method copies a rectangular area for a source image to a destination area of the same size.

Let’s import the graphics of the walking man shown in Figure 18-3 as a PNG image:


var complete:BitmapData;
var walkIndex:int = 0;
const TOTAL:int = 8;
var loader:Loader = new Loader();
loader loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
loader.load(new URLRequest(“/assets/walkingMan.png”);
function onLoaded(event:Event):void {
event.target.removeEventListener(Event.COMPLETE, onLoaded);
complete = event.target.content.bitmapData;


Add an ENTER_FRAME event listener. Change the definition of the rectangle to copy a different segment of the original bitmap, and push these pixels into the BitmapData called cell. Wrap it into a Bitmap to render it to the screen:


var cell:BitmapData = new BitmapData(CELL_WIDTH, CELL_HEIGHT, true, 0);
stage.addEventListener(Event.ENTER_FRAME, walk);
function walk(event:Event):void {
var rectangle:Rectangle = new Rectangle(
CELL_WIDTH*walkIndex, 0,
cell.copyPixels(complete, rectangle, new Point(0, 0));
var bitmap:Bitmap = new Bitmap(cellBitmap);
if (walkIndex == TOTAL) {
walkIndex = 0;


Some open source AIR applications are available to ease the process of creating sprite sheets. Sprite Sheet Maker, by Mike Jones, lets you import separate PNGs, reorganize them as needed, and export them as the final sprite sheet (see http://blog.flashgen.com/ 2010/12/21/sprite-sheet-maker-preview/). SWFSheet, by Keith Peters, imports an .swf file and converts it to a sprite sheet (see http://www.bit-101.com/blog/?p=2939). Keith takes advantage of Flash as an animation tool and shows the .swf running. The number of frames captured can be adjusted, as well as the frame area and the sprite dimension before exporting the final PNG.



Optimizing Art

If you or your designer uses Flash Professional as a paint tool, optimize the shapes under Modify→Shape→Optimize. Then, convert strokes to fill.

Use a utility to optimize PNG and JPEG formats. Both PNG Optimizer (http://www .pngoptimizer.com/) and ImageOptim (http://imageoptim.pornel.net/) are free and will reduce the file size of your images.

If you have several vectors that are not moving, are grouped, or are on top of each other, convert them to a single bitmap. Crop the alpha channel of your bitmap carefully. Remove the alpha channel completely on PNGs if you don’t need it. In Photoshop, use the File→Save for Web & Devices option instead of File→Save As. Save your image as PNG-8.

To monitor the rendering process, make the redraw regions visible. They are the dirty regions that are processed and rendered. In AIR, you do not have access to the context menu, but you can show the regions in code and choose the color:


flash.profiler.showRedrawRegions(true, 0xFF0000);



Packaging the Application

You must package your application as a XAP file to be able to upload it to Windows Marketplace. The Windows Phone Tools for Visual Studio and the special versions of Microsoft Visual Studio® Express and Microsoft Expression Blend® design software included in the Windows Phone Tools automatically create a XAP file that you can upload.

A XAP file contains a series of artifacts that are required to install the application on a Windows Phone. These include the following:

  • A valid Windows Phone application manifest file named WMAppManifest.xml, with the application title contained inthe <Title> element, and including a list of permission demands for device capabilities
  • A valid .NET Framework application manifest file named AppManifest.xaml that lists the assemblies used by the
  • The assembly (DLL) files specified in the AppManifest.xaml file
  • Any user controls or XAML files required by the application
  • The resources, icons, images, media, and other content files that the application requires; the application icon must be a 62-by-62 pixel PNG file, and the application tile image used on the Start screen must be a 173-by-173 pixel PNG file.

The maximum size of the XAP package file you can upload is 400 MB, including all the media or other content that is included in the XAP file but not compiled into the application as resources.

If the package to download to the phone exceeds 20 MB in size, users will not be able to download it from Windows Marketplace when using a GPRS or 3G phone connection. Packages over 20 MB in size must be downloaded and installed over a Wi-Fi or physical wired connection.

If your application requires a separate download in order to work, such as an additional data package, and if the separate download exceeds 50 MB, you must inform the user and obtain permission to download it.

The Windows Marketplace Repackaging Process

The validation process, which the Windows Marketplace applies to all submitted applications, automatically unpacks and then repackages the application. It unpacks all the contents of the submitted XAP file and performs a range of validation checks on these. Afterward, it creates a new WMAppManifest.xml file based on the results of the validation process and repackages all the original content with the new WMAppManifest.xml into the XAP package that users will download.

The updated WMAppManifest.xml file contains a product identifier that is used in Windows Marketplace to uniquely identify the application download package; it also includes an indicator of the application type (such as a music and videos hub application). However, the main feature of the validation process that it is important for developers to understand is the way that the security permission demands are applied in the new WMAppManifest.xml file.

By default, the WMAppManifest.xml file created by the Windows Phone Tools templates in Visual Studio and Expression Blend contains permission demands for all the device capabilities. These include networking, location, microphone, push notifications, and more. However, applications distributed through Windows Marketplace advertise the capabilities they require, and users can be sure that an application that does not indicate it uses (for example) the location service, actually will not use it internally without the user being aware.

To ensure that this is the case, the validation process searches the application code for calls to any of the device capabilities that require permission. If the way that these are used in the application meets the certification  requirements, the validation process includes that permission demand in the WMAppManifest.xml file and the product
metadata displayed by Windows Marketplace. If a feature is not used, the permission demand is removed from the  MAppManifest.xml file. Therefore, code can use only the features for which it is certified, and any attempt to use other features (such as using reflection to invoke a feature) will fail because the application does not have the relevant permission.

You can perform the same detection process as is carried out by the submission validation process on your own computer by using the Windows Phone Capability Detection Tool to check that the correct capabilities are detected.

The Windows Phone Capability Detection Tool (Capability Detection.exe) is installed by default in the  %ProgramFiles%MicrosoftSDKsWindows Phonev7.0ToolsCapDetect folder. For information on using this tool, see “How to: Use the Windows Phone Capability Detection Tool” on MSDN (http://msdn.microsoft.com/en-gb/library/gg180730(v=VS.92).aspx).

The Windows Phone Capability Detection Tool is part of the October 2010 update to the Windows Phone Tools, which is available from the Microsoft Download Center (http://www.microsoft.com/downloads/en/details.aspx?FamilyID=49B9D0C5-6597-4313-912A-F0CCA9C7D277&displaylang=en).

After the validation process completes, the XAP file is rebuilt, including all the original content plus a file named WMAppPRHeader. xml that contains the Digital Rights Management (DRM) information. The DRM information controls how and where the application can be used, and it manages the trial period where applicable.


Notification Payloads

Tile notifications include three items of data:

  • A background PNG or JPG image for the tile that should be 173 pixels by 173 pixels in size
  • A text label that overlays the background image
  • A count value that also overlays the background image; for example, the number of new surveys available

A toast notification includes two strings that appear in the toast: the first string is a header, and the second string is optional body text.

You must make sure that the strings you send on both tile and toast notifications fit in the available space.

The following code example from the TileNotificationPayload Builder class in the TailSpin.Web.Survey.Shared project shows how the Surveys service constructs a tile notification message.

public static byte[] Create(string title,
string backgroundImage = null, int count = 0)
using (var stream = new MemoryStream())
var settings = new XmlWriterSettings
{ Indent = true, Encoding = Encoding.UTF8 };
using (var writer = XmlWriter.Create(stream, settings))
if (writer != null)
writer.WriteStartElement(“wp”, “Notification”,
writer.WriteStartElement(“wp”, “Tile”, “WPNotification”);
writer.WriteStartElement(“wp”, “BackgroundImage”,
writer.WriteValue(backgroundImage ?? string.Empty);
writer.WriteStartElement(“wp”, “Count”,
writer.WriteValue(count == 0 ? string.Empty :
writer.WriteStartElement(“wp”, “Title”,
byte[] payload = stream.ToArray();
return payload;

The backgroundImage parameter is the URL of the image, which must be no more than 80 KB in size.

The following code example from the ToastNotificationPayload Builder class in the TailSpin.Workers.Notifications project shows how the Surveys service constructs a toast notification message.

public static byte[] Create(string text1, string text2 = null)
using (var stream = new MemoryStream())
var settings = new XmlWriterSettings { Indent = true,
Encoding = Encoding.UTF8 };
XmlWriter writer = XmlWriter.Create(stream, settings);
writer.WriteStartElement(“wp”, “Notification”,
writer.WriteStartElement(“wp”, “Toast”, “WPNotification”);
writer.WriteStartElement(“wp”, “Text1”, “WPNotification”);
writer.WriteStartElement(“wp”, “Text2”, “WPNotification”);
byte[] payload = stream.ToArray();
return payload;


Why Mobile?, The APK File

The mobile marketplace is a cruel world. Users often download an application on impulse, try it once, and may never open it again if their first impression is negative. You should aim for excellence and simplicity. Define your application in one sentence. Find your niche. Make it unique, if not in concept, then in execution.

Also, you should analyze mobile consumption. An application is often only used for a few minutes at a time. Tasks should be completed quickly or in incremental steps saved on the device. Whatever you do, do not attempt to replicate your existing website. Instead, adapt it for mobile or create a companion application.

The APK File

The default system icon is an Android-green icon. Icons need to be 36×36-, 48×48-, or 72×72-pixel PNG images. They are used for low-, medium-, and high-density screens, respectively.

You should make your icon stand out, while clearly identifying your application. Google provides guidelines on how to make icons, and provides a Photoshop template, at http://developer.android.com/guide/practices/ui_guidelines/icon_design.html.

In Flash Professional, go to File→AIR Android Settings→Icon and browse to the icons on your computer.

In Flash Builder, create a directory called icons in the src folder, and place your icons in it. Right-click on your project and select Properties. Then select ActionScript Build Packaging→Google Android→Package Contents and select the icons folder if it is not already selected.

In other tools, add a node to your application descriptor file as follows:


Alternatively, if you are using the command line, place all your icons in a folder called icons and add it to the end of your statement:

AIR-sdk-path/bin/adt -package -target apk -storetype pkcs12
-keystore yourCertificate.p12 hello.apk Main-app.xml Main.swf icons

Publish your application one last time. Change your application to release mode if it is in debug mode. In Flash Professional, select File→AIR Android settings. Under the Deployment tab, for “Android deployment type,” choose Release.

If you are publishing from the command line, verify that the target is apk:

AIR-sdk-path/bin/adt -package -target apk -storetype pkcs12
-keystore yourCertificate.p12 hello.apk Main-app.xml Main.swf


Resource Management and Performance

Like all mobile devices, Windows Phone 7 devices have limited processing, memory, and storage facilities compared to a desktop or laptop computer. It is important to take this into account when building applications that will run on Windows Phone 7.

The main areas of concern when designing mobile device applications are to manage device resource usage effectively and maximize performance on the device. Typically, maximizing performance involves optimizing memory utilization, maximizing use of the graphics processing unit (GPU), and more general issues, such as good programming practice. The following sections provide guidance on achieving these aims in your applications.

Minimize Device Resource Usage

reduce battery life. You should consider limiting use of these features when not necessary. For example, you should access the location service (which may initiate the built-in GPS) only when necessary, and turn it off when not required. Consider implementing options or power profiles in your applications that allow the user to minimize power use or turn off features of the device when not required.

Also consider how you can minimize the use of the network, Bluetooth, storage, video, sound, and background tasks. All of these require processor and memory resources, and they impose an additional load on the battery. For information about using the device capabilities, such as location services, and minimizing their impact on performance, see Appendix C, “Leveraging Device Capabilities.”

Apply Good Practice Programming Techniques

You must be aware of the work your application is doing and the use of resources within your code. Where possible, you should attempt to do the following:

  • Apply the optimizations you would use in any other .NET Framework code, such as caching the appropriate types and volumes of data, avoiding boxing (which occurs when you treat a value type as an object), and using lazy loading so that memory is only allocated to objects if they are actually used by the application.
  • Use asynchronous programming techniques to perform any complex processing tasks on background threads instead of on the UI thread. Use Dispatcher.BeginInvoke to initiate operations on the UI thread, and pass data between tasks on different threads using local variables, a memory buffer, or isolated storage. Also consider using the Reactive Extensions (Rx) to implement asynchronous execution for tasks. Rx can simplify asynchronous programming tasks and make code easier to read and assimilate.
  • Minimize the visual tree of objects and code execution. Less activity in the application will give better performance and reduce battery drain.
  • Minimize the size of objects that you redraw or animate within the UI and the number of operations (such as transitions or effects) you perform on the objects. If you use code to drive animations with per-frame callbacks, these execute on the UI thread and may appear less smooth than those on the compositor thread when the UI thread is busy.
  • Ensure that media you display or play (such as video or audio) is encoded at the appropriate size for display, and that it has the minimum acceptable frame or audio bit rate.
  • Minimize the processing that occurs in the Loaded event of the initial page, which will delay initial display of the application. Alternatively, consider using the splash screen (SplashScreen Image.jpg) that is included in each Windows Phone 7 project template. You can modify it to show a suitable image while the application loads.
  • Test regularly on a physical device, not just by using the emulator, because the performance of the two will vary dramatically based on the hardware available to each.

Optimize Memory Usage

running on Windows Phone 7 if you carefully control use of memory and isolated storage. You must also be aware that garbage collection in Windows Phone 7 devices works differently than it does in desktop and server versions of the .NET Framework. In Windows Phone 7, the garbage collector runs only when memory usage exceeds a specified level, and it can interrupt execution—which can, therefore, affect performance.

Where possible, you should attempt to do the following:

  • Optimize data downloads and store only data that is actually required on the device.
  • When accessing large sets of remote data, consider using data paging so that only the data that will be viewed is sent to the device.
  • If you will reuse objects you create in your code, consider holding them in memory instead of allowing them to be garbage collected and then recreating them. This reduces the frequency of garbage collection, which can interrupt program execution to a greater extent than in a desktop application. However, do not hold onto objects that will not be regularly reused, and consider minimizing the total number of object instances that you create in order to reduce memory requirements. Using a few large objects instead of a large number of small objects can also reduce garbage collection overhead, but you must also consider whether the performance gain outweighs the benefits of
    smaller, more-focused and decoupled objects that follow the single responsibility principle.
  • Consider creating or loading the objects and data you need when your application starts. If you minimize the requirement to allocate memory for new objects as the application runs, the garbage collector will run less often. If you use mainly value types and arrays of value types, you will minimize the time that the garbage collector takes to execute. However, you must balance the effects, depending on whether you require fast startup or faster execution after startup. Alternatively, consider downloading the data you need asynchronously on a background
    thread and allowing the application to start with some limit to its functionality and then gradually enable features as the data that they need becomes available. A dependency injection container can help you to manage object creation.
  • Consider calling the GC.Collect method to force garbage collection when the application is at a point where there is no interaction with the user and no major tasks are running in your application. This may be immediately after initializing when the main screen is displayed or at a suitable point where the user is reading content you have displayed. This reduces the chance that it will run at a more intrusive time and affect the interactivity of your application.
  • Consider simplifying the structure of your application components when using design patterns that provide decoupling (such as MVVM or Dependency Injection) if you can do so without fundamentally affecting the architecture or objectives of the  patterns. For example, you may be able to share a view model between two or more views.
  • Minimize the size of the application assemblies. Use a Build Action of Content (instead of Resource) for media, images, and other content to include them as files in the XAP file instead of as resources embedded within the assembly. However, if you are building a reusable component, you will probably need to embed the resources within the assembly for easier distribution.

Note: You can query the current memory availability and usage by your application using the DeviceExtendedProperties class in Windows Phone 7. For more information, see Appendix C of this book, “Leveraging Device Capabilities,” and “Device Information for Windows Phone” on MSDN (http://msdn.microsoft.com/en-us/library/ff941122(VS.92).aspx).

Maximize GPU Usage

Windows Phone 7 provides a background compositor thread that generates output for display and a relatively powerful GPU that maximizes performance for graphics and maintains an acceptable frame rate. However, you must take care to not negate this advantage through the use of code that cannot run on the GPU.

Where possible, you should attempt to do the following:

  • Minimize the code you execute on the UI thread, which handles input, the drawing of new visual elements, and calls to user code. Use the compositor thread to execute transitions and animations, and apply caching for bitmaps. The compositor thread handles opacity, scale transforms, translate transforms, rotate transforms, and plane projection. It does not handle opacity masks, non-rectangular clips, and texture for animated objects greater than 2000 × 2000 pixels.
  • Consider caching complex visual elements if they will be regularly reused. When you hide a visual element (visibility = collapsed) it is discarded and requires no processor utilization. This means that it must be completely recreated when you show that element again. Instead, consider setting the opacity to zero. This causes the device to keep the object, while still minimizing processor usage. However, you must balance this with memory usage.
  • Consider using images instead of creating complex visual elements in Extensible Application Markup Language (XAML) or code. This can considerably reduce processor load. If you need transparency, you must use a PNG image. If you do not need transparency, use a JPEG image because the render speed is higher for this image format.
  • Images larger than 2000 × 2000 pixels will be sampled at a lower resolution when displayed, with subsequent loss of quality. Store larger images in a WriteableBitmap and display only the appropriate section.

There are several modes you can enable when debugging. These are especially useful when maximizing graphics performance; they are automatically included in the code generated by the phone application template in Visual Studio. Graphics debug mode is set by the following properties of the Application.Current.Host.Settings class in the startup code in your App.xaml.cs file:

  • EnableFrameRateCounter. This displays a frame rate counter, memory usage information, and other useful data about the performance of the device. It shows the frame rates for the UI and the compositor threads, the amount of texture memory in use, the number of surfaces, and the fill rate. Ensure that the SystemTray.IsVisible static property is set to false in your application so that the system tray does not obscure the frame rate counter.
  • EnableCacheVisualization. This shows the graphics items that are being drawn by applying tint and transparency to each one to show where overlapping textures are being drawn on each frame update. Each tinted area represents a texture that is handed off to the GPU for composition.
  • EnableRedrawRegions. This shows the graphical items that are being redrawn in each frame update.

For more detailed information about the background compositor thread and maximizing performance on Windows Phone 7, see Creating High Performing Silverlight Applications for Windows Phone from the Microsoft Download Center (http://download.microsoft.com/download/B/2/7/B2748D7A-F368-4C33-B0F2-844CFE085193/SilverlightForWindowsPhonePerformance. zip). For more background information about performance optimization, see the video presentation, Silverlight Performance on Windows Phone, on the Mix 10 website (http://live.visitmix.com/MIX10/Sessions/CL60) and “WP7/SilverlightGraphics Performance” on andybeaulieu.com (http://www.andybeaulieu.

These and all links in this book are accessible from the book’s online bibliography. The URL to the bibliography can be found in the preface, in the final section, entitled, “Where to Go for More Information.”


Working with Shadows, Gradients, and Transformations

While rounded corners get a lot of attention, that’s just the beginning
of what we can do with CSS3. We can add drop shadows to elements to
make them stand out from the rest of the content, we can use gradients
to make backgrounds look more defined, and we can use transformations
to rotate elements. Let’s put several of these techniques together
to mock up a banner for the upcoming AwesomeConf, a trade show and
conference that AwesomeCo puts on each year. The graphic designer
has sent over a PSD that looks like Figure 8.3, on the next page. We
can do the badge, shadow, and even the transparency all in CSS. The
only thing we’ll need from the graphic designer is the background image
of the people.

The Basic Structure

Let’s start by marking up the basic structure of the page in HTML.

<div id=”conference”>
<section id=”badge”>
<h3>Hi, My Name Is</h3>
<section id=”info”>

We can style the basics with this:

# c o n f e r e n c e {
b a c k g r o u n d – c o l o r : #000;
w i d t h : 960px;
f l o a t : l e f t ;
background-image: u r l ( ‘ i m a g e s / a w e s o m e c o n f . j p g ‘ );
b a c k g r o u n d – p o s i t i o n : c e n t e r;
h e i g h t : 240px;

re-create using CSS3

t e x t – a l i g n : c e n t e r ;
w i d t h : 200px;
b o r d e r : 2px s o l i d b l u e ;
# i n f o {
m a r g i n : 20px;
p a d d i n g : 20px;
w i d t h : 660px;
h e i g h t : 160px;
#badge, # i n f o {
f l o a t : l e f t ;
b a c k g r o u n d – c o l o r : #fff;
#badge h2{
m a r g i n : 0;
c o l o r : red;
f o n t – s i z e : 40px;
#badge h3{
m a r g i n : 0;
b a c k g r o u n d – c o l o r : blue;
c o l o r : #fff;

Once we apply that style sheet to our page, we have our badge and
content region displayed side-by-side, as shown in Figure 8.4, on the
following page, so let’s start styling the badge.

Adding a Gradient

We can add definition to the badge by changing the white background
to a subtle gradient that goes from white to light gray. This gradient
will work in Firefox, Safari, and Chrome, but the implementation is
different for Firefox. Chrome and Safari use WebKit’s syntax, which
was the original proposal, whereas Firefox uses a syntax that’s close to
the W3C proposal. Once again, we’re using browser prefixes

b a c k g r o u n d – i m a g e : – m o z – 1 i n e a r – g r a d i e n t (
t o p , # f f f , # e f e f e f
b a c k g r o u n d – i m a g e : – w e b k i t – g r a d i e n t (
l i n e a r , l e f t t o p , l e f t b o t t o m ,
c o l o r – s t o p C O , # f f f ) ,
c o l o r – s t o p C l , # e f e f e f )
b a c k g r o u n d – i m a g e : 1i n e a r – g r a d i e n t (
t o p , # f f f , # e f e f e f

Firefox uses the -moz-linear-gradient method, in which we specify the
starting point of the gradient, followed by the starting color, and, finally,
the ending color.

WebKit-based browsers let us set color stops. In our example, we only
need to go from white to gray, but if we needed to add colors, we’d just
need to add an additional color stop in the definition.

Adding a Shadow to the Badge

We can easily make the badge appear to be sitting above the banner by
adding a drop shadow. Traditionally, we’d do this shadow in Photoshop
by adding it to the image or by inserting it as a background image.
However, the CSS3 box-shadow property lets us quickly define a shadow
on our elements.

We’ll apply this rule to our style sheet to give the badge a shadow:

-moz-box-shadow: 5px 5px 5px #333;
– w e b k i t – b o x – s h a d o w : 5px 5px 5px #333;
– o – b o x – s h a d o w : 5px 5px 5px #333;
box-shadow: 5px 5px 5px #333;

The box-shadow property has four parameters. The first is the horizontal
offset. A positive number means the shadow will fall to the right of the
object; a negative number means it falls to the left. The second parameter
is the vertical offset. With the vertical offset, positive numbers make
the shadow appear below the box, whereas negative values make the
shadow appear above the element.

The third parameter is the blur radius. A value of 0 gives a very sharp
value, and a higher value makes the shadow blurrier. The final parameter
defines the color of the shadow.

You should experiment with these values to get a feel for how they work
and to find values that look appropriate to you. When working with
shadows, you should take a moment to investigate how shadows work
in the physical world. Grab a flashlight and shine it on objects, or go
outside and observe how the sun casts shadows on objects. This use
of perspective is important, because creating inconsistent shadows can
make your interface more confusing, especially if you apply shadows to
multiple elements incorrectly. The easiest approach you can take is to
use the same settings for each shadow you create.

Rotating the Badge

You can use CSS3 transformations to rotate, scale, and skew elements
much like you can with vector graphics programs such as Flash, Illustrator,
or Inkscape.6 This can help make elements stand out a bit more
and Is another way to make a web page not look so “boxy.” Let’s rotate
the badge just a bit so It breaks out of the straight edge of the banner.

– m o z – t r a n s f o r m : r o t a t e ( – 7 . 5 d e g ) ;
– o – t r a n s f o r m : r o t a t e ( – 7 . 5 d e g ) ;
– w e b k i t – t r a n s f o r m : r o t a t e ( – 7 . 5 d e g ) ;
– m s – t r a n s f o r m : r o t a t e ( – 7 . 5 d e g ) ;
t r a n s f o r m : r o t a t e ( – 7 . 5deg) ;

Rotation with CSS3 Is pretty simple. All we have to do Is provide the
degree of rotation, and the rendering just works. All the elements contained
within the element we rotate are rotated as well.
Rotating Is just as easy as rounding corners, but don’t overuse It. The
goal of Interface design Is to make the Interface usable. If you rotate
elements containing a lot of content, ensure that your viewers can read
the content without turning their heads too far In one direction!

Transparent Backgrounds

Graphic designers have used semitransparent layers behind text for
quite some time, and that process usually involves either making a
complete image in Photoshop or layering a transparent PNG on top of
another element with CSS. CSS3 lets us define background colors with
a new syntax that supports transparency.

When you first learn about web development, you learn to define your
colors using hexadecimal color codes. You define the amount of red,
green, and blue using pairs of numbers. 00 is “all off’ or “none,” and FF
is “all on.” So, the color red would be FF0000 or “all on for red, all off for
blue, and all off for green.”

CSS3 introduces the rgb and rgba functions. The rgb function works
like the hexadecimal counterpart, but you use values from 0 to 255 for
each color. You’d define the color red as rgb(255,0,0).

The rgba function works the same way as the rgb function, but it takes
a fourth parameter to define the amount of opacity, from 0 to 1. If you
use 0, you’ll see no color at all, because it’s completely transparent. To
make the white box semitransparent, we’ll add this style rule:

# i n f o {
b a c k g r o u n d – c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 9 5 ) ;

When working with transparency values like this, your users’ contrast
settings can sometimes impact the resulting appearance, so be sure to
experiment with the value and check on multiple displays to ensure
you get a consistent result.

While we’re working with the info section of our banner, let’s round the
corners a bit.

# i n f o {
m o z – b o r d e r – r a d i u s : 12px;
w e b k i t – b o r d e r – r a d i u s : 12px;
o – b o r d e r – r a d i u s : 12px;
b o r d e r – r a d i u s : 12px;

With that, our banner looks pretty good in Safari, Firefox, and Chrome.
Now let’s implement a style sheet for Internet Explorer.

Falling Back

The techniques we used in this section work fine in IE 9, but they’re
all possible with Internet Explorer 6, 7, and 8 too! We just have to use
Microsoft’s DirectX filters to pull them off. That means we’ll want to
rely on a conditional comment to load a specific IE-only style sheet.
We’ll also need to use JavaScript to create the section element so we
can style it with CSS since these versions of IE don’t recognize that
element natively.

<! — [ i f l t e IE 8]>
document.createElement(“section”) ;
<link rel=”stylesheet” href=”ie.css” type=”text/css” media=”screen”>
< ! [ e n d i f ] – – >
<div id=”conference”>
<section id=”badge”>
<h3>Hi, My Name Is</h3>
<section id=”info”>

The DirectX filters work in IE 6, 7, and 8, but in IE 8 the filters are
invoked differently, so you’ll be declaring each of these filters twice.
Let’s start by looking at how we rotate elements.


We can rotate elements using these filters, but it’s not as easy as just
specifying a degree of rotation. To get the effect we want, we need to

use the Matrix filter and specify cosines and sines of the angle we want.
Specifically, we need to pass the cosine, the negative value of sine, the
sine, and the cosine again,7 like this:

f i l t e r : p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . M a t r i x(
s i z i n g M e t h o d = ‘ a u t o expand’,
– m s – f i l t e r : ” p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . M a t r i x(
s i z i n g M e t h o d = ‘ a u t o expand’,
) ” ;

Complicated? Yes, and more so when you look at the previous example
more closely. Remember that our original angle was negative 7.5
degrees. So, for our negative sine, we need a positive value, and our
sine gets a negative value.

Math is hard. Let’s make gradients instead.


IE’s Gradient filter works just like the one in the standard, except that
you have to type a lot more characters. You provide the starting color
and the ending color, and the gradient just shows up.

f i I t e r : progi d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # F F F F F F , endColorStr=#EFEFEF
– m s – f i l t e r : ” p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . g r a d i e n t(
s t a r t C o l o r S t r = # F F F F F F , endColorStr=#EFEFEF

Unlike the other browsers, you’re applying the gradient directly to the
element, rather than to the background-image property.

Let’s use this filter again to define the transparent background for our
info section.


The Gradient filter can take extended hexadecimal values for the start
and end colors, using the first two digits to define the amount of transparency.
We can get very close to the effect we want with this code:

background: none;
f i l t e r :
p r o g i d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # B B F F F F F F , endColorStr=#BBFFFFFF
– m s – f i l t e r : ” p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . g r a d i e n t(
s t a r t C o l o r S t r = ‘ # B B F F F F F F ‘ , EndColorStr=’#BBFFFFFF’

These eight-digit hex codes work very much like the rgba function,
except that the transparency value comes first rather than last. So,
we’re really looking at alpha, red, green, and blue.
We have to remove the background properties on that element to make
this work in IE 7. Now, if you’ve been following along trying to build this
style sheet up, you’ve noticed that it doesn’t actually work yet, but we
can fix that.

Putting It All Together

One of the more difficult problems with these IE filters is that we can’t
define them in pieces. To apply multiple filters to a single element, we
have to define the filters as a comma-separated list. Here’s what the
actual IE style sheet looks like:

# i n f o {
background: none;
f i l t e r :
p r o g i d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # B B F F F F F F , endColorStr=#BBFFFFFF
– m s – f i l t e r : ” p r o g i d ¡ D X I m a g e T r a n s f o r m . M i c r o s o f t . g r a d i e n t(
s t a r t C o l o r S t r = ‘ # B B F F F F F F ‘ , EndColorStr=’#BBFFFFFF’
) ” ;

f i l t e r :
p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . M a t r i x C
s i z i n g M e t h o d = ‘ a u t o expand’,
p r o g i d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # F F F F F F , endColorStr=#EFEFEF ),
progid:DXImageTransform.Mi c r o s o f t . S h a d o wC
c o l o r = # 3 3 3 3 3 3 , D i r e c t i o n = 1 3 5 , Strength=3
– m s – f i l t e r : ” p r o g i d ¡ D X I m a g e T r a n s f o r m . M i c r o s o f t . M a t r i xC
s i z i n g M e t h o d = ‘ a u t o expand’,
p r o g i d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # F F F F F F , endColorStr=#EFEFEF ),
progid:DXImageTransform.Mi c r o s o f t . S h a d o wC
c o l o r = # 3 3 3 3 3 3 , D i r e c t i o n = 1 3 5 , Strength=3
) ” ;

That’s a lot of code to get the desired result, but it shows that it is
possible to use these features. If you look at Figure 8.5, you’ll see we
got pretty close. All we have to do now is round the corners on the infosection, and you can refer to Rounding Rough Edges, on page 146 to
see how to do that.
Although these filters are clunky and a little bit quirky, you should still
investigate them further in your own projects because you’ll be able to
provide a similar user experience to your IE users.
Remember that the effects we explored in this section are all presentational.
When we created the initial style sheet, we made sure to apply
background colors so that text would be readable. Browsers that cannot
understand the CSS3 syntax can still display the page in a readable




Drawing a Logo

The canvas element is a container element much like the script element. It’s a blank slate we can draw on. We define a canvas with a width and height like this:

<canvas id=”my_canvas” width=”150″ height=”150″>
Fallback content here

Unfortunately, you can’t use CSS to control or alter the width and height of a canvas element without distorting the contents, so you need to decide on your canvas dimensions when you  declare it.

We use JavaScript to put shapes on the canvas. Even if you provided fallback content to those browsers without the canvas element, you still need to prevent the JavaScript code from trying to manipulate it. Find the canvas by its ID, and see whether the browser supports the canvas’ getContext method.

var canvas = document.getElementById(‘my_canvas’);
if (canvas.getContext){
var context = canvas.getContext(‘2d’);
// do something to show the canvas’ hidden contents
// or let the browser display the text within the <canvas> element.

If we get a response from the getContext method, we grab the 2D context for the canvas so we can add objects. If we don’t have a context, we need to devise a way to display the fallback content. Since we know that the canvas element requires JavaScript in order to work, we’re building a framework to handle fallbacks from the beginning.

Once you have the canvas’ context, you simply add elements to that context. To add a red box, you set the fill color and then create the box, like this:


context.fi 11 Style = “rgb(200,0,0)” ;
context.fillRect (10, 10, 100, 100);

The canvas’s 2D context is a grid, with the top-left corner as the default origin. When you create a shape, you specify the starting X and Y coordinates and the width and height.

Each shape is added onto its own layer, so you could create three boxes with a 10-pixel offset, like this:

context, f i l l Style = “rgb(200,0,0)”;
context. fillRect (10, 10, 100, 100);
context.fill Style = “rgb(0,200,0)”;
context.fillRect (20, 20, 100, 100);
context.fill Style = “rgb(0,0,200)”;
context.fillRect (30, 30, 100, 100);

and they’ll stack on top of each other, like this:

Now that you have an understanding of the basics of drawing, let’s put together the AwesomeCo logo. It’s pretty simple, as you can see from Figure 6.1, on the following page.

Drawing the Logo

The logo consists of a string of text, an angled path, a square, and a triangle. Let’s start by creating a new HTML5 document, adding a canvas element to the page, and then creating a JavaScript function for drawing the logo, which detects whether we can use the 2D canvas.

var drawLogo = function(){
var canvas = document.getElementByld(‘logo’);
var context = canvas.getContext(‘2d’);

We invoke this method after first checking for the existence of the canvas
element, like this:

var canvas = document.getElementByld(‘logo’);
if (canvas.getContext){
drawLogo() ;

Notice here we’re using the j Query function again to ensure that the
event fires when the document is ready. We’re looking for an element
on the page with the ID of logo, so we’d better make sure we add our
canvas element to the document so it can be found, and our detection
will work.


<canvas id=”logo” width=”900″ height=”80″>


Next, let’s add the “AwesomeCo” text to the canvas.

Adding Text

Adding text to the canvas involves choosing a font, a font size, and an
alignment, and then applying the text to the appropriate coordinates
on the grid. We can add the text “AwesomeCo” to our canvas like this:

context.font = ‘italic 40px sans-serif’;
context.textBaseline = ‘top’;
context.fillTextC’AwesomeCo’, 60, 0);

We’re defining the text type and setting its baseline, or vertical alignment,
before we apply it to the canvas. We’re using the fillText method
so we get text that’s filled in with the fill color, and we’re setting it 60
pixels to the right so we can make room for the large triangle-shaped
path we’ll draw next.

Drawing Lines

We draw lines on the canvas by playing a game of “connect-the-dots.”
We specify a starting point on the canvas grid and then specify additional
points on the grid to move to. As we move around the canvas, the
dots get connected, like this:

Drawing LinesWe use the beginPathO method to start drawing a line, and then we
create our path, like this:

context.lineWidth = 2;
context.begi nPath Q;
context.moveTo(0, 40);
context.lineTo(30, 0);
context.lineTo(60, 40 );
context.lineTo(285, 40 );
context. strokeO ;
context.closePathQ ;

When we’re all done moving around the canvas, we have to call the
stroke method to draw the line and then call the closePath method to
stop drawing.
Now all that’s left is the box and triangle combination that sits within
the big triangle.

Moving the Origin

We need to draw a small square and triangle inside the larger triangle.
When we draw shapes and paths, we can specify the X and Y coordinates
from the origin at the top-left corner of the canvas, but we can
also just move the origin to a new location.
Let’s draw the smaller inner square by moving the origin.

context.translate(20,20) ;

Notice that before we move the origin, we call the save() method. This
saves the previous state of the canvas so we can revert easily. It’s like
a restore point, and you can think of it as a stack. Every time you
call save(), you get a new entry. When we’re all done, we’ll call restoreO,
which will restore the top savepoint on the stack.

Now let’s use paths to draw the inner triangle, but instead of using a
stroke, we’ll use a fill to create the illusion that the triangle is “cutting
into” the square.

context.fi11 Style = ‘#fff’;
context.strokeStyle = ‘#fff’;
context.lineWidth = 2;
context.beginPathO ;
context.moveTo(0, 20);
context.lineTo(10, 0);
context.lineTo(20, 20 ) ;
context.lineTo(0, 20 );
context.closePathO ;
context. restoreO ;

Here we set the stroke and fill to white (#fff) before we begin drawing.
Then we draw our lines, and since we moved the origin previously, we’re
relative to the top-left corner of the square we just drew.

We’re almost done, but it needs a little color.

Adding Colors

In Section 13, Moving the Origin, on the previous page, you saw briefly
how to set the stroke and fill color for the drawing tools. We could set
the color of everything to red just by adding this code before we draw

context.fi11 Style = “#fOO”;
context.strokeStyle = “#fOO”;

But that’s a little boring. We can create gradients and assign those to
strokes and fills like this:

// context.fi11 Style = “#fOO”;
// context.strokeStyle = “#fOO”;
var gradient = context.createLinearGradient(0, 0, 0, 40);
gradient.addColorStopCO, ‘#a00’); // red
gradient.addColorStop(l, ‘#f00’); // red
context.fi11 Style = gradient;
context.strokeStyle = gradient;

We just create a gradient object and set the color stops of the gradient.
In this example, we’re just going between two shades of red, but we
could do a rainbow if we wanted.2

Note that we have to set the color of things before we draw them.
At this point, our logo is complete, and we have a better understanding
of how we draw simple shapes on the canvas. However, versions of
Internet Explorer prior to IE9 don’t have any support for the canvas
element. Let’s fix that.

Falling Back

Google released a library called ExplorerCanvas3 that makes most of
the Canvas API available to Internet Explorer users. All we have to do
is include this library on our page:

<! — [ i f lte IE 8]>
<script src=”javascripts/excanvas.js”x/script>

and things should work just fine in Internet Explorer—but they don’t
work just yet. At the time of writing, the most stable release doesn’t support
adding text at all, and the version from the Subversion repository4
doesn’t use the correct fonts. Also, there’s no support yet for adding
gradients on strokes with this library.

So, instead, we rely on other solutions, such as placing a PNG of the
logo inside the canvas element, or we simply don’t use the canvas at all.
Since this was just an exercise to show you how to draw, it’s not the end
of the world if we can’t use this particular example in a cross-platform
production system yet.