---
title: "Read image with WPF"
canonical: https://www.graphicsmill.com/samples/Codecs/CustomCodecs/ReadImageWithWPF_cs
product: "Graphics Mill"
source: "Codecs/CustomCodecs/ReadImageWithWPF.cs"
---

# Read image with WPF

Demonstrates passing an image to a pipeline with the custom image reader.

## Instruction

Demonstrate how to pass an image to a pipeline using a custom image reader.

## Usings

```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Aurigma.GraphicsMill.Codecs;
```

## Code Snippet

```csharp
public sealed class WpfImageReader : IImageReader, IImageParams
{
    private readonly BitmapSource bitmapSource;

    public WpfImageReader(System.IO.Stream stream)
    {
        this.bitmapSource = BitmapFrame.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);

        if (this.bitmapSource.Metadata is BitmapMetadata metadata)
        {
            this.Exif = new ExifDictionary();
            this.CaptureMetadata(metadata, string.Empty);
        }
    }

    public Aurigma.GraphicsMill.Ink Ink => null;

    public ColorPalette Palette => null;

    public ColorProfile ColorProfile => null;

    public float DpiX
    {
        get
        {
            return (float)this.bitmapSource.DpiX;
        }
    }

    public float DpiY
    {
        get
        {
            return (float)this.bitmapSource.DpiY;
        }
    }

    public PixelFormat PixelFormat
    {
        get
        {
            return ConvertFormat(this.bitmapSource.Format);
        }
    }

    public int Height
    {
        get
        {
            return this.bitmapSource.PixelHeight;
        }
    }

    public int Width
    {
        get
        {
            return this.bitmapSource.PixelWidth;
        }
    }

    public ExifDictionary Exif { get; private set; }

    public ImageParams GetImageParams()
    {
        return ImageParams.Create(this);
    }

    public Bitmap GetStripe(int stripeY, int stripeHeight)
    {
        // In production, it makes sense to consider more sophisticated logic
        // for disposing stripe bitmaps. The possible approach is to dispose
        // the previous stripe with generating the next one.
        var stripe = new Bitmap(this.Width, stripeHeight, this.PixelFormat);

        stripe.DpiX = this.DpiX;
        stripe.DpiY = this.DpiY;

        this.bitmapSource.CopyPixels(new System.Windows.Int32Rect(0, stripeY, stripe.Width, stripe.Height), stripe.Scan0, stripe.Stride * stripe.Height, stripe.Stride);

        return stripe;
    }

    /// <summary>
    /// Converts WPF pixel format to corresponding one for Graphics Mill.
    /// </summary>
    private static PixelFormat ConvertFormat(System.Windows.Media.PixelFormat format)
    {
        var dict = new Dictionary<System.Windows.Media.PixelFormat, Aurigma.GraphicsMill.PixelFormat>
        {
            { System.Windows.Media.PixelFormats.Bgr24, Aurigma.GraphicsMill.PixelFormat.Format24bppRgb },
            { System.Windows.Media.PixelFormats.Bgr32, Aurigma.GraphicsMill.PixelFormat.Format32bppRgb },
            { System.Windows.Media.PixelFormats.Bgra32, Aurigma.GraphicsMill.PixelFormat.Format32bppArgb },
            { System.Windows.Media.PixelFormats.Gray8, Aurigma.GraphicsMill.PixelFormat.Format8bppGrayscale },
            { System.Windows.Media.PixelFormats.Gray16, Aurigma.GraphicsMill.PixelFormat.Format16bppGrayscale },
        };

        if (!dict.ContainsKey(format))
        {
            throw new Aurigma.GraphicsMill.UnsupportedPixelFormatException();
        }

        return dict[format];
    }

    private static UnsignedRational ToRational(ulong value)
    {
        var firstHalf = (uint)(value >> 32);
        var secondHalf = (uint)(value & 0xffffffff);

        return new UnsignedRational(firstHalf, secondHalf);
    }

    private static Rational ToRational(long value)
    {
        var firstHalf = (int)(value >> 32);
        var secondHalf = (int)(value & 0xffffffff);

        return new Rational(firstHalf, secondHalf);
    }

    private void WriteTag(long tag, object value)
    {
        if (this.Exif.Contains(tag))
        {
            return;
        }

        if (value is byte)
        {
            this.Exif.Add(tag, (byte)value);
        }
        else if (value is long)
        {
            this.Exif.Add(tag, ToRational((long)value));
        }
        else if (value is ulong)
        {
            this.Exif.Add(tag, ToRational((ulong)value));
        }
        else if (value is ushort)
        {
            this.Exif.Add(tag, (ushort)value);
        }
        else if (value is short)
        {
            this.Exif.Add(tag, (short)value);
        }
        else if (value is string)
        {
            this.Exif.Add(tag, value as string);
        }
        else if (value is ulong[])
        {
            var data = value as ulong[];
            this.Exif.Add(tag, new UnsignedRational[] { ToRational(data[0]), ToRational(data[1]), ToRational(data[2]) });
        }
        else if (value is long[])
        {
            var data = value as long[];
            this.Exif.Add(tag, new Rational[] { ToRational(data[0]), ToRational(data[1]), ToRational(data[2]) });
        }
        else
        {
            const long exifTagUserComment = 0x9286;
            if (tag == exifTagUserComment)
            {
                var blob = value as BitmapMetadataBlob;
                this.Exif.Add(tag, blob.GetBlobValue());
            }
        }
    }

    private void CaptureMetadata(BitmapMetadata metadata, string query)
    {
        foreach (var relativeQuery in metadata)
        {
            var queryReader = metadata.GetQuery(relativeQuery);

            if (queryReader is BitmapMetadata innerMetadata)
            {
                this.CaptureMetadata(innerMetadata, query + relativeQuery);
            }
            else if (query.Contains("app1") || query.Contains("ifd"))
            {
                var m = Regex.Match(relativeQuery, @"\d+");
                if (m.Length == 0)
                {
                    continue;
                }

                var tag = long.Parse(m.Value);
                WriteTag(tag, queryReader);
            }
        }
    }

    internal class ReadImageWithWPF
    {
        public static void Run()
        {
            using (var fs = new FileStream("IMG_4212.HEIC", FileMode.Open, FileAccess.Read))
            using (var reader = new CustomImageReader(new WpfImageReader(fs)))
            using (var writer = new TiffWriter("ReadImageWithWPF.tif"))
            {
                var wpfReader = (WpfImageReader)reader.InternalImageReader;
                if (wpfReader.Exif != null)
                {
                    writer.Exif = wpfReader.Exif;

                    foreach (object key in wpfReader.Exif.Keys)
                    {
                        Console.WriteLine("{0}: {1}, {2}", wpfReader.Exif.GetKeyDescription(key), wpfReader.Exif[key], wpfReader.Exif.GetItemString(key));
                    }
                }

                Pipeline.Run(reader + writer);
            }
        }
    }
}
```

## Explanation

Here is a C# program that utilizes a custom image reader to pass an image into the memory-friendly Pipeline API for processing:

This code defines `WpfImageReader`, a custom image reader that extracts metadata and image properties from a WPF `BitmapSource`. The `ReadImageWithWPF.Run()` method reads an HEIC image, extracts EXIF metadata, and writes it to a TIFF file using the Pipeline API.

## Tags

- [Format Conversion](<https://www.graphicsmill.com/samples/tags/format-conversion_md>): Converting images between formats
- [Metadata](<https://www.graphicsmill.com/samples/tags/metadata_md>): Reading and writing image metadata
- [Pipeline](<https://www.graphicsmill.com/samples/tags/pipeline_md>): Working with images via the Pipeline API
- [Read](<https://www.graphicsmill.com/samples/tags/read_md>): Reading images with codecs
- [Stream](<https://www.graphicsmill.com/samples/tags/stream_md>): Reading and writing images from streams
- [TIFF](<https://www.graphicsmill.com/samples/tags/tiff_md>): Reading and writing TIFF images
- [WPF](<https://www.graphicsmill.com/samples/tags/wpf_md>): Interoperability and conversion with WPF images
- [Write](<https://www.graphicsmill.com/samples/tags/write_md>): Writing images with codecs

## Input

- [IMG\_4212.HEIC](<https://www.graphicsmill.com/samples/Codecs/compiled/Debug/IMG_4212_HEIC>) (download)

## Output

- [ReadImageWithWPF.tif](<https://www.graphicsmill.com/samples/Codecs/compiled/Debug/ReadImageWithWPF_tif>) (download)
- [ReadImageWithWPF.txt](<https://www.graphicsmill.com/samples/Codecs/compiled/Debug/ReadImageWithWPF_txt>) (preview)

## Used APIs

- [Aurigma.GraphicsMill.Bitmap.DpiX](<https://www.graphicsmill.com/docs/gm/P_Aurigma_GraphicsMill_Bitmap_DpiX.htm.md>)
- [Aurigma.GraphicsMill.Bitmap.DpiY](<https://www.graphicsmill.com/docs/gm/P_Aurigma_GraphicsMill_Bitmap_DpiY.htm.md>)
- [Aurigma.GraphicsMill.Bitmap.Scan0](<https://www.graphicsmill.com/docs/gm/P_Aurigma_GraphicsMill_Bitmap_Scan0.htm.md>)
- [Aurigma.GraphicsMill.Bitmap.Stride](<https://www.graphicsmill.com/docs/gm/P_Aurigma_GraphicsMill_Bitmap_Stride.htm.md>)
- [Aurigma.GraphicsMill.Codecs.CustomImageReader](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Codecs_CustomImageReader.htm.md>)
- [Aurigma.GraphicsMill.Codecs.CustomImageReader.InternalImageReader](<https://www.graphicsmill.com/docs/gm/P_Aurigma_GraphicsMill_Codecs_CustomImageReader_InternalImageReader.htm.md>)
- [Aurigma.GraphicsMill.Codecs.ExifDictionary](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Codecs_ExifDictionary.htm.md>)
- [Aurigma.GraphicsMill.Codecs.IImageReader](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Codecs_IImageReader.htm.md>)
- [Aurigma.GraphicsMill.Codecs.MetadataDictionary.Add(Object,Object)](<https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Codecs_MetadataDictionary_Add_2_df764941.htm.md>)
- [Aurigma.GraphicsMill.Codecs.MetadataDictionary.Add(Object,Object\[\])](<https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Codecs_MetadataDictionary_Add_2_65aeff2b.htm.md>)
- [Aurigma.GraphicsMill.Codecs.MetadataDictionary.Contains(Object)](<https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Codecs_MetadataDictionary_Contains_1_d02429b3.htm.md>)
- [Aurigma.GraphicsMill.Codecs.MetadataDictionary.GetItemString(Object)](<https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Codecs_MetadataDictionary_GetItemString_1_d02429b3.htm.md>)
- [Aurigma.GraphicsMill.Codecs.MetadataDictionary.GetKeyDescription(Object)](<https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Codecs_MetadataDictionary_GetKeyDescription_1_d02429b3.htm.md>)
- [Aurigma.GraphicsMill.Codecs.MetadataDictionary.Keys](<https://www.graphicsmill.com/docs/gm/P_Aurigma_GraphicsMill_Codecs_MetadataDictionary_Keys.htm.md>)
- [Aurigma.GraphicsMill.Codecs.TiffWriter](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Codecs_TiffWriter.htm.md>)
- [Aurigma.GraphicsMill.Codecs.TiffWriter.Exif](<https://www.graphicsmill.com/docs/gm/P_Aurigma_GraphicsMill_Codecs_TiffWriter_Exif.htm.md>)
- [Aurigma.GraphicsMill.ColorPalette](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_ColorPalette.htm.md>)
- [Aurigma.GraphicsMill.ColorProfile](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_ColorProfile.htm.md>)
- [Aurigma.GraphicsMill.IImageParams](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_IImageParams.htm.md>)
- [Aurigma.GraphicsMill.ImageParams](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_ImageParams.htm.md>)
- [Aurigma.GraphicsMill.ImageParams.Create(IImageParams)](<https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_ImageParams_Create_1_0e3a4906.htm.md>)
- [Aurigma.GraphicsMill.Ink](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Ink.htm.md>)
- [Aurigma.GraphicsMill.Rational](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Rational.htm.md>)
- [Aurigma.GraphicsMill.UnsignedRational](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_UnsignedRational.htm.md>)
- [Aurigma.GraphicsMill.UnsupportedPixelFormatException](<https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_UnsupportedPixelFormatException.htm.md>)
