Chapter 21. Working with bitmap images

Images are used on web pages all across the internet. They're used in the form of application icons, corporate logos, and photos of you and your friends. It's been quite a while since I've seen even a regular forms-over-data application that didn't have images in the UI somewhere. Obviously, bitmap-based images have become a mainstay of application design.

Naturally, Silverlight includes mechanisms for displaying this content through the Image element and the MultiScaleImage control.

In addition to displaying images, Silverlight includes the powerful ability to create images from scratch or from other elements using the WriteableBitmap type. You can even use the WriteableBitmap to provide support for formats not natively supported in Silverlight (such as .GIF).

In this chapter, we'll start with the basics of imaging with the Image element. From there, we'll move on to creating images on the fly using the WriteableBitmap. Once we have a handle on the WriteableBitmap, we'll turn to Silverlight's answer to enormous gigapixel-level images or collections of images-Deep Zoom with the MultiScaleImage control. Finally, we'll wrap up the chapter with a discussion of the different ways of stretching content to fit the space allotted.

21.1 Basic imaging

The Image element enables you to display images from across the internet. In addition to loading images relative to your project, the Image element allows you to retrieve images from another domain. Take a look at how listing 21.1 uses the Source property to get an image from the http://www.silverlightinaction.com web site.

Example 21.1. An Image element that uses a picture from another domain

An Image element that uses a picture from another domain

The Image in this markup retrieves an image from the silverlightinaction.com domain. This image is referenced through the Source property, which is set with a Uri through XAML. If this property needs to be set programmatically, you must use an ImageSource instead. Because this abstract class can't be used directly, you use a derivation known as BitmapImage. This class name is a little misleading because only the types listed in table are 21.1 supported.

Table 21.1. Image formats supported by the Bitmap-Image class. These formats are inherently supported formats of the Image element.

Format

Extension(s)

Joint Photographic Experts Group

.jpg, .jpeg, .jpe, .jfif, .jfi, .jif

Portable Network Graphics

.png

This table shows the image formats supported by the BitmapImage class. Because this is the type used by the Source property, these image formats also represent those supported by the Image element in general.

What are you to do if you want to load an image type that's unsupported by Silverlight, such as .gif or .bmp? In the previous chapter, we saw how you can use the MediaStream-Source API to provide a hook to use when a video or audio format is unsupported. Luckily, Silverlight includes an equivalent for still images, the WriteableBitmap.

21.2 Creating images at runtime

The WriteableBitmap (sometimes referred to as The Bitmap API) was introduced in Silverlight 3. It provides the ability to generate new images based on existing images, onscreen UI elements, or from scratch using pixel manipulation.

WriteableBitmap is a class in the System.Windows.Media.Imaging namespace, deriving from the common BitmapSource base class. Deriving from that class allows us to use the WriteableBitmap in almost every place you could normally use any other type of bitmap image class.

The uses for this feature are numerous, and all over the map. I've personally used it to generate Windows 7-style window thumbnails in a large Silverlight business application for a customer. I've seen others use it in games, for destructive 2D UI (think Lemmings where a bomb takes a chunk out of the ground). Still others have built their own paint programs using this feature.

In this section, we'll look at the three main ways to use the WriteableBitmap class: creating editable bitmaps from existing images, creating bitmaps from portions of the visual tree, and creating bitmaps from scratch.

Before we do that, we'll have the usual project setup to do. In this case, create a new Silverlight project and modify the MainPage.xaml markup to look like listing 21.2.

Example 21.2. MainPage.xaml for the WriteableBitmap examples

MainPage.xaml for the WriteableBitmap examples
MainPage.xaml for the WriteableBitmap examples

The markup includes a StackPanel MainPage.xaml for the WriteableBitmap examplesthat we'll use for our visual tree rendering example, including an image of me at three years old that we'll use to test creating from existing images, and a result bitmap MainPage.xaml for the WriteableBitmap examplesthat will display the writeable bitmap we create in the code-behind.

You likely don't have a picture of me at three years old hanging around (if you do, we probably need to chat), so pick any old jpeg you have on your machine and drag it into the Silverlight project as a resource, using it instead.

Our first trial of the WriteableBitmap class is going to be to create a new image from an old one.

21.2.1 Creating from existing images

If you're creating a photo-manipulation program, you'll likely want to create a WriteableBitmap from an existing image. That new bitmap will enable you to access the pixels to allow for drawing, erasing, recoloring, and pretty much anything else you can write code for.

There are some restrictions when creating images from existing images. If you download the image from another server—in other words, a cross-domain call like we discussed in chapter 14—you won't be able to access the individual pixels of the image. In our example, we'll use an image that already exists in our project. The resulting application will look like figure 21.1 once you click the Capture button.

This example shows how to use one bitmap as a source to the writeable bitmap. Once you have the image in a writeable bitmap, you can manipulate it all you'd like (within the cross-domain restrictions I mentioned). Double-click the Capture button to create an event handler, then place this code in the handler:

BitmapSource source = SourceImage.Source as BitmapSource;
WriteableBitmap bmp = new WriteableBitmap(source);
ResultBitmap.Source = bmp;

Of course, you can also load the image from a URL. This code, used in place of the previous example, shows how:

A WriteableBitmap (right) created from another bitmap (left)

Figure 21.1. A WriteableBitmap (right) created from another bitmap (left)

Uri uri =
    new Uri("BitmapApi;component/Pete3YearsOld.jpg",
    UriKind.Relative);
StreamResourceInfo res = Application.GetResourceStream(uri);

BitmapImage image = new BitmapImage();
image.SetSource(res.Stream);
WriteableBitmap bmp = new WriteableBitmap(image);

ResultBitmap.Source = bmp;

This example shows how to create a WriteableBitmap from an existing image that hasn't necessarily been loaded into an Image element onscreen.

The convoluted loading scheme is required only because this file is a resource in the Silverlight project. If it's just a normal file on the server, you could've passed the URI directly to the BitmapImage constructor.

Note

The image is loaded asynchronously; the data isn't available until the BitmapImage.ImageLoaded event has fired. This is especially important when working with images from external servers.

Another way to use the WriteableBitmap class is to create a rendering of a portion of the visual tree.

21.2.2 Creating from UI elements

The WriteableBitmap class can be used to take a snapshot of all or a portion of the visual tree. This allows you to easily create thumbnails of large forms for a Windows 7 taskbar-like effect, or capture still frames from videos playing in a MediaElement. Note that cross-domain pixel-access checks are enforced, so if anything in the tree fails the cross-domain check, everything will.

As it turns out, creating a snapshot of a portion of the video tree is extremely simple. You pass the root element of the branch of the tree into the constructor of WriteableBitmap, along with an optional render transform. For example, see figure 21.2 for a direct 1:1 representation.

To create the bitmap version of the UI as shown in figure 21.2, you only need a couple of lines of code. Place these in the click event handler in place of the other code shown so far:

WriteableBitmap bmp = new WriteableBitmap(Elements, null);
ResultBitmap.Source = bmp;

Elements is the name of the StackPanel containing the four elements. The fidelity of the capture is close, but not perfect. For example, you lose ClearType rendering for fonts, so most text will look a little different. Of course, if you pass in a render transform (to rotate, skew, resize), it'll definitely look different.

The final approach is to create an image from scratch. We'll discuss direct pixel access at the same time; it applies to all three approaches.

The elements to the left are live elements in the visual tree. On the right, you can see the bitmap representation of those elements, captured while the Capture button was clicked.

Figure 21.2. The elements to the left are live elements in the visual tree. On the right, you can see the bitmap representation of those elements, captured while the Capture button was clicked.

DIRECT PIXEL ACCESS

The third approach to using the WriteableBitmap is to create an image from scratch. This is useful when you want to create an image from code or allow the user to draw on an empty canvas using the mouse.

The direct pixel access techniques shown here also work for any of the previous approaches, once the base image is loaded. Keep in mind that cross-domain images don't allow direct pixel access, and you'll get an exception if you try to do so.

To create an image from scratch, you need only provide dimensions, like this:

WriteableBitmap bmp = new WriteableBitmap(640, 480);

Then you're free to start working with the image. The pixels are manipulated using the Pixels property, which returns an array of integers. Each 32-bit integer represents one pixel in pARGB (premultiplied alpha, red, green, blue) format. This example iterates through the array, setting completely random values for the pixels:

Random random = new Random();
for (int i = 0; i < bmp.Pixels.Length; i++)
    bmp.Pixels[i] = random.Next();

ResultBitmap.Source = bmp;

That's interesting, but not particularly helpful, as it doesn't show how to set a specific color. Setting a single pixel to a specific color is just as easy:

Color c = Colors.Orange;
bitmap.Pixels[i] = c.A << 24 | c.R << 16 | c.G << 8 | c.B;

The shifting combined with the bitwise OR operation packs the four values into their correct position within the integer. To get an existing value, the code is a little more verbose, but still amounts to the reverse of putting the pixel:

int pixel = bitmap.Pixels[i]
Color c = Color.FromArgb((byte)(pixel >> 24),
                         (byte)(pixel >> 16),
                         (byte)(pixel >> 8),
                         (byte)(pixel));

In this example, the variable c will contain the correct color code for the pixel at position i. The bitshift operators and byte masking take care of getting the correct values from the correct positions in the integer.

One interesting use of all this is to create new images using an algorithm. One of the most impressive and best-known algorithms is the Mandelbrot fractal. We'll close the section on the WriteableBitmap by creating our own little Mandelbrot fractal generator.

21.2.3 A Mandelbrot fractal generator

I love fractals. A number of the desktop wallpapers I created and offer through my personal site were generated using fractal explorer programs such as Ktaza (no longer available). In this section, we'll build a simple visualizer for the Mandelbrot set, a common fractal. The Silverlight application will be able to produce results like figure 21.3. For efficiency, we'll simply modify the same project we've been working with throughout this section. Inside the button click handler, add the code from listing 21.3. This code generates a Mandelbrot fractal, coloring it using the escape time algorithm.

The WriteableBitmap sample application modified to show a Mandelbrot fractal

Figure 21.3. The WriteableBitmap sample application modified to show a Mandelbrot fractal

Example 21.3. Mandelbrot fractal in WriteableBitmap

Mandelbrot fractal in WriteableBitmap
Mandelbrot fractal in WriteableBitmap

Listing 21.3 shows how to generate a simple Mandelbrot fractal with coloration based on the escape time algorithm. The code to make this work in Silverlight is straightforward. First, I build a color table Mandelbrot fractal in WriteableBitmapto be used by the escape time algorithm. The colors simply fade from white to dark blue. I then create the WriteableBitmap instance.

The majority of the remaining code is an implementation of the Mandelbrot fractal algorithm. Should you want to modify the scale of the content, the zoom variable is a good place to begin.

The final step is to set the source of the bitmap onscreen to be the WriteableBitmap Mandelbrot fractal in WriteableBitmap. This is consistent with the other examples.

When I run this on my PC, the 1024 × 768 fractal generates and displays in subsecond time. I remember in the '80s I had a CBM-BASIC program that generated a Mandelbrot set, and it ran all night, just to create a 320 × 200 image. Impressive.

WriteableBitmap enables a number of important scenarios. First, you can use it to duplicate an existing image to prepare it for editing. Second, you can take a snapshot of a portion of the visual tree in order to create a thumbnail, or to snap a frame of a video. Third, you can generate images, or modify existing images, entirely from code. The scenarios for WriteableBitmap reach across all types of applications from games, to image manipulation, to forms-over-data business applications. Plus, just about anywhere you can use an Image, you can use a WriteableBitmap.

Tip

WriteableBitmap requires a fair bit of effort to use for generating shapes or lines. For those situations, you have to do the math to plot pixels efficiently. The WriteableBitmapEx library on CodePlex at http://writeablebitmapex.codeplex.com builds upon the WriteableBitmap to add support for these and other functions.

The Image element will support images up to 4 gigapixels (four billion pixels) in size. As you can imagine, using images this large can force your users to endure painful wait times. In addition, with advancements in digital photography and photo-stitching, what are you supposed to do with images larger than 4 gigapixels? To address these types of situations, Silverlight exposes a slick feature called Deep Zoom.

21.3 Deep Zoom

Deep Zoom is a feature of Silverlight that enables users to explore groupings of highresolution images. Traditionally, viewing high-resolution images over the internet is associated with painful wait times because high-resolution images are generally larger in size. Silverlight's Deep Zoom feature removes the usual long wait times. In addition, Deep Zoom natively allows users to drill into an image and see its most intricate details. All this is delivered in a smooth viewing experience that runs with unprecedented performance. This kind of experience is made possible by the MultiScaleImage control.

The MultiScaleImage control, similar to the Image control, has the ability to efficiently show incredibly high-resolution images. These images can be zoomed into, giving the user a close-up view of the content. Alternatively, the user can zoom away from the image to get a bird's-eye view of the image. Either way, these zooming features are constrained to an area known as the viewport. This viewport can be zoomed into, zoomed out of, and moved around the surface of an image, but this image really isn't an image at all—it's a collection of images typically created by Deep Zoom Composer, a free tool from Microsoft.

21.3.1 Showing an image

Showing an image within a MultiScaleImage control is nearly identical to showing an image within an Image control. As with the Image control, the only property you need to set to display an image is Source . This property is a MultiScaleTileSource that can be used to reference a file, but this file isn't a typical image file. Instead, this file is an XML file, known as the Deep Zoom image (DZI) file, that describes a multiscale image. We'll share more about this file type in a moment; for now, look at how a MultiScaleImage is created in XAML:

<MultiScaleImage x:Name="myMultiScaleImage"
  Source="images/dzc_output.xml" />

This shows the XAML to load a relative multiscale image at design time. In the event that you need to load a multiscale image at runtime, you use a DeepZoomImageTileSource instance. This type derives from the abstract base class MultiScaleTileSource, so it can be used at runtime as shown here:

myMultiScaleImage.Source = new DeepZoomImageTileSource(
  new System.Uri("images/dzc_output.xml", UriKind.Relative));

This line of code shows how to load a multiscale image at runtime. If the referenced image can't be found, the MultiScaleImage object's ImageOpenFailed event will be fired. If the image is found, the ImageOpenSucceeded event will be triggered and the image will be shown. Once this happens, you may consider giving your user the ability to zoom in and out of the high-resolution image.

21.3.2 Zooming in and out

The MultiScaleImage control has the ability to show an extremely high-resolution image. This control helps remove the traditional limitations associated with screen real estate. This is accomplished by enabling your users to zoom in from a view as if they were standing on top of a mountain.

Zooming within a MultiScaleImage is handled by a method called ZoomAboutLogicalPoint. The ZoomAboutLogicalPoint method takes three parameters that describe the zoom attempt. The first parameter determines how much to zoom by. The second and third parameters specify from where in the image the zoom originates. These parameters and the ZoomAboutLogicalPoint method are shown in listing 21.4.

Example 21.4. Implementing zoom functionality

Implementing zoom functionality
Implementing zoom functionality

This listing looks like a lot of code. In short, this code enables the user to zoom in or out of the MultiScaleImage defined earlier. To enable this functionality, you first listen for a keypress Implementing zoom functionality. If the keypress is made from the Shift key, the user is saying to zoom away from the image. If the Shift key hasn't been pressed, the zooming feature will default to zooming in, so you also need to listen for a user releasing a key through the KeyUp event. This event resets the zoom mode to the default after the Shift key has been pressed. The real meat of this feature is demonstrated when the user clicks the Multi-ScaleImage Implementing zoom functionality. This action forces a call to the ZoomAboutLogicalPoint method, which zooms according to the three parameters passed to it.

The first parameter passed to the ZoomAboutLogicalPoint method determines how to zoom on the image. If this double value is less than 1, the method will zoom away from the image. If the value is greater than 1, the method will zoom into the image. Either way, you can play around with this value to also adjust the speed in which the zoom is applied. To determine where the zoom begins, you must rely on the second and third parameters.

The second parameter represents the logical x coordinate to zoom from, the third parameter represents the logical y coordinate to zoom from. Both parameters are double values that fall between 0 and 1. The fact that these values fall between 0 and 1 is what defines them as part of a logical coordinate system, but most items use a standard Cartesian coordinate system. Fortunately, the MultiScaleImage class exposes two methods that enable you to convert Point objects between the two coordinate systems. The methods are:

  • ElementToLogicalPoint

  • LogicalToElementPoint

ElementToLogicalPoint converts a Cartesian Point to a logical Point. This is generally used to convert the position of the mouse cursor before zooming because the ZoomAboutLogicalPoint method expects a logical point. Here's an example, assuming the user clicked the mouse at 125,200:

Point cartesianPoint = new Point(125, 200);
Point logicalPoint = myMultiScaleImage.ElementToLogicalPoint(cartesianPoint);
myMultiScaleImage.ZoomAboutLogicalPoint(1.5,
  logicalPoint.X, logicalPoint.Y);

LogicalToElementPoint converts a logical Point to a Cartesian Point. This allows you to work with a point in a more familiar interface. Here's an example of how to use this method:

Point logicalPoint = new Point(0.25, 0.75);
Point cartesianPoint =
myMultiScaleImage.LogicalToElementPoint(logicalPoint);

Together, these examples show how to convert between the two coordinate systems. The logical coordinate system is necessary because it's used for two important tasks. The first task is zooming in and out of an image—which you just saw. The other major task is selecting what part of a multiscale image to zoom in on. You'll learn how to do this in a moment. Regardless of your task, both require some knowledge of how to manage the viewport.

21.3.3 Managing the viewport

The viewport is a rectangular region used to view a specific area of an image. This region enables you to zoom in and focus on the details of a specific part of an image, so you may want to think of the viewport as a way to interact with an image three-dimensionally. By default, this region is the size of the entire MultiScaleImage control, but you can change the size of the viewport through the ViewportWidth property.

The ViewportWidth property sets the size of a viewport in relation to the logical coordinate space. Anytime you change the ViewportWidth property, it'll be in relation to the hosting MultiScaleImage. For instance, if you set the ViewportWidth property to 1.0, the viewport will be the same size as the hosting MultiScaleImage control. If you change the ViewportWidth property to a double greater than 1.0, you'll make the viewport larger than the MultiScaleImage. This approach would give the user a sense of zooming away from the image. Alternatively, you can focus on a smaller portion of a multiscale image by providing a value less than 1.0. Figure 21.4 illustrates this zooming-away effect.

A MultiScaleImage with a ViewportWidth of 0.33

Figure 21.4. A MultiScaleImage with a ViewportWidth of 0.33

This figure shows the effects of changing the ViewportWidth property to a double less than 1.0. The figure on the left represents the original multiscale image. As you can see, the Silverlight logo in this image is only a small portion of the area shown to the user. By setting the ViewportWidth property to 0.33, the Silverlight logo becomes the focus of the entire MultiScaleImage. In reality, the ViewportWidth is only part of the story. The other part involves using the ViewportOrigin property.

The ViewportOrigin specifies the position of the upper-left corner of the viewport. This position is a logical Point relative to the upper-left corner of the MultiScaleImage. Each of the coordinates within the Point will be between 0.0 and 1.0. Note that you can still define this value at design time. To do this, you have to set the ViewportOrigin property with the syntax shown here:

<MultiScaleImage x:Name="myMultiScaleImage"
                 Source="images/dzc_output.xml"
                 ViewportOrigin=".33,.33" />

This line of markup shows how to set the ViewportOrigin property value at design time. As you might expect, this value can also be set at runtime by creating an instance of the Point class. This explanation hardly details the real value of the ViewportOrigin property—that it enables you to navigate around the surface of an image once you're zoomed in. By handling a user action (see chapter 8), you can change the viewport position as necessary. The following example shows how to change the position of the viewport on a mouse click:

void myMultiScaleImage_MouseLeftButtonUp(object sender,
  MouseButtonEventArgs e)
{
  Point newOrigin = e.GetPosition(myMultiScaleImage);
  myMultiScaleImage.ViewportOrigin =
    myMultiScaleImage.ElementToLogicalPoint(newOrigin);
}

This example shows how to reposition the ViewportOrigin based on where a user clicked. Once it's clicked, the viewport will move to the new Point. This process begins with a nice smooth animation called a spring animation. This animation will play anytime the viewport changes size or location—anytime you zoom in or out of an image or pan the surface. This animation can be turned off by changing the UseSprings bool property to false, but you won't usually want to do this.

In general, it's recommended that you leave the UseSprings property set to true because the animation creates a rich viewing experience. In addition, it gives the MultiScaleImage control more time to download any necessary data. Once the viewport does change size or location, the MotionFinished event will be triggered, giving you an opportunity to perform any UI updates that you may want to make to the display.

