This chapter
is taken from book "Programming Windows Phone 7" by Charles Petzold published by
Microsoft press.
http://www.charlespetzold.com/phone/index.html
Bitmap and Texture: Bitmap is an object which is
appear in both Silverlight and XNA applications but in XNA, a bitmap has a data
type of Texture2D
and hence is often referred to as a
texture.
Bitmaps are used to symbolize your application
on the phone. A new XNA or Silverlight project in Visual Studio results in the
creation of three bitmaps for various purposes.
The native Windows bitmap format has an extension of BMP but it's become less
popular in recent years as compressed formats have become widespread. At this
time, the three most popular bitmap formats XNA supports all three (and more),
and silverlight supports only JPEG and PNG, the formats are probably:
- JPEG (Joint Photography Experts Group)
- PNG (Portable Network Graphics)
- GIF (Graphics Interchange File)
XNA Texture Drawing
XNA 2D programming is almost entirely a process of moving sprites around the
screen, you might expect that loading and drawing bitmaps in an XNA program is
fairly easy, and you would be correct, you can create the bitmap in an external
program, Windows Paint is often convenient.
The first project is called XnaLocalBitmap,to add this file as part of the
program's content, right-click the XnaLocalBitmapContent project in Visual
Studio, select Add and Existing Item, and then navigate to the file. Once the
file shows up, you can right-click it to display Properties, and you'll see that
it has an Asset Name of "Hello." The goal is to display this bitmap centered on
the screen. Define a field in the Game1.cs file to store the Texture2D and another
field for the position:
Example
public
class Game1 :
Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D helloTexture;
Vector2 position;
.....
}
Both fields are set during the LoadContent method. Use the same generic method to load the Texture2D as you use to load a SpriteFont.
The Texture2D class has properties named Width
and Height that provide the dimensions of the bitmap in pixels.
protected
override void
LoadContent()
{
spriteBatch = new
SpriteBatch(GraphicsDevice);
helloTexture = this.Content.Load<Texture2D>("Hello");
Viewport viewport =
this.GraphicsDevice.Viewport;
position = new
Vector2((viewport.Width - helloTexture.Width)
/ 2,
(viewport.Height - helloTexture.Height) / 2);
}
The SpriteBatch
class has seven Draw methods to render bitmaps. This one is certainly the simplest:
protected
override void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Navy);
spriteBatch.Begin();
spriteBatch.Draw(helloTexture, position,
Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
The final argument to Draw is a color that can be used to attenuate the
existing colors in the bitmap. Use Color.White if you want the bitmap's colors to display without any
alteration, And here it is:
Silverlight Image Element
The equivalent program in Silverlight is even simpler. Let's create a project named SilverlightLocalBitmap. First create a directory in the project to store the bitmap. Right-click the project name and choose Add and then New Folder. Let's name it Images. Then right-click the folder name and choose Add and Existing Item. Navigate to the Hello.png file. From the Add button choose either Add or Add as Link. If you choose Add, a copy will be made and the file will be physically copied into a subdirectory of the project. If you choose Add as Link, only a file reference will be retained with the project but the file will still be copied into the executable. The final step: Right-click the bitmap filename and display Properties. Note that the Build Action is Resource. It's possible to change that Build Action to Content, but let's leave it for now and I'll discuss the difference shortly.
In Silverlight, you use the
Image
element to display bitmaps just as you use the TextBlock element to display text. Set the Source
property of Image to the folder and filename of the bitmap within the
project:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Image
Source="Images/Hello.png"
/>
</Grid>
The display looks a little different than the XNA
program, and it's not just the titles, this is most noticeable if you set the
SupportedOrientations
attribute of the
PhoneApplicationPage start tag to
PortraitOrLandscape
and turn the phone sideways:
Images Via the Web
One feature that's really nice about the
Image element is that
you can set the Source
property to a URL, such as in this Silverlight project:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Image
Source="http://www.charlespetzold.com/Media/HelloWP7.jpg"
/>
</Grid>
Here
it is:
You can use WebClient to download either strings (commonly XML files) or
binary objects. The actual transfer occurs asynchronously and then WebClient
calls a method in your program to
indicate completion or failure. To use
WebClient in an XNA program, you'll need to add a
reference to the System.Net library: In the Solution Explorer, under the project
name, right click References and select Add Reference. In the .NET table, select
System.Net. (Silverlight programs get a reference to System.Net automatically.)
The Game1.cs file of the XnaWebBitmap project also
requires a using
directive for the
System.Net namespace. The program defines the same
fields as the earlier program:
public
class Game1 :
Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D helloTexture;
Vector2 position;
...
}
The LoadContent
method creates an instance of
WebClient, sets the
callback method, and then initiates the transfer:
protected
override void
LoadContent()
{
spriteBatch = new
SpriteBatch(GraphicsDevice);
WebClient webClient =
new WebClient();
webClient.OpenReadCompleted += OnWebClientOpenReadCompleted;
webClient.OpenReadAsync(new
Uri("http://www.charlespetzold.com/Media/HelloWP7.jpg"));
}
The OnWebClientOpenReadCompleted method is called when the entire file
has been downloaded. You can use that Stream with the static
Texture2D.FromStream method to create a Texture2D object:
void
OnWebClientOpenReadCompleted(object sender,
OpenReadCompletedEventArgs args)
{
if (!args.Cancelled && args.Error ==
null)
{
helloTexture = Texture2D.FromStream(this.GraphicsDevice,
args.Result);
Viewport viewport =
this.GraphicsDevice.Viewport;
position = new
Vector2((viewport.Width - helloTexture.Width)
/ 2,
(viewport.Height - helloTexture.Height) /
2);
}
}
By default, the AllowReadStreamBuffering property of WebClient is true, which
means that the entire file will have been downloaded when the OpenReadCompleted
event is raised.
Normally the
LoadContent method of a Game derivative is called
before the first call to the Update or Draw method, but it is essential to
remember that a gap of time will separate LoadContent from the
OnWebClientOpenReadCompleted method. During that time an asynchronous read is
occurring, but the Game1 class is proceeding as normal with calls to Update and
Draw. For that reason, you should only attempt to access the Texture2D object
when you know that it's valid:
protected
override void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Navy);
if (helloTexture !=
null)
{
spriteBatch.Begin();
spriteBatch.Draw(helloTexture, position,
Color.White);
spriteBatch.End();
}
base.Draw(gameTime);
}
In a real program, you'd also want to provide some kind of notification to
the user if the bitmap could not be downloaded.
Image and ImageSource
Once you begin investigating the Image element, it may seem a little
confusing. The Image element is not the bitmap; the Image element merely
displays the bitmap. In the uses you've seen so far, the Source property of
Image has been set to a relative file path or a URL:
<Image
Source="Images/Hello.png"
/>
<Image
Source="http://www.charlespetzold.com/Media/HelloWP7.jpg"
/>
From BitmapSource derives BitmapImage, which supports a constructor that
accepts a Uri object and also includes a UriSource property of type Uri. The
SilverlightTapToDownload1 project mimics a program that needs to download a
bitmap whose URL is known only at runtime. The XAML contains an Image element
with no bitmap to display:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Image
Name="img" />
</Grid>
BitmapImage requires a using directive for the System.Windows.Media.Imaging
namespace. When MainPage gets a tap, it creates a BitmapImage from the Uri
object and sets that to the Source property of the Image:
protected
override void
OnManipulationStarted(ManipulationStartedEventArgs
args)
{
Uri uri =
new Uri("http://www.charlespetzold.com/Media/HelloWP7.jpg");
BitmapImage bmp =
new BitmapImage(uri);
img.Source = bmp;
args.Complete();
args.Handled = true;
base.OnManipulationStarted(args);
}
Remember to tap the screen to initiate the download!
If you want to explicitly use
WebClient in a Silverlight program, you can do
that as well, as the next project demonstrates. The
SilverlightTapToDownload2.xaml file is the same as
SilverlightTapToDownload1.xaml. The code-behind file uses WebClient much like
the earlier XNA program:
protected
override void
OnManipulationStarted(ManipulationStartedEventArgs
args)
{
WebClient webClient =
new WebClient();
webClient.OpenReadCompleted += OnWebClientOpenReadCompleted;
webClient.OpenReadAsync(new
Uri("http://www.charlespetzold.com/Media/HelloWP7.jpg"));
args.Complete();
args.Handled = true;
base.OnManipulationStarted(args);
}
void OnWebClientOpenReadCompleted(object
sender, OpenReadCompletedEventArgs args)
{
if (!args.Cancelled && args.Error ==
null)
{
BitmapImage bmp =
new BitmapImage();
bmp.SetSource(args.Result);
img.Source = bmp;
}
}
Notice the use of SetSource to create the bitmap from the Stream object.
Summary
I hope this article helps you to learn about how to use
Bitmap and Texture in Silverlight and XNA application for Windows Phone 7.