Saving Image Data in an XML File






4.56/5 (16 votes)
How to save and retrieve binary image data in an XML file

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 uint
s then coded most significant byte (MSB) first into 4 byte
s. 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 |