Partager via


Imaging Overview

This topic provides an introduction to the Microsoft Windows Presentation Foundation Imaging Component. WPF Imaging enables developers to display, transform, and format images.

This topic contains the following sections.

  • WPF Imaging Component
  • WPF Image Formats
  • Displaying Images in WPF
  • Image Metadata
  • Codec Extensibility
  • Related Topics

WPF Imaging Component

WPF Imaging provides significant enhancements in imaging capabilities within Microsoft Windows. Imaging capabilities, such as displaying a bitmap or using an image on a common control were previously reliant upon the Microsoft Windows Graphics Device Interface (GDI) or Microsoft Windows GDI+ libraries. These API provide baseline imaging functionality, but lack features such as support for codec extensibility and high fidelity image support. WPF Imaging is designed to overcome the shortcomings of GDI and GDI+ and provide a new set of API to display and use images within your applications.

There are two ways to access the WPF Imaging API, a managed component and an unmanaged component. The unmanaged component provides the following features.

  • Extensibility model for new or proprietary image formats.

  • Improved performance and security on native image formats including bitmap (BMP), Joint Photographics Experts Group (JPEG), Portable Network Graphics (PNG), Tagged Image File Format (TIFF), Microsoft Windows Media Photo, Graphics Interchange Format (GIF), and icon (.ico).

  • Preservation of high bit depth image data up to 32 bits per channel.

  • Nondestructive image scaling, cropping, and rotations.

  • Simplified color management.

  • Support for in-file, proprietary metadata.

  • The managed component utilizes the unmanaged infrastructure to provide seamless integration of images with other WPF features such as user interface (UI), animation, and graphics. The managed component also benefits from the Windows Presentation Foundation (WPF) imaging codec extensibility model which enables automatic recognition of new image formats in WPF applications.

The majority of the managed WPF Imaging API reside in the System.Windows.Media.Imaging namespace, though several important types, such as ImageBrush and ImageDrawing reside in the System.Windows.Media namespace and Image resides in the System.Windows.Controls namespace.

This topic provides additional information about the managed component. For more information on the unmanaged API see the Unmanaged WPF Imaging Component documentation.

WPF Image Formats

A codec is used to decode or encode a specific media format. WPF Imaging includes a codec for BMP, JPEG, PNG, TIFF, Windows Media Photo, GIF, and ICON image formats. Each of these codecs enable applications to decode and, with the exception of ICON, encode their respective image formats.

BitmapSource is an important class used in the decoding and encoding of images. It is the basic building block of the WPF Imaging pipeline and represents a single, constant set of pixels at a certain size and resolution. A BitmapSource can be an individual frame of a multiple frame image, or it can be the result of a transform performed on a BitmapSource. It is the parent of many of the primary classes used in WPF imaging such as BitmapFrame.

A BitmapFrame is used to store the actual bitmap data of an image format. Many image formats only support a single BitmapFrame, although formats such as GIF and TIFF support multiple frames per image. Frames are used by decoders as input data and are passed to encoders to create image files.

The following example demonstrates how a BitmapFrame is created from a BitmapSource and then added to a TIFF image.

BitmapSource image5 = BitmapSource.Create(
    width,
    height,
    96,
    96,
    PixelFormats.Indexed1,
    BitmapPalettes.WebPalette,
    pixels,
    stride);

FileStream stream5 = new FileStream("palette.tif", FileMode.Create);
TiffBitmapEncoder encoder5 = new TiffBitmapEncoder();
encoder5.Frames.Add(BitmapFrame.Create(image5));
encoder5.Save(stream5);

Image Format Decoding

Image decoding is the translation of an image format to image data that can be used by the system. The image data can then be used to display, process, or encode to a different format. Decoder selection is based on the image format. Codec selection is automatic unless a specific decoder is specified. The examples in the Displaying Images in WPF section demonstrate automatic decoding. Custom format decoders developed using the unmanaged WPF Imaging interfaces and registered with the system automatically participate in decoder selection. This allows custom formats to be displayed automatically in WPF applications.

