Click here to Skip to main content
Click here to Skip to main content

Saving Image Data in an XML File

, 11 May 2010
Rate this:
Please Sign up or sign in to vote.
How to save and retrieve binary image data in an XML file
BMP2XML

Introduction

In a recent Windows Forms project, I needed the ability to import and export graphics data from bitmaps to and from XML files. Since the bitmap data is binary and XML files only allow ASCII, it was apparent that a binary to ASCII conversion would be needed. A review of other articles and an internet search did not turn up anything much to my liking, so I decided to design and implement the needed functions myself. I hope that perhaps my efforts might be useful to anyone else with a similar problem to solve.

The particular application I was working on involved relatively small graphics symbols used in describing oil well cores and geological outcrops. The application stores all data, including symbols, in a database so that projects can be backed up or transferred simply by copying the database. In the application, the graphics symbols are stored as BLOBs or equivalent, however in transferring or sharing symbols between projects it was desirable to be able to save the data to an ASCII file containing all of the symbols needed. In that way, a corporate standard symbol library could be built up and made available with minimal effort.

The main problem in converting image data to and from an XML file format is the encoding of the binary data into an ASCII format that is compatible with XML files. Unfortunately I was unable to find a generic and generally accepted specification for saving binary data in XML files. At first, I decided to use hexadecimal encoding, however it was pointed out that .NET includes a function for Base64 encoding[^]. Since the images I needed to encode were relatively small, most any encoding would probably work, but Base64 is the simplest to implement, since it is already available as a .NET method.

The sample project included with this article contains the VS2008 C# project for reading and writing the XML files, as well as a couple of bitmap files (*.bmp) for testing. The main program allows the bitmaps to be imported and saved to the XML file and also imported from the XML file and displayed on the screen. In the sample project, the methods to create and read the XML files are included in the main form. The code to do the conversions in my real project is embedded in the methods that save and restore graphics symbols to the XML files.

Description of Code

The conversion from Bitmap to XML is done in 2 steps. First the Bitmap data is converted to a byte array, then the byte array is converted to an ASCII Base64 string. This could be coded in a single step, however I chose to use two steps to permit easily changing the encoding later if needed. The resulting string is then be saved as the value of the bitmap attribute of an XML <symbol name="sym_name" bitmap="hexstring" /> node.

In converting the bitmap to a byte array, a format was defined that saves the width and height (in pixels) of the image followed by an array of the pixel colors in order by rows. To accommodate 4 byte integers, the width and height numbers are specified as uints then coded most significant byte (MSB) first into 4 bytes. The pixel colors are extracted from the Bitmap using the GetPixel(i,j).ToArgb() method and the 32 bit color is then encoded in 4 bytes. I've not shown the details of converting width and height, but the brute force code to convert a pixel to 4 bytes is as follows:

    // b[] is the byte array defined as
    // byte[] b = new byte[4 * (bitmap.Height * bitmap.Width + 2)];
    // Loop through each row
    for (int j = 0; j < bitmap.Height; j++)
    {
        // Loop through the pixel on this row
        for (int i = 0; i < bitmap.Width; i++)
        {
            x = (uint)bitmap.GetPixel(i, j).ToArgb();
            y = (int)x;
            b[n] = (byte)(x / 0x1000000);
            x = x % (0x1000000);
            n++;
            b[n] = (byte)(x / 0x10000);
            x = x % (0x10000);
            n++;
            b[n] = (byte)(x / 0x100);
            x = x % 0x100;
            n++;
            b[n] = (byte)x;
            n++;
        }
    }

Once the byte array of the Bitmap data has been created, the simple application of Convert.ToBase64String() yields a string representation. Once the bitmap has been converted to a string, then it can be saved in an XML attribute. A sample of the created XML file is shown below:

<Graphics>
  <Symbols>
    <symbol name="BMP_1" bitmap="AAAAEAAAAA7/wcEA ... " />
    <symbol name="BMP_2" bitmap="AAAACgAAAAf/av9q ... " />
    <symbol name="BMP_3" bitmap="AAAACAAAAAj/AAAA ... " />
    <symbol name="BMP_4" bitmap="AAAAEgAAABP///// ... " />
  <Symbols>
</Graphics>

To read the bitmap data from the XML file and recreate the images, the above procedure is reversed. For each symbol in the XML file, the value of the bitmap attribute is extracted as a normal ASCII string which is then converted into a byte array using Convert.FromBase64String(). Then the bitmap can be reconstructed by extracting the width and height from the first 8 bytes, then each of the pixels from groups of 4 bytes. The code to accomplish this is as follows:

    private Bitmap BitmapFromByteArray(byte[] byteArray)
    {
        int n = 0;
        // Get the width
        uint x = (((uint)byteArray[n] * 256 + (uint)byteArray[n + 1]) * 256 
               + (uint)byteArray[n + 2]) * 256 + (uint)byteArray[n + 3];
        int width = (int)x;
        n += 4;
        // Get the height
        x = (((uint)byteArray[n] * 256 + (uint)byteArray[n + 1]) * 256 
          + (uint)byteArray[n + 2]) * 256 + (uint)byteArray[n + 3];
        int height = (int)x;
        n += 4;
        // Create the Bitmap object
        Bitmap bmp = new Bitmap(width, height);
        // The pixels are stored in order by rows
        for (int j = 0; j < height; j++)
        {
            // Read the pixels for each row
            for (int i = 0; i < width; i++)
            {
                x = (((uint)byteArray[n] * 256 + (uint)byteArray[n + 1]) * 256 
                  + (uint)byteArray[n + 2]) * 256 + (uint)byteArray[n + 3];
                bmp.SetPixel(i, j, Color.FromArgb((int)x));
                n += 4;
            }
        }
        return bmp;
    }

Conclusions

The code I've presented here is probably not optimized in any way. Certainly there are methods for packing binary data into fewer ASCII characters using techniques more advanced than Base64 encoding. I did not pursue those methods here.

It would seem that with the popularity of the XML data format, there should be a nice specification for saving binary data, such as a Bitmap, in an XML file. Apparently the XML group has at least thought about the problem, for example see http://www.xml.com/pub/a/98/07/binary/binary.html. Unfortunately, it doesn't appear that a simple specification has ever been formalized, at least not one that I could find. If a more universally accepted way to store binary data in an XML file is found, then I'll be happy to change my code accordingly.

Commenters have asked why the XML CDATA construct was not used to enclose the binary image data. This certainly could be done, but I see no real advantage, so CDATA was not used.

History

Date Description
10 May 2010 Initial submission
10 May 2010 Sure enough, had to fix some typos
11 May 2010 Modified to use Base64 encoding, per suggestion from commenters

License

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

About the Author

Walt Fair, Jr.
Engineer Comport Computing
United States United States
Walt has been playing with software since around 1967 and has generated more runtime errors than the average village idiot. He is a CEO, Petroleum Engineer, software consultant, janitor, and now a graduate student again. Rather than sleep, he also plays with algorithms and systems for technical computing, develops software for engineering evaluations and is an avid amateur radio operator.
 
Walt was admitted back to UT Austin and is actually attempting to complete a PhD in engineering, thereby proving that he is crazier than the average old fart.
 
And now UT has gone and admitted Walt to PhD candidacy, proving that old guys can still ... what was he doing again?

Comments and Discussions

 
GeneralMy vote of 5 PinmemberMember 916682515-Nov-12 2:41 
GeneralMy vote of 5 Pinmembermanoj kumar choubey23-Feb-12 19:37 
GeneralSmall Change PinmemberRod Kemp17-May-10 14:15 
GeneralRe: Small Change PinmemberWalt Fair, Jr.17-May-10 14:38 
GeneralInteresting! Pinmemberthund3rstruck11-May-10 8:47 
GeneralRe: Interesting! PinmemberWalt Fair, Jr.11-May-10 12:37 
GeneralMy vote of 1 PinmemberSuperToha11-May-10 3:33 
GeneralRe: My vote of 1 PinmemberWalt Fair, Jr.11-May-10 6:22 
GeneralIt might be faster if you do not use GetPixel/SetPixel PinmemberWong Shao Voon10-May-10 23:18 
GeneralRe: It might be faster if you do not use GetPixel/SetPixel PinmemberWalt Fair, Jr.11-May-10 6:23 
GeneralGood idea Pinmemberalexxksys10-May-10 21:58 
GeneralRe: Good idea PinmemberWalt Fair, Jr.11-May-10 6:21 
GeneralMy vote of 2 PinmemberJohn Brett10-May-10 21:49 
GeneralRe: My vote of 2 PinmemberWalt Fair, Jr.11-May-10 4:38 
GeneralRe: My vote of 2 PinmemberWalt Fair, Jr.11-May-10 6:20 
GeneralMy vote of 1 PinmemberPavel Vladov10-May-10 21:11 
GeneralRe: My vote of 1 PinmemberWalt Fair, Jr.11-May-10 6:19 
GeneralNice Idea PinmemberKing Balkoth10-May-10 7:36 
GeneralRe: Nice Idea PinmemberWalt Fair, Jr.10-May-10 7:54 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 11 May 2010
Article Copyright 2010 by Walt Fair, Jr.
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid