Click here to Skip to main content
15,885,216 members
Articles / Desktop Programming / XAML

Saving Bitmaps to Isolated Storage in Silverlight 3

Rate me:
Please Sign up or sign in to vote.
4.20/5 (6 votes)
31 Jul 2009CPOL3 min read 37.2K   12   2
How to save bitmaps to isolated storage in Silverlight 3

There may be times when you wish to save a bitmap image to the user's local storage. Perhaps it was a generated image that is used in the application, or maybe it is an externally referenced image that you are loading locally for caching purposes. I found many examples online of generating a "save dialog" box, but none for saving it.

This is a very bare bones application that demonstrates how to save the image. This is in no industry-standard format - it literally writes out the size of the image in pixels (height, width), then streams out the bytes for alpha, red, green, and blue. It is meant as a foundation to better understand how to get the image data and manipulate it. Once you "know" the pixels, then you can easily start to apply more advanced algorithms like BMP, PNG, JPG, or even GIF to save and retrieve the data.

Here is the XAML - you can create a new Silverlight Application and literally plop in the XAML and code-behind to get started:

XML
<UserControl x:Class="BitmapSaver.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d=http://schemas.microsoft.com/expression/blend/2008 
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Canvas HorizontalAlignment="Left" x:Name="CanvasSource"
		Height="20" Width="100" Grid.Row="0">
                <TextBlock FontSize="14" VerticalAlignment="Center" 
		HorizontalAlignment="Center" Width="Auto" Height="Auto" 
		Foreground="Red" FontWeight="Bold" Text="BitMapExample"/>             
        </Canvas>
        <Image HorizontalAlignment="Left" x:Name="BitmapImageRef" Stretch="None" 
		Grid.Row="1"/>
        <TextBlock x:Name="Status" Text="Processing..." Grid.Row="2"/>        
  </Grid>
</UserControl>

In the XAML, there is a grid with three rows. The first is a canvas with red text inside it. This is what we'll turn into a bitmap image and save. The second row is an image "placeholder" for the image that we process. The final row is a simple text block to update the status.

Here is the code-behind:

C#
public partial class MainPage
{
    private const string SAVEDIMG = "SavedBitmapImage.xyz";
        
    /// <summary>
    ///     Default constructor
    /// </summary>
    public MainPage()
    {
        InitializeComponent();
        CanvasSource.Loaded += _MainPageLoaded;
    }

    /// <summary>
    ///     Canvas loaded
    /// </summary>
    /// <param name="sender">
    /// <param name="e">
    void _MainPageLoaded(object sender, RoutedEventArgs e)
    {
        Status.Text = "Checking disk...";
        byte[] buffer = _LoadIfExists(SAVEDIMG); 

        if (buffer.Length > 0)
        {
            Status.Text = "Loading...";
            BitmapImageRef.Source = _GetImage(buffer);
            Status.Text = "Loaded.";
        }
        else
        {
            Status.Text = "Rendering...";
            WriteableBitmap bitmap = new WriteableBitmap
				(CanvasSource, new TransformGroup());
            BitmapImageRef.Source = bitmap;
            Status.Text = "Saving...";
            _SaveToDisk(_GetSaveBuffer(bitmap), SAVEDIMG);
            Status.Text = "Saved.";
        }                       
    }

    /// <summary>
    ///     Load file, if it exists
    /// </summary>
    /// <param name="fileName">The filename</param>
    /// <returns>The byte array for the file</returns>
    private static byte[] _LoadIfExists(string fileName)
    {
        byte[] retVal;

        using (IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (iso.FileExists(fileName))
            {
                using (IsolatedStorageFileStream stream = 
				iso.OpenFile(fileName, FileMode.Open))
                {
                    retVal = new byte[stream.Length];
                    stream.Read(retVal, 0, retVal.Length);
                }
            }
            else
            {
                retVal = new byte[0];
            }
        }
        return retVal;
    }

    /// <summary>
    ///     Saves to isolated storage
    /// </summary>
    /// <param name="buffer">The buffer</param>
    /// <param name="fileName"></param>
    private static void _SaveToDisk(byte[] buffer, string fileName)
    {
        using (IsolatedStorageFile iso = 
			IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (
                IsolatedStorageFileStream stream = 
			new IsolatedStorageFileStream(fileName, FileMode.CreateNew,
                           	iso))
            {
                stream.Write(buffer, 0, buffer.Length);
            }
        }
    }