The viewport is an important concept within the MultiScaleImage control. This item gives you the power to scan the surface of a high-resolution image. In addition, the viewport enables you to readily zoom in and out of an image. To enable this zooming functionality, you first load an image into the MultiScaleImage control. This image is loaded through the Source property, and the Source should reference a .xml file, which can be created by a tool. This tool is used when you're ready to deploy a multiscale image.

21.3.4 Deploying multiscale images

The MultiScaleImage control has built-in support for handling XML that details a multiscale image. This type of file can be generated programmatically or by a tool called Deep Zoom Composer. We won't cover this tool in detail because of its simplistic nature. Once you download and install the tool from the Microsoft Expression website, you can quickly create XML files that can be used by the MultiScaleImage. These files can be generated within the tool by going through a basic wizard. This wizard goes through the following steps:

  1. Import—Enables you to import your own images.

  2. Compose—Lets you lay out how the images should appear.

  3. Export—Determines where the result will be stored.

These three steps will generate a .xml file and a file/folder structure. These two items must then be added to your web application so that the MultiScaleImage control can access them. Once this has been done, you can use Deep Zoom on your own images.

Deep Zoom is a powerful feature available within Silverlight. Because this technology relies on basic images and an XML file, there are no server-side requirements. In addition, the only client-side requirement is Silverlight itself. This is great news because, as you play with Deep Zoom, you'll see the rich experience it provides. This experience truly makes viewing high-resolution images over the internet enjoyable.

One of the misconceptions about Silverlight is that everything must be a vector shape. Silverlight provides a number of ways to load and display bitmap images, as well as powerful ways to manipulate them. You can load regular .png and .jpeg files from the web or a local resource; you can create images on the fly, or from other visuals; and you can quickly zoom through collections with millions or billions of pixels at play. All of this Silverlight does natively and fluidly, proving both a simple developer experience and an excellent end-user experience.

In the examples shown so far in this chapter, you may have noticed some different values for the Stretch property. This property is shared by video and image elements alike, and helps control how the content will fill (or not fill) the space provided.

21.4 Dealing with dead space

Throughout this chapter you've seen a variety of ways to deliver different kinds of media. Often, media is intended to be a secondary part of an application instead of the main attraction. For instance, a user's profile picture is part of an application but not as important as the profile information itself. As you can imagine, there's the possibility that these profile pictures may be of different sizes. This can lead to dead space, or areas that don't include content. Fortunately, there's a way to gracefully deal with these situations. Please look at figure 21.5.

The Image, MediaElement, and Shape (discussed chapter 18) classes expose a property called Stretch. This property determines how the area devoted to an element will be filled. This description will become clearer as you see the examples available in this section. This property must be set to one of the four options available in the System. Windows.Media.Stretch enumerator, which exposes the None, Uniform, Fill, and UniformToFill options.

The boundary of an Image element in comparison to the actual size of the photo

Figure 21.5. The boundary of an Image element in comparison to the actual size of the photo

21.4.1 Filling the space

Most of the time, photos are represented as raster-based graphics. Raster-based graphics often become pixellated and lose their detail when they're enlarged. You can prevent this from happening by using the option None for the Stretch value. This option commands an element to maintain the original size of the requested content—this option doesn't make the content stretch at all. As you probably expected, you can set this property value at design time, as shown in here:

<Image x:Name="myImage"
       Source="http://www.silverlightinaction.com/man.png"
       Stretch="None" />

The XAML in this example loads an Image and prevents it from stretching. The result from this XAML can be seen in figure 21.5, which shows the results of the None option when there's plenty of space for a piece of content. Consider the scenario where the content is larger than the hosting element. Take a look at Walker's picture (the content) in an Image element (the hosting element) smaller than the picture (see listing 21.5).

Example 21.5. Use of the None option on an undersized Image element

Use of the None option on an undersized Image element

Unfortunately for Walker, his legs got cut off! (One of my slightly twisted tech reviewers suggested he should be named "Sitter" now. Thanks, Tom.) As the result in this listing shows, the original photo remains the same size, so the bottom and right edges of the photo are cropped so that the image fits within the 75px-by-75px dimension of the Image element. This illustration also erases any fears of an exception being thrown in the case of an element being smaller than its content.

At first glance, the None option may seem like the most obvious default option. It may come as a surprise to find out that another option makes even more sense as the default. When you begin to consider the fact that the Stretch option is applicable to Image, MediaElement, and Shape elements, it makes much more sense to stretch items by default, uniformly.

21.4.2 Uniform sizing

If you set the Stretch property to Uniform, the content of the element will symmetrically expand or contract to occupy most of the available area. While the content expands or contracts, the native aspect ratio will be preserved. This means that if you revisit Walker's picture, it'll be stretched vertically, making him stand tall and proud (see listing 21.6).

Example 21.6. A uniformly stretched Image with a photo smaller than the element

A uniformly stretched Image with a photo smaller than the element

The gray backdrop in this listing represents the area that could be filled by an image. Because the Uniform option stretches content proportionally, it must stop once either a vertical or horizontal boundary is met. But, what happens if the content is larger than the bounding element? Listing 21.7 shows how to maintain aspect ratio.

Example 21.7. A uniformly stretched Image with a photo larger than the element

A uniformly stretched Image with a photo larger than the element

As this listing illustrates, the content remains intact. Instead of cropping the image, as was the case in listing 21.5, the content scales to a smaller size. As the content scales down, the aspect ratio stays the same. Although maintaining the aspect has its benefits, occasionally you might need to fill the entire area, no matter what. For these situations, you have the Fill option.

21.4.3 Fill the area

The Fill option allows you to expand or contract the content of an element to fully occupy the space allocated to it. You'll most likely use this option in backdrop scenarios where you want an Image to serve as wallpaper. Listing 21.8 shows what the Fill option does to Walker's picture.

Example 21.8. An Image using the Fill option to stretch an image

An Image using the Fill option to stretch an image

Walker looks a little bloated (it's water weight, trust me) in this listing because, although the Fill option will expand to ensure that every pixel allotted to an element is used, the aspect ratio of the content won't be preserved. Because of the oblong dimensions of the photo, the photo is stretched horizontally—in turn, horizontally stretching Walker.

Sometimes, you may need the flexibility to fill an element while maintaining the aspect ratio. For these circumstances, you have the UniformToFill option.

21.4.4 UniformToFill

As the name implies, this option is a hybrid between the Uniform and Fill options. The content within an element will maintain its aspect ratio while filling the entire bounding area. If the content has a different aspect ratio than the housing element, the overflowing content will be clipped. See listing 21.9 for a small sample using our friend Walker.

Example 21.9. An Image using the UniformToFill Stretch option

An Image using the UniformToFill Stretch option

This listing illustrates how a raster-based graphic can become pixellated when inflated. It also shows how the aspect ratio is maintained as the picture is enlarged to fill every allocated pixel. As you've probably noticed, Walker's legs are removed from this picture. This is because the bottom edge has been removed to ensure that the image fits within the allocated boundaries.

Stretching can help you address a wide variety of filling situations. Silverlight allows you to easily address any type of stretching situation through the Stretch property. This property can be used with the Image and MediaElement items discussed earlier in this chapter. Interestingly, the Stretch property isn't supported by the MultiScaleImage control. But, with those powerful deep-zooming capabilities, does it really make sense to stretch? I think not.

21.5 Summary

Over the course of this chapter, we explored several ways of working with bitmap (also known as raster) images. Silverlight natively supports .jpeg and .png images in the Image element, but provides facilities for you to be able to construct any type of image you'd like using direct pixel access with the WriteableBitmap.

The WriteableBitmap allows you to construct images from existing UI elements, from existing images, or even from individually placed pixels. This supports countless scenarios from screen thumbnails, to video stills, to games and paint programs.

For truly large images, or collections of large images, Silverlight provides the MultiScaleImage control, also known as Deep Zoom. Deep Zoom supports images in the millions to billions of pixels allowing for very fast enlargement of specific areas while minimizing the amount of memory, processing power, and bandwidth used.

All of the image types support various stretch options to allow them to conform to the shape of the container they're placed in. Silverlight smoothly resizes images, preserving aspect ratio if you desire.

Along with media (see chapter 20) and vector graphics (see chapter 18), bitmap images round out the graphical presentation capabilities of Silverlight, helping to put the "rich" in rich internet application. In the next chapter we'll learn how to use animation to move from these static shapes to a more dynamic and interactive user experience.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.182.66