Groups | Blog | Home
all groups > dotnet drawing api > january 2007 >

dotnet drawing api : Loading image byte array



Michael Phillips, Jr.
1/31/2007 10:15:53 AM
You load your bitmap as a memory stream. You then discard the
BITMAPFILEHEADER. What you have left is a packed DIB.

The packed DIB represents the BITMAPINFOHEADER, palette, if indexed, and the
bitmap's image byte array.


[quoted text, click to view]

Andreas
1/31/2007 3:33:52 PM
Hi,

I recently found a thread where Frank Hileman suggested using the Bitmap
overload that enables you to get direct access to the image byte-array,
opposed to using unsafe code along with lockbits. I've written a small
wrapper for this functionality and its working ok. My question is, how would
I load an image from a file into this byte array? Obviously using the
Bitmap.FromFile() it out of the question since I dont want an extra bitmap
object in memory and besides it would require me to transfer pixel by pixel
over to the byte array.

So does anyone have any suggestions on how to do this, while (preferbly)
keep the ability to load various formats (I will settle for the onces that
Bitmap.FromFile can handle for now)? Speed is also a factor I would like to
increase as much as possible. It would require me to get the image
dimentions as well so I can initilize the correct byte array, get a known
stide size and so on.. you get the picture (no phun intended) =)


Thanks!

Andreas

Bob Powell [MVP]
1/31/2007 10:18:16 PM
Ahh, despite all the refinements of .NET we still find ourselves up to
the elbows in header files eh?

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.





[quoted text, click to view]
Andreas
2/1/2007 7:49:05 AM
So there is a tradeoff between speed of rendering and ease of loading =(

"Bob Powell [MVP]" <bob@_spamkiller_bobpowell.net> skrev i meddelandet
news:%23eX$T1XRHHA.4404@TK2MSFTNGP03.phx.gbl...
[quoted text, click to view]
Michael Phillips, Jr.
2/1/2007 10:07:56 AM
You must load and parse the headers yourself. Read them one at a time and
use seek, if necessary, to move around the stream.

The BITMAPFILEHEADER tells you that you have a valid bitmap and the actual
location of the bitmap's byte array.

The Bitmap's constructor needs the BITMAPINFO header and the byte array for
the bits.

Do interop the easy way by navigating your web browser to pinvoke.net. That
site will help you set up the headers.


[quoted text, click to view]

Michael Phillips, Jr.
2/1/2007 11:14:15 AM
[quoted text, click to view]

Yes

[quoted text, click to view]

The BITMAPFILEHEADER tells you that your image is in fact a bitmap and not a
..png, .gif, .jpeg, etc.

It is important to look at the offset in the BITMAPFILEHEADER for the
location of the bitmap's byte array, because it does not necessarily follow
that this location is directly after the BITMAPINFOHEADER and the palette,
if the bitmap is indexed. There are some applications that make use of file
mapping. In that case, the bits must be DWORD aligned in the actual file so
the location may differ by a few bytes.

[quoted text, click to view]

palette or color table if the bitmap is 256 colors or less

DWORD masks, if necessary, for 16bpp or 32bpp bitmap images.
If the bitmap uses the defaults, the masks are not necessary.

[quoted text, click to view]

Yes, if following the original Windows 3.1 bitmap specification. No, if the
application is using file mapping. The DIB will be located on a DWORD
aligned location in the file.

You may use the Marshal class to fill in the headers for the following:

[quoted text, click to view]

The above is one way to load a bitmap and use Frank Hileman's method to
speed access using System.Drawing methods.


Frank Hileman
2/1/2007 3:34:59 PM
Load it first into a Bitmap as usual. You will know the width and height at
that point Then create a Graphics using a fast-access second bitmap (we call
it EffectBuffer), and render into that second bitmap using
DrawImageUnscaled. Dispose the first Bitmap.

Also I just posted some more information on the fast access technique in the
original thread.

Regards,
Frank Hileman

check out VG.net: http://www.vgdotnet.com
Animated vector graphics system
Integrated Visual Studio graphics editor

[quoted text, click to view]

Andreas
2/1/2007 3:42:15 PM
So if I want to use the fast bitmap access code I will have to implement
custom fileformat loaders? That's defintilty a bit over my head at this
moment, very new to graphics programming. I get this far then I come to a
screaming halt. The loading of the byte-array is very fast

DialogResult results = this.openFileDialog1.ShowDialog();
if (results == DialogResult.OK)
{
if (File.Exists(this.openFileDialog1.FileName))
{
byte[] contents = null;
using (FileStream reader =
File.OpenRead(this.openFileDialog1.FileName))
{
contents = new byte[reader.Length];
reader.Read(contents, 0, (int)reader.Length);
reader.Close();
}
}
}

"Andreas" <dont.need@one.com> skrev i meddelandet
news:ObOyczcRHHA.3316@TK2MSFTNGP02.phx.gbl...
[quoted text, click to view]

Andreas
2/1/2007 4:26:10 PM
Michael,

Please have patience with me, hehe. So is this
http://pinvoke.net/default.aspx/Structures.BITMAPINFOHEADER the header info
I should use in my application? The pinvoke site doesnt declare the
BITMAPFILEHEADER header. Also, Im probably completly wrong here, but are you
saying that the difference in the fileformats are the BITMAPFILEHEADER and
the rest is the same for all format, i.e a "DIB" section ?

According to http://www.webopedia.com/TERM/D/DIB.html a DIB is something
used with BMP fileformats. Then again maybe the Bitmap class in dotnet works
with a DIB internally, to be format independant? Like I've said Im very new
to graphics programming and I havent even started disecting fileformats to
get the basic understandings from them. I see this as a challenge though! So
I would like to get this to work to add to my knowledge!

So once I have my complete byte-array and I have defined the
BITMAPINFOHEADER structure in my application.. then what? Im guessing
something like this

The image fileformats roughly look something like this

-- start of file --
BITMAPFILEHEADER
BITMAPINFOHEADER
DIB
-- end of file --

(1) I know the size of the BITMAPFILEHEADER in bytes, so I skip them in my
bye-array. This is the offset, in the byte-array, for the BITMAPINFOHEADER
(2) I know the offset and size for the BITMAPINFOHEADER in bytes, so I use
some kind of memory copy to fill a BITMAPINFOHEADER structure
(3) From the BITMAPINFOHEADER I can then dimension my new byte-array (to be
used with the Bitmap constructor which takes the pointer to the byte-array).
I now also know the offset, in bytes, to the DIB section of the file.
(4) I use some kind of memory copy to copy the DIB section information into
the byte array I was able to create in (3)
(5) I create the bitmap object (Bitmap x = new Bitmap(width,height, stride,
pixelformat, intptr)


Thanks

Andreas


"Michael Phillips, Jr." <mphillips53@nospam.jun0.c0m> skrev i meddelandet
news:eTRNELhRHHA.2172@TK2MSFTNGP04.phx.gbl...
[quoted text, click to view]

Frank Hileman
2/2/2007 9:08:48 AM
Hi Andreas,

Yes, the double memory is the tradeoff with using the built-in bitmap file
reader. We can hope that if the file format is compressed, the first bitmap
will be smaller than the second (the second is fully uncompressed). Also, if
you care about speed, I don't recommend PictureBox, but a Control-derived
class and paint a portion of the bitmap directly on that.

Regards,
Frank Hileman

[quoted text, click to view]

Michael Phillips, Jr.
2/2/2007 9:31:16 AM
[quoted text, click to view]

It's the Windows 3.1 Bitmap specification(i.e.,top left pixel at the bottom)
.. That is how it is stored in the file. You may flip it before creating or
after creating the bitmap object.

Michael Phillips, Jr.
2/2/2007 9:42:01 AM
[quoted text, click to view]

For compressed image formats like jpeg, png, gif, tiff, etc., you will not
know what the image array looks like until it is fully decompressed.

Loading, parsing headers and decompressing image bytes for the other image
formats is best left for the gdiplus decoders.

Frank Hileman's suggestion to let gdiplus load and decode the image is the
easy way to get the job done.

Just create a second bitmap object with his method and use DrawImage to
transfer the image from the source to the optimized bitmap object and then
delete the original bitmap object.



Andreas
2/2/2007 10:00:34 AM
Hi,

Thanks for your patience and help. I've managed to successfully load the DIB
of the bitmap and use Frank Hileman's suggested method for creating the
bitmap. It's lightning fast. I loaded a 3000x3000x24bit BMP into a
picturebox and it took about ½ second to do it.

However, I have come across something strange. My bitmap loads perfectly,
but when its displayed in the picturebox its flipped on the vertical axis
(top left pixel is drawn at bottom left and so on). What am I missing here?

Thanks,

Andreas

"Michael Phillips, Jr." <mphillips53@nospam.jun0.c0m> skrev i meddelandet
news:unLCPwhRHHA.4744@TK2MSFTNGP02.phx.gbl...
[quoted text, click to view]

Andreas
2/2/2007 10:05:21 AM
Thanks Frank, I actually got it working myself before I saw your code. I
load the my image like Michael Phillips suggested. Sure I'm currently
limited to BMP fileformat but that can be changed. Wouldnt your method mean
I would your method mean I would, for a moment, have two bitmaps at the
same time? If I use large images then this will eat memory for sure.

The one advantage with your method is that I will be able to use various
fileformats "out of the box" and don't need to make my own file format
parsers (BMP was easy, but I fear PNG; JPG and GIF can be a bit tricker).


"Frank Hileman" <frankhil@no.spamming.prodigesoftware.com> skrev i
meddelandet news:%23RIeYmlRHHA.4632@TK2MSFTNGP04.phx.gbl...
[quoted text, click to view]

Andreas
2/2/2007 10:08:26 AM
By the way Michael, do you know if other file formats like png, gif and jpg
(after decompression and such) all have a "DIB" section as well that I can
extract and send to the Bitmap contructor overload just like a bmp image? Or
will I have to parse, for example, a png file, retrieve its image
information and fill out my own DIB which is then used byt the Bitmap
constructor overload?

Hope it was somewhat clear ;)


"Michael Phillips, Jr." <mphillips53@nospam.jun0.c0m> skrev i meddelandet
news:unLCPwhRHHA.4744@TK2MSFTNGP02.phx.gbl...
[quoted text, click to view]

AddThis Social Bookmark Button