    /// <summary>
    ///     Gets an image from storage
    /// </summary>
    /// <param name="buffer"></param>
    /// <returns>The bitmap</returns>
    private static WriteableBitmap _GetImage(byte[] buffer)
    {
        int width = buffer[0]*256 + buffer[1];
        int height = buffer[2]*256 + buffer[3];

        long matrixSize = width*height;

        WriteableBitmap retVal = new WriteableBitmap(width, height);

        int bufferPos = 4; 

        for (int matrixPos = 0; matrixPos < matrixSize; matrixPos++)
        {
            int pixel = buffer[bufferPos++];
            pixel = pixel << 8 | buffer[bufferPos++];
            pixel = pixel << 8 | buffer[bufferPos++];
            pixel = pixel << 8 | buffer[bufferPos++];
            retVal.Pixels[matrixPos] = pixel;
        }

        return retVal; 
    }

    /// <summary>
    ///     Gets the buffer to save to disk from the writeable bitmap
    /// </summary>
    /// <param name="bitmap">The bitmap image</param>
    /// <returns>The buffer of bytes</returns>
    private static byte[] _GetSaveBuffer(WriteableBitmap bitmap)
    {
        long matrixSize = bitmap.PixelWidth*bitmap.PixelHeight;
        
        long byteSize = matrixSize*4 + 4; 

        byte[] retVal = new byte[byteSize];

        long bufferPos = 0;

        retVal[bufferPos++] = (byte) ((bitmap.PixelWidth / 256) & 0xff);
        retVal[bufferPos++] = (byte) ((bitmap.PixelWidth % 256) & 0xff);
        retVal[bufferPos++] = (byte) ((bitmap.PixelHeight / 256) & 0xff);
        retVal[bufferPos++] = (byte) ((bitmap.PixelHeight % 256) & 0xff);

        for (int matrixPos = 0; matrixPos < matrixSize; matrixPos++)
        {
            retVal[bufferPos++] = (byte)((bitmap.Pixels[matrixPos] >> 24) & 0xff);
            retVal[bufferPos++] = (byte)((bitmap.Pixels[matrixPos] >> 16) & 0xff);
            retVal[bufferPos++] = (byte)((bitmap.Pixels[matrixPos] >> 8) & 0xff);
            retVal[bufferPos++] = (byte)((bitmap.Pixels[matrixPos]) & 0xff);    
        }

        return retVal;
    }
}

The main process simply waits for the canvas to be loaded.

We check if the image is saved in isolated storage. If it exists, we render it and attach it to the image. If not, we render it based on the canvas in the first row and then save it.

_LoadIfExists is a basic routine that checks for a file's existence and if it is there, loads it into a byte buffer and returns it. If the file is not there, it returns an empty byte array.

_SaveToDisk takes a byte array and persists it to isolated storage. Possible enhancements to this routine include checking to see if the available storage exists and prompting the user to extend it if needed, as well as organizing files into subdirectories and checking for/creating those as well.

_GetSaveBuffer takes the bitmap and returns a buffer of bytes. The first two bytes are the width, the second two the height, and finally the remaining bytes contain the alpha, red, green, and blue channels for each pixel in the bitmap. Obviously this is where you can conform to any one of the graphics standards available and supply your own compression.

_GetImage takes the buffer for an image saved in the previous format, and renders it to the bitmap so it can be displayed.

The idea behind this example is to run in debug and step through to see. The first time you run it, it will simply copy the image (the text in this case) and display it as well as save it to isolated storage. Any subsequent run will load it from storage and show it unless you clear storage or change the file name.

Again, this is just a basic introduction to manipulating and saving bitmaps in isolated storage.

Jeremy Likness

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Program Manager Microsoft
United States United States
Note: articles posted here are independently written and do not represent endorsements nor reflect the views of my employer.

I am a Program Manager for .NET Data at Microsoft. I have been building enterprise software with a focus on line of business web applications for more than two decades. I'm the author of several (now historical) technical books including Designing Silverlight Business Applications and Programming the Windows Runtime by Example. I use the Silverlight book everyday! It props up my monitor to the correct ergonomic height. I have delivered hundreds of technical presentations in dozens of countries around the world and love mentoring other developers. I am co-host of the Microsoft Channel 9 "On .NET" show. In my free time, I maintain a 95% plant-based diet, exercise regularly, hike in the Cascades and thrash Beat Saber levels.

I was diagnosed with young onset Parkinson's Disease in February of 2020. I maintain a blog about my personal journey with the disease at https://strengthwithparkinsons.com/.


Comments and Discussions

 
GeneralMy vote of 5 Pin
viragdesai25-Sep-12 18:04
viragdesai25-Sep-12 18:04 
GeneralMy vote of 1 Pin
Member 829288310-Oct-11 13:16
Member 829288310-Oct-11 13:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.