How come biClrUsed is always zero?

  • Thread starter Thread starter active
  • Start date Start date
A

active

The code below does much as expected.

For example

biSize=40



but biClrUsed is always zero!

InBitmap.Palette.Entries.Length has the expected value

Marshal.SizeOf(dibSec)) is 84 as expected

GetObject succeeds

Any suggestions?



Thanks a lot



Dim hInBitmap As IntPtr = InBitmap.GetHbitmap()

Dim dibSec As New Wnd.DIBSECTION

Dim dibSecPtr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(dibSec))

If Wnd.GetObject(hInBitmap, Marshal.SizeOf(dibSec), dibSecPtr) <>
Marshal.SizeOf(dibSec) Then

'???release hInBitmap???

Exit Sub

End If

dibSec = CType(Marshal.PtrToStructure(dibSecPtr, dibSec.GetType),
Wnd.DIBSECTION)

Dim NColor As Integer

'???dibSec.dsBmih.biClrUsed is always zero???

If .biClrUsed > 0 Then

NColor = CInt(.biClrUsed)

ElseIf .biBitCount < 24 Then

NColor = 1 << .biBitCount

Else

'??? Can this be other than 24 if we get here???

NColor = 0

End If

End With



NColor = InBitmap.Palette.Entries.Length
 
Dim hInBitmap As IntPtr = InBitmap.GetHbitmap()

The above always returns a 32bpp bitmap.

32bpp bitmaps have no color table and therefore biClrUsed will always be 0.
 
I'm trying to put a 8bpp on the clipboard
You can if you pass a handle created by CreateDIBSection


I'm trying to generate the arguments for CreateDIBSection

Can't get past GetObject


Michael Phillips said:
The above always returns a 32bpp bitmap.

I don't understand what the System does.
I ask for a handle to and 8bpp and it must generate a new bitmap someplace
that's 32bpp and give me a handle to that. Is that correct?

It is created in unmanaged memory right?

When is that memory released back to the system?


Is it simply that it can only handle 32bpp except that it can accept other
layouts as input?



Thanks for helping
 
Michael Phillips said:
The above always returns a 32bpp bitmap.

The code above doesn't return a bitmap at all and the OP didn't show us the
code used to load the bitmap into 'InBitmap'.
 
Herfried K. Wagner said:
The code above doesn't return a bitmap at all


Doesn't the above create a new GDI bitmap and return the handle to it?

I found the following which seems to support Philip's asertion (except I
guess the return is actually a handle to the bitmap). I think the point I
was missing is that it is a 32bpp bitmap not necessarily the same as
InBitmap


Bitmap.GetHbitmap () Creates a GDI bitmap object from this Bitmap.

and the OP didn't show us the code used to load the bitmap into 'InBitmap'.

Panel1.BackgroundImage = Image.FromFile(FilenameSaved)


Then call a sub with this for InBitmap
CType(Panel1.BackgroundImage, Bitmap)



What means OP anyway.
I guess the P is Poster but what is the O


Thanks for the reply
 
active said:
Doesn't the above create a new GDI bitmap and return the handle to it?

Yes.

'GdipCreateHBITMAPFromBitmap', which is called internally by 'GetHBitmap'
unfortunately isn't documented, so I cannot confirm what Michael answered.
Nevertheless, it might be true.
What means OP anyway.
I guess the P is Poster but what is the O

"Original Poster", the person, who started the thread.
 
Herfried K. Wagner said:
Yes.

'GdipCreateHBITMAPFromBitmap', which is called internally by 'GetHBitmap'
unfortunately isn't documented, so I cannot confirm what Michael answered.
Nevertheless, it might be true.

I don't know if this sheads any light on anything but GDI has:
Bitmap::GetHBITMAP Method that I'd guess InBitmap.GetHbitmap()
uses (which probbably calls GdipCreateHBITMAPFromBitmap.)
Do you think?


The GetHBITMAP method creates a Microsoft Windows Graphics Device Interface
(GDI) bitmap from this Bitmap object.


Thanks


But I'm geting way for my problem which is to put a 8bpp on the clipboard.

But trying to generate the arguments for CreateDIBSection I can't get past
GetObject right now because I don't know how to get a handle to my bitmap.

At least I think that's my problem.

You don't happen to know how do you?
 
I've been searching the Internet and can't find an example of anyone using
CreateDIBSection to create a handle for the clipboard. You don't happen to
know of anything do you?

As I understand it, if I put my 8bpp on the clipboard it will be converted
to 32bpp.

But if I create a DIBSection copy of the original and put a handle to that
on the clipboard there will then be an 8bpp on the clipboard. Right.

Thanks
 
active said:
I don't know if this sheads any light on anything but GDI has:
Bitmap::GetHBITMAP Method that I'd guess InBitmap.GetHbitmap()
uses (which probbably calls GdipCreateHBITMAPFromBitmap.)
Do you think?

That's another wrapper around 'GdipCreateHBITMAPFromBitmap', but the
documentation doesn't make more clear what is going on than the
documentation on 'Bitmap.GetHBitmap'.
The GetHBITMAP method creates a Microsoft Windows Graphics Device
Interface (GDI) bitmap from this Bitmap object.

Yes, but it's nowhere specified what number of bits per pixel is used for
the destination bitmap. However, it might be the case that the resulting
GDI bitmap is always a 32bpp bitmap.
But trying to generate the arguments for CreateDIBSection I can't get
past GetObject right now because I don't know how to get a handle to my
bitmap.

I am not sure what 'Wnd.GetObject' refers to in your post.
 
thanks

Herfried K. Wagner said:
That's another wrapper around 'GdipCreateHBITMAPFromBitmap', but the
documentation doesn't make more clear what is going on than the
documentation on 'Bitmap.GetHBitmap'.


Yes, but it's nowhere specified what number of bits per pixel is used for
the destination bitmap. However, it might be the case that the resulting
GDI bitmap is always a 32bpp bitmap.


I am not sure what 'Wnd.GetObject' refers to in your post.
 
The GetHBITMAP method creates a Microsoft Windows Graphics Device
Interface (GDI) bitmap from this Bitmap object.

The MSDN documentation may not explicitly state it but this method always
returns a HBITMAP that is optimized for the current display depth.

Most of the pc's have their display depth set to 32bpp, so the HBITMAP
returned is 32bpp.

For those using a 16bpp display the HBITMAP returned will be 16bpp.

When stepping through the code in a debugger with the gdiplus symbols,
one will see that Microsof't uses an algorithm similar if not identical to
that provided here:
http://support.microsoft.com/kb/230492
 
I've been searching the Internet and can't find an example of anyone using
CreateDIBSection to create a handle for the clipboard. You don't happen to
know of anything do you?

Here is the easiest most painless approach that I know.
It requires no knowledge of headers, image formats, GDI or even how the
clipboard works with images:
1) Use p-invoke CreateStreamOnHGlobal(NULL,...) to create a memory stream
backed by global allocated memory.
2) Use System.Drawing.Bitmap.Save to save to this stream.
3) Use p-invoke GetHGlobalFromStream to decouple the HGLOBAL handle from the
stream.
4) Use p-invoke SetClipboardData(CF_DIB, hGlobal) to place the DIB on the
clipboard.

The above works with any System.Drawing.Bitmap, System.Drawing.Image
regardless of the image's bit-depth.

You can place any image(i.e, GIF, TIFF, PNG, Bitmap) on the clipboard using
the above.

For the non bitmap images, you need to call RegisterClipboardFormat(e.g,
"PNG", "GIF", "TIFF") and use the returned CLIPFORMAT
for SetClipboardData.
 
Sound too good to be true.
But as always there seens to be a gotcha
1)Returns a ComTypes.IStream
and
2)Requires a System.IO.Stream

There doesn't seem to be any way of converting.

Is there around this problem?


Thanks
 
Here is vb code that demonstrates the concept:
Dim ms As New MemoryStream
' Create an 8bpp Indexed Bitmap
gifImage.Save(ms, ImageFormat.Bmp)

' Create a clipboard CF_DIB
ms.Seek(0, SeekOrigin.Begin)
ms.Seek(14, SeekOrigin.Begin)
Dim DIB(ms.Length - 15) As Byte
ms.Read(DIB, 0, ms.Length - 14)
' Strip the BITMAPFILEHEADER
Dim cfDIB As New MemoryStream(ms.Length - 14)
cfDIB.Write(DIB, 0, DIB.Length)
cfDIB.Seek(0, SeekOrigin.Begin)

' Place the CF_DIB on the clipboard
Dim dobj As New DataObject
dobj.SetData("DeviceIndependentBitmap", False, cfDIB)
Clipboard.SetDataObject(dobj)

You now have an 8bpp indexed bitmap on the clipboard as a DIB

This same technique can be use to place a "GIF" or any other image on the
clipboard.

For the GIF just eliminate the code for stripping the 14 bytes of the
BITMAPFILEHEADER
and use dobj.SetData("GIF", False, cfGIF).
 
I really appreciate the time you spend on this.
For me it is more than just getting it done,
more important is the understanding, so...

Why the ms.Seek(0, SeekOrigin.Begin)
Seems like something you do when not necessary
Is it considered good programming proctice to leave the stream rewound?
Or always rewind before usage?

What you've done is much like what I've done except shorter because I filled
an array that you avoided. But it seems that the reason you got an indexed
image and I didn't is because I used :
SetClipboardData(CF_DIB, hGlbData)
Is it the CF_DIB that caused a conversion to 32bpp?
I had also tried:
SetData(DataFormats.Bitmap, False, ...
Clipboard.SetDataObject(myData, True)
Do you think that is why I got the 32bpp or should I look elsewhere to
understand why that happened?

There is a System.Windows.Forms DataObject and a System.Windows.DataObject
and others also.
At first I thought I just lucked out with my Imports an got the one that
worked ( I have System.Windows.Forms but not System.Windows) but after
checking it looks like they both use the same implementation.
Is that your understanding?

But it is posible that some implementatiom might not be appriate fot this
task. Right?

But finally and most important to me is that I would have liked to
implemented your suggestion that began:
1) Use p-invoke CreateStreamOnHGlobal(NULL,...) to create a memory stream
backed by global allocated memory.
2) Use System.Drawing.Bitmap.Save to save to this stream.

But got hung up with
1)Returns a ComTypes.IStream
2)Requires a System.IO.Stream
and I couldn't find a way to do 2
Is that an approach that requires much code to implement?
It bugs me that I still don't know how to do thius.


Thanks again helping me learn so much!
 
Why the ms.Seek(0, SeekOrigin.Begin)
Seems like something you do when not necessary
Is it considered good programming practice to leave the stream rewound?
Or always rewind before usage?

Never make an assumption as to how another API function handles a stream.
If the stream is not rewound and an API function does not check to see if
the stream
pointer is at 0, then stream errors will occur.
What you've done is much like what I've done except shorter because I
filled an array that you avoided. But it seems that the reason you got an
indexed image and I didn't is because I used :
SetClipboardData(CF_DIB, hGlbData)
Is it the CF_DIB that caused a conversion to 32bpp?

No. The CF_DIB clipboard format requires a handle to a Global memory
representation
of a DIB. The DIB memory layout is:
BITMAPINFOHEADER
Color Table or Bitfield Array, if appropriate
DIB's bits

If you do not create a DIB memory layout for an indexed DIB then
you won't place one on the clipboard. No conversion to 32bpp takes place.
There is a System.Windows.Forms DataObject and a System.Windows.DataObject
and others also.
At first I thought I just lucked out with my Imports an got the one that
worked ( I have System.Windows.Forms but not System.Windows) but after
checking it looks like they both use the same implementation.
Is that your understanding?

A DataObject was required for my code snippet to work. A DataObject from
any valid namespace will do.
But finally and most important to me is that I would have liked to
implemented your suggestion that began:
1) Use p-invoke CreateStreamOnHGlobal(NULL,...) to create a memory stream
backed by global allocated memory.
2) Use System.Drawing.Bitmap.Save to save to this stream.

But got hung up with
1)Returns a ComTypes.IStream
2)Requires a System.IO.Stream
and I couldn't find a way to do 2
Is that an approach that requires much code to implement?
It bugs me that I still don't know how to do thius.

The net MemoryStream object is equivalent to the Native Windows Api
"CreateStreamOnHGlobal.
Both implement the features of an IStream interface backed by Window's
Global Memory.

There are many ways to get the job done.
You can either use the net common language features which sometimes wrap the
Window's Native API.
If not, you can use the Window's Native API.

Using the Window's Native API is more work.
The pitfalls are many.
You need to take into account managed COM versus unmanaged COM.
Managed memory versus unmanaged memory.
Marshaling back and forth.

I do not recommend reinventing the wheel.
Use the vb.net features and use the Windows Native API
when vb.net cannot be used to implement your design features.
 
Thanks for all the help

I'm still trying to understand some things you did.

For example to get a palette from the clipboard you used:
GetObject(HPal, Marshal.SizeOf(GetType(UShort)), NEntries)

If NEntries > 0 Then

Lp = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(PALETTEENTRY)) * NEntries)

End If

NRetrieved = GetPaletteEntries(HPal, 0, NEntries, Lp)

GetObject returns "A WORD count of the number of entries in the logical
palette" in NEntries.

But in one case I ran it returned zero.
So you didn't allocate memory but went ahead and did GetPaletteEntries which
returned a nonzero number(I forget the value) for NRetrieved


GetPaletteEntries
If the function succeeds and the handle to the logical palette is a valid
pointer (not NULL), the return value is the number of entries retrieved from
the logical palette. If the function succeeds and handle to the logical
palette is NULL, the return value is the number of entries in the given
palette.

You had checked HPal so it was not NULL.

If non-zero entries were retrieved where did they go?
Why were any retrieved when NEntries (in) was zero?
Why would you even call GetPaletteEntries if NEntries is zero?

I think experience is the answer, but it's a mystery to me.


thanks again
 
I did not do any extensive error checking.

I simply wanted to demonstrate how to retrieve a palette from the clipboard.

The number of entries in the palette is needed to allocate sufficient memory
to Marshal back the palette entries.

GetPaletteEntries will only retrieve the actual number of entries in the
palette. It is possible to fix the allocated buffer for 256 colors.

Obviously, if the palette contains less than 256 colors then the buffer will
only contain the number of entries that are actually in the palette.
 
My confusion is that NEntries returned 0 but NRetrieved was >0
I believe that NRetrieved was correct.
Do they measure different things?

Or do you think NRetrieved was incorrect?

Take note of the fact that NEntries was zero at the call
NRetrieved = GetPaletteEntries(HPal, 0, NEntries, Lp)


Thanks
 
My confusion is that NEntries returned 0 but NRetrieved was >0
I believe that NRetrieved was correct.
Do they measure different things?

The MSDN documentation states that a non zero return indicates success.
Or do you think NRetrieved was incorrect?

If NRetrieved returned all of the colors in the palette that you were
looking for, then it was correct..
Take note of the fact that NEntries was zero at the call
NRetrieved = GetPaletteEntries(HPal, 0, NEntries, Lp)

NEntries should be any positive number less than the total possible colors
for the indexed bitmap(e.g., 1..255, for 8bpp)

It does not make any sense to request 0 entries

As I stated, I did not do extensive error checking.
 
Back
Top