The following example demonstrates the use of a bitmap decoder to decode a BMP format image.

// Open a Uri and decode a BMP image
System::Uri^ myUri = gcnew System::Uri("tulipfarm.bmp", UriKind::RelativeOrAbsolute);
BmpBitmapDecoder^ decoder2 = gcnew BmpBitmapDecoder(myUri, BitmapCreateOptions::PreservePixelFormat, BitmapCacheOption::Default);
BitmapSource^ bitmapSource2 = decoder2->Frames[0];

// Draw the Image
Image^ myImage2 = gcnew Image();
myImage2->Source = bitmapSource2;
myImage2->Stretch = Stretch::None;
myImage2->Margin = System::Windows::Thickness(20);
// Open a Uri and decode a BMP image
Uri myUri = new Uri("tulipfarm.bmp", UriKind.RelativeOrAbsolute);
BmpBitmapDecoder decoder2 = new BmpBitmapDecoder(myUri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource2 = decoder2.Frames[0];

// Draw the Image
Image myImage2 = new Image();
myImage2.Source = bitmapSource2;
myImage2.Stretch = Stretch.None;
myImage2.Margin = new Thickness(20);

Image Format Encoding

Image encoding is the translation of image data to a specific image format. The encoded image data can then be used to create new image files. WPF Imaging provides encoders for each of the image formats described above.

The following example demonstrates the use of an encoder to save a newly created bitmap image.

FileStream^ stream = gcnew FileStream("new.bmp", FileMode::Create);
BmpBitmapEncoder^ encoder = gcnew BmpBitmapEncoder();
TextBlock^ myTextBlock = gcnew TextBlock();
myTextBlock->Text = "Codec Author is: " + encoder->CodecInfo->Author->ToString();
encoder->Frames->Add(BitmapFrame::Create(image));
encoder->Save(stream);
FileStream stream = new FileStream("new.bmp", FileMode.Create);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
TextBlock myTextBlock = new TextBlock();
myTextBlock.Text = "Codec Author is: " + encoder.CodecInfo.Author.ToString();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);

Displaying Images in WPF

BitmapImage is a specialized BitmapSource that is optimized for Extensible Application Markup Language (XAML) loading and is an easy way to display images as the Source of an Image control.

Using the Image Control

Image is a framework element and the primary way to display images in applications. In XAML, Image can be used in two ways; attribute syntax or property syntax. The following example shows how to render an image 200 pixels wide using both attribute syntax and property tag syntax. For more information on attribute syntax and property syntax, see Dependency Properties Overview.

<!-- Simple image rendering. However, rendering an image this way may not
     result in the best use of application memory. See markup below which
     creates the same end result but using less memory. -->
<Image Width="200" 
Source="C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Water Lilies.jpg"/>

<Image Width="200">
  <Image.Source>
    <!-- To save significant application memory, set the DecodePixelWidth or  
     DecodePixelHeight of the BitmapImage value of the image source to the desired 
     height and width of the rendered image. If you don't do this, the application will 
     cache the image as though it were rendered as its normal size rather then just 
     the size that is displayed. -->
    <!-- Note: In order to preserve aspect ratio, only set either DecodePixelWidth
         or DecodePixelHeight but not both. -->
    <BitmapImage DecodePixelWidth="200"  
     UriSource="C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Water Lilies.jpg" />
  </Image.Source>
</Image>

The following example shows how to render an image 200 pixels wide using code.

NoteNote:

BitmapImage implements the ISupportInitialize interface to optimize initialization on multiple properties. Property changes can only occur during object initialization. Call BeginInit to signal that initialization has begun and EndInit to signal that initialization has completed. Once initialized, property changes are ignored.

// Create Image Element
Image myImage = new Image();
myImage.Width = 200;
         
// Create source
BitmapImage myBitmapImage = new BitmapImage();

// BitmapImage.UriSource must be in a BeginInit/EndInit block
myBitmapImage.BeginInit();
myBitmapImage.UriSource = new Uri(@"C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Water Lilies.jpg");

// To save significant application memory, set the DecodePixelWidth or  
// DecodePixelHeight of the BitmapImage value of the image source to the desired 
// height or width of the rendered image. If you don't do this, the application will 
// cache the image as though it were rendered as its normal size rather then just 
// the size that is displayed.
// Note: In order to preserve aspect ratio, set DecodePixelWidth
// or DecodePixelHeight but not both.
myBitmapImage.DecodePixelWidth = 200;
myBitmapImage.EndInit();
//set image source
myImage.Source = myBitmapImage;

Rotating, Converting, and Cropping Images

WPF enables users to transform images by using properties of BitmapImage or by using additional BitmapSource objects such as CroppedBitmap or FormatConvertedBitmap. These image transformations can scale or rotate an image, change the pixel format of an image, or crop an image.

Image rotations are performed using the Rotation property of BitmapImage. Rotations can only be done in 90 degree increments. In the following example, an image is rotated 90 degrees.

<Image Width="150" Margin="5" Grid.Column="0" Grid.Row="1">
  <Image.Source>
    <TransformedBitmap Source="/sampleImages/watermelon.jpg" >
      <TransformedBitmap.Transform>
        <RotateTransform Angle="90"/>
      </TransformedBitmap.Transform>
    </TransformedBitmap>
  </Image.Source>
</Image>
// Create Image element.
Image rotated90 = new Image();
rotated90.Width = 150;

// Create the TransformedBitmap to use as the Image source.
TransformedBitmap tb = new TransformedBitmap();

// Create the source to use as the tb source.
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(@"sampleImages/watermelon.jpg", UriKind.RelativeOrAbsolute);
bi.EndInit();

// Properties must be set between BeginInit and EndInit calls.
tb.BeginInit();
tb.Source = bi;
// Set image rotation.
RotateTransform transform = new RotateTransform(90);
tb.Transform = transform;
tb.EndInit();
// Set the Image source.
rotated90.Source = tb;

Converting an image to a different pixel format such as grayscale is done using FormatConvertedBitmap. In the following examples, an image is converted to Gray4.

<!-- Grayscale XAML Image -->
<Image Width="200" Grid.Column="0" Grid.Row="1">
   <Image.Source>
      <FormatConvertedBitmap Source="/sampleImages/rocks.jpg"  DestinationFormat="Gray4" />
   </Image.Source>
</Image>
//Create Image Element
Image grayImage = new Image();
grayImage.Width = 200;
grayImage.Margin = new Thickness(5);

//Create source using xaml defined resource.
FormatConvertedBitmap fcb = new FormatConvertedBitmap(
   (BitmapImage)this.Resources["masterImage"],PixelFormats.Gray4,null,0);
//set image source
grayImage.Source = fcb;

To crop an image, either the Clip property of Image or CroppedBitmap can be used. Typically, if you just want to display a portion of an image, Clip should be used. If you need to encode and save a cropped image, the CroppedBitmap should be used. In the following example, an image is cropped using the Clip property using an EllipseGeometry.

<!-- Cropping an Image using Clip -->
<Image Width="200" Grid.Column="0" Grid.Row="5" Margin="5"
   Source="/sampleImages/gecko.jpg">
  <Image.Clip>
    <EllipseGeometry Center="75,50" RadiusX="50" RadiusY="25" />
  </Image.Clip>
</Image>
//Create our image for clipping
Image clipImage = new Image();
clipImage.Width = 200;
clipImage.Margin = new Thickness(5);
 
//Create & Set source
BitmapImage bi = new BitmapImage();
//BitmapImage.UriSource must be in a BeginInit/EndInit block
bi.BeginInit();
bi.UriSource = new Uri("pack://application:,,/sampleImages/gecko.jpg");
bi.EndInit();
clipImage.Source = bi;

//Clip the using an EllipseGeometry
EllipseGeometry clipGeometry = new EllipseGeometry(new Point(75, 50), 50, 25);
clipImage.Clip = clipGeometry;

Stretching Images

The Stretch property controls how an image is stretched to fill its container. The Stretch property accepts the following values, defined by the Stretch enumeration:

  • None: The image is not stretched to fill the output area. If the image is larger than the output area, the image is drawn to the output area, clipping what does not fit.

  • Fill: The image is scaled to fit the output area. Because the image height and width are scaled independently, the original aspect ratio of the image might not be preserved. That is, the image might be warped in order to completely fill the output container.

  • Uniform: The image is scaled so that it fits completely within the output area. The image's aspect ratio is preserved.

  • UniformToFill: The image is scaled so that it completely fills the output area while preserving the image's original aspect ratio.

The following example applies each of the available Stretch enumerations to an Image.

The following image shows the output from the example and demonstrates the affect the different Stretch settings have when applied to an image.

<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" >
  <DockPanel>

    <Border DockPanel.Dock="Top" Background="Black">
      <TextBlock Foreground="White" HorizontalAlignment="Stretch" FontSize="20">
        Stretching an Image
      </TextBlock>
    </Border>

    <Grid Name="simpleGrid" Background="{StaticResource CheckeredBrushResource}" 
       Margin="10" 
       ShowGridLines="True"
       VerticalAlignment="Center"
       HorizontalAlignment="Center">
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="175" />
        <ColumnDefinition Width="175" />
        <ColumnDefinition Width="175" />
        <ColumnDefinition Width="175" />
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="200"/>
      </Grid.RowDefinitions>
      <!-- Labels -->
      <TextBlock Style="{StaticResource Header1}" 
        Grid.Column="0" Grid.Row="0">None</TextBlock>
      <TextBlock Style="{StaticResource Header1}" 
        Grid.Column="1" Grid.Row="0">Uniform</TextBlock>
      <TextBlock Style="{StaticResource Header1}" 
        Grid.Column="2" Grid.Row="0">UniformToFill</TextBlock>
      <TextBlock Style="{StaticResource Header1}"
        Grid.Column="3" Grid.Row="0">Fill</TextBlock>
      <Border Grid.Column="0" Grid.Row="1" BorderThickness="1" BorderBrush="Black">
        <!-- None: Image is not scaled. If image is larger than the
             output area, the image will be cropped to the size of the output area.-->
        <Image
          Source="sampleImages/gecko.jpg" 
          Stretch="None" />
      </Border>
      <Border Grid.Column="1" Grid.Row="1" BorderThickness="1" BorderBrush="Black">
        <!-- Uniform: Scale to fit output area.
             Aspect ratio is preserved.-->
        <Image
          Source="sampleImages/gecko.jpg" 
          Stretch="Uniform" />
      </Border>
      <Border Grid.Column="2" Grid.Row="1" BorderThickness="1" BorderBrush="Black">
        <!-- UniformToFill: Scale to completely fill output area.
             Aspect ratio is preserved. Cropping may occur.-->
        <Image  
          Source="sampleImages/gecko.jpg" 
        Stretch="UniformToFill" />
      </Border>
      <Border Grid.Column="3" Grid.Row="1" BorderThickness="1" BorderBrush="Black">
      <!-- Fill: Scale to completely fill output area.
             Aspect ratio may not be preserved.-->
      <Image 
        Source="sampleImages/gecko.jpg" 
        Stretch="Fill" />
      </Border>
    </Grid>
  </DockPanel>
</Page>

Painting with Images

Images can also be displayed in an application by painting with a Brush. Brushes enable you to paint UI objects with anything from simple, solid colors to complex sets of patterns and images. To paint with images, use an ImageBrush. An ImageBrush is a type of TileBrush that defines its content as a bitmap image. An ImageBrush displays a single image, which is specified by its ImageSource property. You can control how the image is stretched, aligned, and tiled, enabling you to prevent distortion and produce patterns and other effects. The following illustration shows some effects that can be achieved with an ImageBrush.


Image brushes can fill shapes, controls, text, and more

ImageBrush output examples

The following example demonstrates how to paint the background of a button with an image using an ImageBrush.

<!-- Sets the button's Background property with an ImageBrush. The resulting
     button has an image as its background. -->
<Button Grid.Row="3" Grid.Column="2" 
 Height="75" Width="100" Foreground="White" FontWeight="Bold"
 HorizontalAlignment="Left">
  A Button
  <Button.Background>
    <ImageBrush ImageSource="sampleImages\blueberries.jpg" />
  </Button.Background>
</Button>

For additional information about ImageBrush and painting images see Painting with Images, Drawings, and Visuals.

Image Metadata

Some image files contain metadata that describes the content or the characteristics of the file. For example, most digital cameras create images that contain metadata about the make and model of the camera used to capture the image. Each image format handles metadata differently but WPF Imaging provides a uniform way of storing and retrieving metadata for each supported image format.

Access to metadata is provided through the Metadata property of a BitmapSource object. Metadata returns a BitmapMetadata object that includes all the metadata contained by the image. This data may be in one metadata schema or a combination of different schemes. WPF Imaging supports the following image metadata schemas: Exchangeable image file (Exif), tEXt (PNG Textual Data), image file directory (IFD), International Press Telecommunications Council (IPTC), and Extensible Metadata Platform (XMP).

In order to simplify the process of reading metadata, BitmapMetadata provides several named properties that can be easily accessed such as Author, Title, and CameraModel. Many of these named properties can also be used to write metadata. Additional support for reading metadata is provided by the metadata query reader. The GetQuery method is used to retrieve a metadata query reader by providing a string query such as "/app1/exif/". In the following example, GetQuery is used to obtain the text stored in the "/Text/Description" location.

// Add the metadata of the bitmap image to the text block.
TextBlock^ myTextBlock = gcnew TextBlock();
myTextBlock->Text = "The Description metadata of this image is: " + pngInplace->GetQuery("/Text/Description")->ToString();
// Add the metadata of the bitmap image to the text block.
TextBlock myTextBlock = new TextBlock();
myTextBlock.Text = "The Description metadata of this image is: " + pngInplace.GetQuery("/Text/Description").ToString();

To write metadata, a metadata query writer is used. SetQuery obtains the query writer and sets the desired value. In the following example, SetQuery is used to write the text stored in the "/Text/Description" location.

Stream^ pngStream = gcnew FileStream("smiley.png", FileMode::Open, FileAccess::ReadWrite, FileShare::ReadWrite);
PngBitmapDecoder^ pngDecoder = gcnew PngBitmapDecoder(pngStream, BitmapCreateOptions::PreservePixelFormat, BitmapCacheOption::Default);
BitmapFrame^ pngFrame = pngDecoder->Frames[0];
InPlaceBitmapMetadataWriter^ pngInplace = pngFrame->CreateInPlaceBitmapMetadataWriter();
if (pngInplace->TrySave() == true)
{
   pngInplace->SetQuery("/Text/Description", "Have a nice day.");
}
pngStream->Close();
Stream pngStream = new System.IO.FileStream("smiley.png", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
PngBitmapDecoder pngDecoder = new PngBitmapDecoder(pngStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapFrame pngFrame = pngDecoder.Frames[0];
InPlaceBitmapMetadataWriter pngInplace = pngFrame.CreateInPlaceBitmapMetadataWriter();
if (pngInplace.TrySave() == true)
{ pngInplace.SetQuery("/Text/Description", "Have a nice day."); }
pngStream.Close();

Codec Extensibility

A core feature of WPF Imaging is the extensibility model for new image codecs. These unmanaged interfaces enable codec developers to integrate codecs with WPF so new image formats can automatically be used by WPF applications. To learn more about unmanaged WPF Imaging, see the Unmanaged WPF Imaging Component documentation.

For a sample of the extensibility API, see the Win32 Sample Codec. This sample demonstrates how to create a decoder and encoder for a custom image format.

NoteNote:

The codec must be digitally signed for the system to recognize it.

See Also

Reference

BitmapSource
BitmapImage
Image
BitmapMetadata

Other Resources

Win32 Sample Codec
Photo Store Demo