How to display a DIB or draw it into a Bitmap

A

active

I've been looking on the Internet for a way to convert a DIB to a Bitmap
without success.

Now I'm wondering if that is the approach I should be taking.

All I want to do is display the DIB or draw it into a Bitmap.

Is there a graphics method that can help?



thanks
 
M

Michael Phillips, Jr.

The System.Drawing.Bitmap object is a DIB. If you want a DDB, use
GetHbitmap. If you want to construct a Bitmap from an existing DIB created
with CreateDIBSection or a GDI DDB handle, use one of the constructors of
the Bitmap class.

Use the Graphics method DrawImage to display or convert to a different
PixelFormat.

If you want to work with the native GDI API, then use CreateBitmap to create
a DDB or use CreateDIBSection to create a DIB.

To convert one to the other, use BitBlt or any of the other GDI "BitBlt"
variations.
 
A

active

I hate it when I don't ask the question right.

I want to get a DIB from the clipboard.

What the clipboard produces is a MemoryStream.

I have read that stream and obtained the palette in the DIB.

Now I'd like to get the bitmap which I think is harder to read from the
stream because there are so many variations.

I suppose at this point it is the same as reading the bitmap from a file
into a Bitmap.

That's what I've been looking for.


Thanks
 
M

Michael Phillips, Jr.

What the clipboard produces is a MemoryStream.

You can construct a Bitmap object from a MemoryStream object as is. Use the
Bitmap constructor or method which uses a stream object as an argument.
 
A

active

I've tried that, can't remember what happened but I concluded that the
stream for the constructor should start with the bitmap data and not with
the stuff in the stream for the header and palette. Once I read the header
and the palette I must be close.

Is that true?

If so, what I need to know might be simply (or not so simply) where to
position the stream. Just after the palette?
Then maybe use a seek command.
Or convert the stream to bytes and from bytes to a stream without the
leading bytes.



Thanks
 
M

Michael Phillips, Jr.

Sorry about my last post.

If you are specifically pulling a DIB off the clipboard, you need to do some
more work.

Follow these steps:
1) Read the BITMAPINFOHEADER from the stream
2) Read the color table or bitfield array, if any. Only Indexed bitmaps
have a color table. Only 16bit or 32bit bitmaps may have a bitfiield array.
3) Read the Bitmap's color pixel data.

Once you have read the above from the memory stream, you may use any one of
the Bitmap constructors or methods to create your bitmap.

If you want to do the least amount of work possible, then do the following:
1) Create a new MemoryStream object.
2) Create a BITMAPFILEHEADER object
Fill in the correct structure offsets. Write this structure to your new
MemoryStream
3) Using the MemoryStream from the clipboard, write it to the new
MemoryStream object following the BITMAPFILEHEADER
4) Construct your Bitmap from the new MemoryStream object as it is now in
the correct format for the Bitmap constructor

The above requires the least amount of work and marshaling. Just make sure
that you set the offsets in the BITMAPFILEHEADER structure correctly and you
must rewind the stream before using it to construct your Bitmap object.
 
A

active

I used your second approach and it work great.
At least so far.
I used: bfOffBits = 1078
which I got from the Internet.

But I see other expressions which seem to require good knowledge of the
Bitmap specifications to understand.

Is 1078 OK?

If there a more general expression using data from the BITMAPINFOHEADER?

Thanks a lot for the previous precise instructions!
 
M

Michael Phillips, Jr.

Is 1078 OK?

Yes only for 8bpp bitmap with 256 colors in the palette.
You should always calculate the offsets by parsing the headers.

The typical memory layout of a bitmap is as follows:
BITMAPFILEHEADER 14 bytes
BITMAPINFOHEADER 40 bytes most of the time. Always use the size given by
the structure member
color palette 4 * nColors in palette = 1024 bytes only for 256 color
palette
for a 16bpp or 32bpp bitmap with bitfields then there will be 3 DWORDS here
for the masks instead of the color palette
bitmaps bits <---------------offset = 1078 only for an indexed bitmap with
256 colors in the palette

Do not assume anything. Parse the headers from the memory stream to
calculate the offset correctly.
You could always rewind the stream after your done calculating the correct
bitmap bit's offset.
 
A

active

Is this what you said, converted to code?



BmH.bfOffBits = CInt(14 + BiSize)

If BiBitCount <= 8 Then

BmH.bfOffBits += 4 * (1 << BiBitCount)

ElseIf BiBitCount = 24 Then

BmH.bfOffBits += CInt(4 * BiClrUsed)

Else 'must be (BiBitCount = 16 Or BiBitCount = 32)

If BiCompression = BI_BITFIELDS Then

BmH.bfOffBits += 12

else

????? what about this

End If

End If
 
M

Michael Phillips, Jr.

ElseIf BiBitCount = 24 Then
BmH.bfOffBits += CInt(4 * BiClrUsed)

24bpp images do not have a color table. The offset remains unchanged.
If BiCompression = BI_BITFIELDS Then

BmH.bfOffBits += 12

else

????? what about this


If BI_BITFIELDS is not set then there are no masks. The offset remains
unchanged.

Generally, if you image is not indexed or has BI_BITFIELDS then the bitmap's
bits will directly follow the BITMAPINFOHEADER.

For indexed bitmaps the size of the color table is always the number of
colors in the palette * 4.
However an indexed bitmap does not have to use all of the possible colors.
If BiBitCount <= 8 Then

BmH.bfOffBits += 4 * (1 << BiBitCount)

Always look at the number of colors used as specified in the
BITMAPINFOHEADER(i.e., biClrUsed).
Gif files are 8bpp but do not always use all of the 256 colors that are
possible in their palette.
If biClrUsed = 0 then the bitmap uses the maximum number of colors for it's
bit depth. Else the bClrUsed should be used for your calculation.
 
A

active

I hope I have it correct now.
Besides, I thought it nice to get it on the Internet documented
'Specifies the offset, in bytes, from the BITMAPFILEHEADER structure to the
bitmap bits

BmH.bfOffBits = CInt(14 + BiSize)

If BiBitCount <= 8 Then

If BiClrUsed = 0 Then

'If biClrUsed = 0 then the bitmap uses the maximum number of colors

BmH.bfOffBits += 4 * (1 << BiBitCount) 'color size * max. 1->2 4->16
8->256...16,24,32

Else

BmH.bfOffBits += 4 * CInt(BiClrUsed) 'color size * NColors

End If

ElseIf BiBitCount = 24 Then

'no palette so add nothing here

Else 'must be (BiBitCount = 16 Or BiBitCount = 32)

If BiCompression = BI_BITFIELDS Then

BmH.bfOffBits += 12 '3 RGB doubleword masks

Else

'no masks so add nothing here

End If

End If

BmH.bfSize = CInt(BmH.bfOffBits + DibClipboardStream.Length) ' size of file
in bytes
 
A

active

The code below is hopefully a mapping of comments by Michael Phillips, Jr.

Michael, do you agree it is OK.

If so, I'd suggest that someone with a vb.net site copy it and also find
Michael's post relating to putting/retrieving a palette on/from the
clipboard.

Both examples are complete and do not appear anywhere else. It would be nice
to save them!


Thanks again to Michael!

'Offset, in bytes, from the BITMAPFILEHEADER structure to the bitmap bits

BmH.bfOffBits = CInt(14 + BiSize)

If BiBitCount <= 8 Then

If BiClrUsed = 0 Then

'If biClrUsed = 0 then the bitmap uses the maximum number of colors

BmH.bfOffBits += 4 * (1 << BiBitCount) 'color size * max colors

Else

BmH.bfOffBits += 4 * CInt(BiClrUsed) 'color size * NColors

End If

ElseIf BiBitCount = 24 Then

'no palette so add nothing here

Else 'must be (BiBitCount = 16 Or BiBitCount = 32)

If BiCompression = BI_BITFIELDS Then

BmH.bfOffBits += 12 '3 RGB doubleword masks

Else

'no masks so add nothing here

End If

End If

BmH.bfSize = CInt(BmH.bfOffBits + DibClipboardStream.Length) ' size of file
 
M

Michael Phillips, Jr.

BmH.bfOffBits = CInt(14 + BiSize)

It is not a good practice to hard code a structure's size(e.g.,
BITMAPFILEHEADER).

You can never be sure that the structure will always compile to that
size(i.e., 14 bytes) unless you lay it out explicitly(i.e., byte align)
or that Microsoft will not change the structure.

Other than that, I recommend that you test your code with other applications
that place/retrieve DIBs on the clipboard(e.g., Office, Paint, Photoshop,
etc.)
You can use the free clipboard viewer as a comparison to demonstrate that
your code get's the expected result. The other applications can be used to
test
whether or not the Dibs that your save as a file can be opened and displayed
properly.
 
A

active

I changed the code below to
BmH.bfOffBits = Marshal.SizeOf(BmH) + CInt(BiSize)

When checking I noticed that Marshal.SizeOf(BmH) returned 16!

So to the structure I added:
<StructLayout(LayoutKind.Sequential, Pack:=2)> _

which fixed it (to 14).

But raised the question how come it worked with 16?

I guess the reason why it worked with 16 is because I wrote the items to the
stream, one at a time rather than writing the structure.

But if the structures were the correct size and bfOffBits pointed wrong, I
believe the first 2 bytes of the data would be skipped over.



Make sense to you?



While searching the Internet I noticed that you have been helping people for
years!

Thanks, took a while for this loop to converge

BTW Even though Visual Basic compiler specifies Sequential layout for value
types, it would not accept Pack without Sequential being explicitly
included in the statement.
 
M

Michael Phillips, Jr.

Make sense to you?

Yes.

When you marshal between managed and unmanaged code this type of alignment
issue can easily cause you headaches.

The correct calculation of the offset for .bfOffBits is important. I have
seen code on the internet that doesn't even look at it.
It assumes that the structures are laid out one after the other with the
bitmap's bits following the structures, which is not necessarily correct.

If you want to use memory mapped files for very large bitmaps and pass a
hSection to a memory mapped file
and dwOffset for the bits to CreateDIBSection, the documentation states that
the dwOffset passed must be located
on a DWORD boundary.

If you do not align on a DWORD boundary, the bits would be located at an
offset that is off by 2 bytes.
CreateDIBSection would fail!

For a 16bpp, 24bpp or 32bpp bitmap:
BITMAPFILEHEADER 14 bytes
BITMAPINFOHEADER 40 bytes
bits <---------------------54 this offset is off by two.
bits<----------------------56 this offset is aligned on a DWORD boundary.

If you loaded a bitmap that was aligned this way and did not check the
..bfOffBits offset,
CreateDIBSection would fail!

The moral to the above is assume nothing. Always check!
While searching the Internet I noticed that you have been helping people
for years!

I am happy to provide assistance.
 
A

active

Michael Phillips said:
Yes.

When you marshal between managed and unmanaged code this type of alignment
issue can easily cause you headaches.

The correct calculation of the offset for .bfOffBits is important. I
have seen code on the internet that doesn't even look at it.
It assumes that the structures are laid out one after the other with the
bitmap's bits following the structures, which is not necessarily correct.

Isn't that what I'm doing unless BiCompression=BI_BITFIELDS?
Is that when it is not correct?
If you want to use memory mapped files for very large bitmaps and pass a
hSection to a memory mapped file
and dwOffset for the bits to CreateDIBSection, the documentation states
that the dwOffset passed must be located
on a DWORD boundary.
I read that.
If you do not align on a DWORD boundary, the bits would be located at an
offset that is off by 2 bytes.
CreateDIBSection would fail!

For a 16bpp, 24bpp or 32bpp bitmap:
BITMAPFILEHEADER 14 bytes
BITMAPINFOHEADER 40 bytes
bits <---------------------54 this offset is off by two.
bits<----------------------56 this offset is aligned on a DWORD boundary.

I'm sorry, I can't follow this. Isn't 14 and 54 the correct values.
And my bfOffBits points to a location not on a DWORD boundary.

I think "bits <---------------------54 " means bfOffBits contains 54.
Right?

Then containing 56 does point to a DWORD boundary.
But 56 is the wrong value.

Guess I'm confused.
 
M

Michael Phillips, Jr.

Isn't that what I'm doing unless BiCompression=BI_BITFIELDS?
Is that when it is not correct?
I'm sorry, I can't follow this. Isn't 14 and 54 the correct values.

Your code is correct. Alignment on a DWORD boundary is only an issue if you
want to use memory mapped bitmap files.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top