Click here to Skip to main content
15,883,873 members
Articles / Programming Languages / XML

Saving Image Data in an XML File

Rate me:
Please Sign up or sign in to vote.
4.56/5 (18 votes)
11 May 2010CPOL5 min read 101K   3.3K   47   20
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:

C#
// 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:

XML
<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:

C#
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

DateDescription
10 May 2010Initial submission
10 May 2010Sure enough, had to fix some typos
11 May 2010Modified 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)


Written By
Engineer Comport Computing
United States United States
I am a software developer specializing in technical and numerical software systems and also a PhD Petroleum Engineer.
I started programming in IBM 1620 machine code in 1967, then FORTAN on CDC mainframes mainframes in 1970. I later used ALGOL, BASIC, FORTH, Pascal,Prolog, C, F#, C#, etc.
I generally use whatever language available thatallows me to accomplish what is neccesary.

Comments and Discussions

 
QuestionHow do I run this code Pin
Member 1449347210-Jun-19 21:55
Member 1449347210-Jun-19 21:55 
GeneralMy vote of 5 Pin
Member 916682515-Nov-12 2:41
Member 916682515-Nov-12 2:41 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey23-Feb-12 19:37
professionalManoj Kumar Choubey23-Feb-12 19:37 
GeneralSmall Change Pin
Rod Kemp17-May-10 14:15
Rod Kemp17-May-10 14:15 
GeneralRe: Small Change Pin
Dr.Walt Fair, PE17-May-10 14:38
professionalDr.Walt Fair, PE17-May-10 14:38 
GeneralInteresting! Pin
thund3rstruck11-May-10 8:47
thund3rstruck11-May-10 8:47 
GeneralRe: Interesting! Pin
Dr.Walt Fair, PE11-May-10 12:37
professionalDr.Walt Fair, PE11-May-10 12:37 
GeneralMy vote of 1 Pin
SuperToha11-May-10 3:33
SuperToha11-May-10 3:33 
GeneralRe: My vote of 1 Pin
Dr.Walt Fair, PE11-May-10 6:22
professionalDr.Walt Fair, PE11-May-10 6:22 
GeneralIt might be faster if you do not use GetPixel/SetPixel Pin
Shao Voon Wong10-May-10 23:18
mvaShao Voon Wong10-May-10 23:18 
GeneralRe: It might be faster if you do not use GetPixel/SetPixel Pin
Dr.Walt Fair, PE11-May-10 6:23
professionalDr.Walt Fair, PE11-May-10 6:23 
GeneralGood idea Pin
alexxksys10-May-10 21:58
alexxksys10-May-10 21:58 
GeneralRe: Good idea Pin
Dr.Walt Fair, PE11-May-10 6:21
professionalDr.Walt Fair, PE11-May-10 6:21 
GeneralMy vote of 2 Pin
John Brett10-May-10 21:49
John Brett10-May-10 21:49 
GeneralRe: My vote of 2 Pin
Dr.Walt Fair, PE11-May-10 4:38
professionalDr.Walt Fair, PE11-May-10 4:38 
GeneralRe: My vote of 2 Pin
Dr.Walt Fair, PE11-May-10 6:20
professionalDr.Walt Fair, PE11-May-10 6:20 
GeneralMy vote of 1 Pin
Pavel Vladov10-May-10 21:11
Pavel Vladov10-May-10 21:11 
GeneralRe: My vote of 1 Pin
Dr.Walt Fair, PE11-May-10 6:19
professionalDr.Walt Fair, PE11-May-10 6:19 
GeneralNice Idea Pin
King Balkoth10-May-10 7:36
King Balkoth10-May-10 7:36 
GeneralRe: Nice Idea Pin
Dr.Walt Fair, PE10-May-10 7:54
professionalDr.Walt Fair, PE10-May-10 7:54 

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.