Saving Bitmap image data to HGLOBAL [modified]
-
alleyes wrote:
The Bitmap images in memory use the System::Drawing::Bitmap classes not the GDI+ version.
System.Drawing IS the managed version of GDI+. It's using GDI+ in its implementation. So your images ARE GDI+ images. Why do you need to use an HGLOBAL in a managed application? If you need old school GDI BITMAPINFOHEADER stuff you can certainly construct that info yourself from info in your GDI+ System.Drawing.Bitmap objects. For example, to extract 24bpp image data from a System.Drawing.Bitmap:
using namespace System::Drawing;
using namespace System::Drawing::Imaging;Bitmap ^bmp = ...;
Rectangle rect = Rectangle(0, 0, bmp->Width, bmp->Height);
BitmapData ^bmpData = bmp->LockBits(rect, ImageLockMode::Read, PixelFormat::Format24bppRgb);BITMAPINFOHEADER bmiHeader;
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = bmp->Width;
bmiHeader.biHeight = bmp->Height;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 24;
bmiHeader.biCompression = BI_RGB;
bmiHeader.biSizeImage = abs(bmpData->Stride) * bmpData->Height;
bmiHeader.biXPelsPerMeter = 0;
bmiHeader.biYPelsPerMeter = 0;
bmiHeader.biClrUsed = 0;
bmiHeader.biClrImportant = 0;bmp->UnlockBits(bmpData);
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
I have ONE store of heap memory with a handle to it - HGLOBAL. What I am not articulating adequately is that instead of creating a new memory block, I would like to use a common one if that's at all possible with what I described. Is that possible using the LockBits method? Like I said previously, I have one block of heap space and assign that to one image from a picture box. I have actually two picturebox objects on my form and want to the ability to copy the image from the second picturebox to the main heap store that was allocated. This is what I feel is the disconnect.
The HGLOBAL is just a handle to an allocated block of memory (a BYTE array). You get a pointer to that block of memory with GlobalLock(). You can copy whatever you want to it. It's up to you to make sure that block is large enough to hold what you write to it.
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
I know that I can create from scratch the BITMAPINFOHEADER. Is it a matter of copying the information to the top of the Bitmap array?
alleyes wrote:
Is it a matter of copying the information to the top of the Bitmap array?
If that's where you want it, yes. You could also start with a pointer to where you want it so you don't have to copy afterwards... BITMAPINFOHEADER *pbmiHeader = (BITMAPINFOHEADER *)GlobalLock(myhglobal); pbmiHeader->biSize = sizeof(BITMAPINFOHEADER); etc...
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
The HGLOBAL is just a handle to an allocated block of memory (a BYTE array). You get a pointer to that block of memory with GlobalLock(). You can copy whatever you want to it. It's up to you to make sure that block is large enough to hold what you write to it.
Mark Salsbery Microsoft MVP - Visual C++ :java:
Mark Salsbery wrote:
You get a pointer to that block of memory with GlobalLock().
That's what I meant by pointer but mis-construed it a bit. To summarize then. I can copy the bitmap data to this HGLOBAL using managed Bitmap classes? I do have the advantage of BITMAPINFOHEADER being available from the image that is to be copied from so I have most of that information which happens to be common to both the images in question If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.
-
alleyes wrote:
Is it a matter of copying the information to the top of the Bitmap array?
If that's where you want it, yes. You could also start with a pointer to where you want it so you don't have to copy afterwards... BITMAPINFOHEADER *pbmiHeader = (BITMAPINFOHEADER *)GlobalLock(myhglobal); pbmiHeader->biSize = sizeof(BITMAPINFOHEADER); etc...
Mark Salsbery Microsoft MVP - Visual C++ :java:
Mark Salsbery wrote:
If that's where you want it, yes.
Well actually the file header will be at top but I get it.
Mark Salsbery wrote:
BITMAPINFOHEADER *pbmiHeader = (BITMAPINFOHEADER *)GlobalLock(myhglobal); pbmiHeader->biSize = sizeof(BITMAPINFOHEADER);
That's helpful...
-
Mark Salsbery wrote:
You get a pointer to that block of memory with GlobalLock().
That's what I meant by pointer but mis-construed it a bit. To summarize then. I can copy the bitmap data to this HGLOBAL using managed Bitmap classes? I do have the advantage of BITMAPINFOHEADER being available from the image that is to be copied from so I have most of that information which happens to be common to both the images in question If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.
alleyes wrote:
I can copy the bitmap data to this HGLOBAL using managed Bitmap classes?
You can't directly copy from managed to unmanaged memory. You can use Marshal::Copy though. You can also wrap the pointer in an UnmanagedMemoryStream object so you can use any managed functionality that uses a Stream object. For example, you could write a BMP file from a Bitmap to memory in one line of code instead of building the headers manually.
alleyes wrote:
If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.
You use the pointer while it's locked.
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
alleyes wrote:
I can copy the bitmap data to this HGLOBAL using managed Bitmap classes?
You can't directly copy from managed to unmanaged memory. You can use Marshal::Copy though. You can also wrap the pointer in an UnmanagedMemoryStream object so you can use any managed functionality that uses a Stream object. For example, you could write a BMP file from a Bitmap to memory in one line of code instead of building the headers manually.
alleyes wrote:
If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.
You use the pointer while it's locked.
Mark Salsbery Microsoft MVP - Visual C++ :java:
Mark Salsbery wrote:
You can't directly copy from managed to unmanaged memory. You can use Marshal::Copy though
I fully intended using the Marshal class for copying.
Mark Salsbery wrote:
You can also wrap the pointer in an UnmanagedMemoryStream object so you can use any managed functionality that uses a Stream object.
Now you threw me a bit. One of the things I was considering was saving the image to a managed memory stream object. Why consider unmanaged? By calling the Save method and then specifying BMP as the image type, I do get the BITMAPHEADERINFO. This is what I'm building upon without doing the Save to Stream technique:
System::Drawing::Rectangle imgRect = System::Drawing::Rectangle(0, 0, MyImage->Width, MyImage->Height); System::Drawing::Imaging::BitmapData^ bmpData = MyImage->LockBits(imgRect, System::Drawing::Imaging::ImageLockMode::ReadOnly, System::Drawing::Imaging::PixelFormat::Format32bppRgb); int byteCount = bmpData->Stride \* MyImage->Height; array<Byte>^ bmpBytes = gcnew array<Byte>(byteCount); Marshal::Copy(bmpData->Scan0, bmpBytes, 0, byteCount); MyImage->UnlockBits(bmpData);
Once I have the image in an array shouldn't I be copying it to the HGLOBAL? Or should the destination be the HGLOBAL and forget about the array?
modified on Wednesday, January 20, 2010 2:42 PM
-
alleyes wrote:
I can copy the bitmap data to this HGLOBAL using managed Bitmap classes?
You can't directly copy from managed to unmanaged memory. You can use Marshal::Copy though. You can also wrap the pointer in an UnmanagedMemoryStream object so you can use any managed functionality that uses a Stream object. For example, you could write a BMP file from a Bitmap to memory in one line of code instead of building the headers manually.
alleyes wrote:
If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.
You use the pointer while it's locked.
Mark Salsbery Microsoft MVP - Visual C++ :java:
I think I have the solution:
MemoryStream^ ms = gcnew MemoryStream();
MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
array<Byte>^ bits = gcnew array<Byte>(MyHeader->biSizeImage);
bits = ms->GetBuffer();
ms->Close();
Marshal::Copy(bits, 0, static_cast<IntPtr>(MyHGlobal), bits->Length);Where: MyHeader->biSizeImage is the member from the BITMAPINFOHEADER MyHGlobal is the handle to the locked memory block. I then throw an AccessViolationException at the copy call. I think that perhaps the array size may be bigger than the memory block. I appreciate your help so far - I'm close!
-
I think I have the solution:
MemoryStream^ ms = gcnew MemoryStream();
MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
array<Byte>^ bits = gcnew array<Byte>(MyHeader->biSizeImage);
bits = ms->GetBuffer();
ms->Close();
Marshal::Copy(bits, 0, static_cast<IntPtr>(MyHGlobal), bits->Length);Where: MyHeader->biSizeImage is the member from the BITMAPINFOHEADER MyHGlobal is the handle to the locked memory block. I then throw an AccessViolationException at the copy call. I think that perhaps the array size may be bigger than the memory block. I appreciate your help so far - I'm close!
I said UnmanagedMemoryStream, not MemoryStream. And you don't cast an HGLOBAL to a pointer - GlobalLock returns the pointer. And Bitmap::Save() saves everything - BITMAPFILEHEADER, BITMAPINFO, and the pixel bits. You're trying to make it harder than it is :)
void *pMyGlobalMemory = GlobalLock(MyHGlobal);
UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream(IntPtr(pMyGlobalMemory), GlobalSize(MyHGlobal));
MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
GlobalUnlock(MyHGlobal);Mark Salsbery Microsoft MVP - Visual C++ :java:
-
I said UnmanagedMemoryStream, not MemoryStream. And you don't cast an HGLOBAL to a pointer - GlobalLock returns the pointer. And Bitmap::Save() saves everything - BITMAPFILEHEADER, BITMAPINFO, and the pixel bits. You're trying to make it harder than it is :)
void *pMyGlobalMemory = GlobalLock(MyHGlobal);
UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream(IntPtr(pMyGlobalMemory), GlobalSize(MyHGlobal));
MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
GlobalUnlock(MyHGlobal);Mark Salsbery Microsoft MVP - Visual C++ :java:
I get an error when trying that: Error C2664: 'System::IO::UnmanagedMemoryStream::UnmanagedMemoryStream(unsigned char *,__int64)' : cannot convert parameter 1 from 'System::IntPtr' to 'unsigned char *' I didn't consider the UnmanagedMemoryStream
Mark Salsbery wrote:
You're trying to make it harder than it is Smile
Yeah it seems that way :doh:
-
I get an error when trying that: Error C2664: 'System::IO::UnmanagedMemoryStream::UnmanagedMemoryStream(unsigned char *,__int64)' : cannot convert parameter 1 from 'System::IntPtr' to 'unsigned char *' I didn't consider the UnmanagedMemoryStream
Mark Salsbery wrote:
You're trying to make it harder than it is Smile
Yeah it seems that way :doh:
Oops wrong constructor :)
unsigned char *pMyGlobalMemory = (unsigned char *)GlobalLock(MyHGlobal);
UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream(pMyGlobalMemory, GlobalSize(MyHGlobal));Note you're still resonsible for making sure the allocated global memory is large enough to write to. GlobalSize() will give you the allocated size, but if you don't know how many bytes will be written, you'll need to write to a growable memory stream first, check how many bytes were written, compare it to the globally allocated size, and reallocate if you need more room.
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
I said UnmanagedMemoryStream, not MemoryStream. And you don't cast an HGLOBAL to a pointer - GlobalLock returns the pointer. And Bitmap::Save() saves everything - BITMAPFILEHEADER, BITMAPINFO, and the pixel bits. You're trying to make it harder than it is :)
void *pMyGlobalMemory = GlobalLock(MyHGlobal);
UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream(IntPtr(pMyGlobalMemory), GlobalSize(MyHGlobal));
MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
GlobalUnlock(MyHGlobal);Mark Salsbery Microsoft MVP - Visual C++ :java:
I made a small change: void* pMyGlobal = GlobalLock(MyHGlobal); UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream((unsigned char*)(pMyGlobal), GlobalSize(gPtrImage)); MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp); It builds but then I get ExternalException was unhandled in GDI+
-
I made a small change: void* pMyGlobal = GlobalLock(MyHGlobal); UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream((unsigned char*)(pMyGlobal), GlobalSize(gPtrImage)); MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp); It builds but then I get ExternalException was unhandled in GDI+
alleyes wrote:
It builds but then I get ExternalException was unhandled in GDI+
I can't debug that for you from here, but I suspect not enough allocated on the HGLOBAL. So maybe something like this:
MemoryStream^ ms = gcnew MemoryStream(); MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp); if (GlobalSize(MyHGlobal) < ms->Length) { GlobalRealloc(MyHGlobal, ms->Length, 0); } void \*pMyGlobalMemory = GlobalLock(MyHGlobal); UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((unsigned char \*)pMyGlobalMemory, GlobalSize(MyHGlobal)); ms->WriteTo(ums); GlobalUnlock(MyHGlobal);
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
alleyes wrote:
It builds but then I get ExternalException was unhandled in GDI+
I can't debug that for you from here, but I suspect not enough allocated on the HGLOBAL. So maybe something like this:
MemoryStream^ ms = gcnew MemoryStream(); MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp); if (GlobalSize(MyHGlobal) < ms->Length) { GlobalRealloc(MyHGlobal, ms->Length, 0); } void \*pMyGlobalMemory = GlobalLock(MyHGlobal); UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((unsigned char \*)pMyGlobalMemory, GlobalSize(MyHGlobal)); ms->WriteTo(ums); GlobalUnlock(MyHGlobal);
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
alleyes wrote:
It builds but then I get ExternalException was unhandled in GDI+
I can't debug that for you from here, but I suspect not enough allocated on the HGLOBAL. So maybe something like this:
MemoryStream^ ms = gcnew MemoryStream(); MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp); if (GlobalSize(MyHGlobal) < ms->Length) { GlobalRealloc(MyHGlobal, ms->Length, 0); } void \*pMyGlobalMemory = GlobalLock(MyHGlobal); UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((unsigned char \*)pMyGlobalMemory, GlobalSize(MyHGlobal)); ms->WriteTo(ums); GlobalUnlock(MyHGlobal);
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
NotSupportedException unhandled. Stream does not support writing? Since when? :confused:
Sorry try another constructor: ...gcnew UnmanagedMemoryStream((unsigned char *)pMyGlobalMemory, GlobalSize(MyHGlobal), GlobalSize(MyHGlobal), FileAccess.ReadWrite);
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Sorry try another constructor: ...gcnew UnmanagedMemoryStream((unsigned char *)pMyGlobalMemory, GlobalSize(MyHGlobal), GlobalSize(MyHGlobal), FileAccess.ReadWrite);
Mark Salsbery Microsoft MVP - Visual C++ :java:
Ugh! Still getting an AccessViolation Exception. That can either mean not enough memory available or is that this memory can't be written to. I see the CanWrite, CanSeek flags are TRUE. Capacity and Length are both the same - 1658934
modified on Thursday, January 21, 2010 9:22 AM
-
Ugh! Still getting an AccessViolation Exception. That can either mean not enough memory available or is that this memory can't be written to. I see the CanWrite, CanSeek flags are TRUE. Capacity and Length are both the same - 1658934
modified on Thursday, January 21, 2010 9:22 AM
Did you use FileAccess.ReadWrite? I'm only guessing here since I don't know what line the exception occurs on...
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Did you use FileAccess.ReadWrite? I'm only guessing here since I don't know what line the exception occurs on...
Mark Salsbery Microsoft MVP - Visual C++ :java:
Hi, This is what I have so far:
MemoryStream^ ms = gcnew MemoryStream();
MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
ms->Seek(0, SeekOrigin::Begin);if (GlobalSize(MyGlobal) < ms->Length)
{
GlobalReAlloc(MyGlobal, (LONG)ms->Length, 0);
}void* pMyGlobal = (UCHAR*)MyGlobal;
UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((UCHAR*)pMyGlobal, GlobalSize(MyGlobal), GlobalSize(MyGlobal), FileAccess::ReadWrite);
ums->Position = 0;
ms->WriteTo(ums);It occurs on the line: ms->WriteTo(ums); This is even without setting the Position of the streams. I did this because just before the Call to WriteTo, the position was set to Length.
-
Hi, This is what I have so far:
MemoryStream^ ms = gcnew MemoryStream();
MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
ms->Seek(0, SeekOrigin::Begin);if (GlobalSize(MyGlobal) < ms->Length)
{
GlobalReAlloc(MyGlobal, (LONG)ms->Length, 0);
}void* pMyGlobal = (UCHAR*)MyGlobal;
UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((UCHAR*)pMyGlobal, GlobalSize(MyGlobal), GlobalSize(MyGlobal), FileAccess::ReadWrite);
ums->Position = 0;
ms->WriteTo(ums);It occurs on the line: ms->WriteTo(ums); This is even without setting the Position of the streams. I did this because just before the Call to WriteTo, the position was set to Length.
Where did this come from? void* pMyGlobal = (UCHAR*)MyGlobal; Casting a handle to a pointer is bad. For an HGLOBAL it's bad unless you know for sure GMEM_FIXED was used to allocate the memory. Much safer to use GlobalLock to get the pointer.
Mark Salsbery Microsoft MVP - Visual C++ :java: