|
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++
|
|
|
|
|
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
|
|
|
|
|
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++
|
|
|
|
|
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
|
|
|
|
|
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++
|
|
|
|
|
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++
|
|
|
|
|
Mark Salsbery wrote: I can't debug that for you from here
No of course not.
I was suspecting perhaps an image already in memory was the issue and maybe that needed a dispose method called, not sure.
Thanks for the re-size tip
|
|
|
|
|
NotSupportedException unhandled.
Stream does not support writing?
Since when?
|
|
|
|
|
Sorry try another constructor:
...gcnew UnmanagedMemoryStream((unsigned char *)pMyGlobalMemory, GlobalSize(MyHGlobal), GlobalSize(MyHGlobal), FileAccess.ReadWrite);
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
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++
|
|
|
|
|
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++
|
|
|
|
|
Sorry I threw you there.
The lock is already done earlier during the creating of the original images. Didn't make sense to call another GlobalLock on it again. The call to GlobalAlloc is GlobalAlloc(GMEM_MOVEABLE, Size)
You have to call GlobalLock on the existing memory again?
|
|
|
|
|
alleyes wrote: The lock is already done earlier during the creating of the original images. Didn't make sense to call another GlobalLock on it again. The call to GlobalAlloc is GlobalAlloc(GMEM_MOVEABLE, Size)
Then you need to use the pointer returned by
the GlobalLock() call.
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
alleyes wrote: Didn't make sense to call another GlobalLock on it again.
There's nothing wrong with locking again as long as you have
an equal number of unlock calls.
GlobalRealloc on a locked handle can only reallocate
memory in the same place so has more chance of failing,
since there may not be enough room in that place.
For best results the handle should be unlocked before
reallocating then locked again when you need the pointer.
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Sincere thanks Mark
When I have the time, I think I'll do all this in managed code by Marshalling the native HGLOBAL. But for now it works well.
|
|
|
|
|
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++
|
|
|
|
|
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...
|
|
|
|
|
After searching on the internet, the possible solution that come out is by using TabPage.DrawToBitmap, put the bitmap in a PictureBox, set the position of PictureBox to the same location of TabPage, and fade it. The code is below :
System::Void tabControl1_SelectedIndexChanged(System::Object^ sender, System::EventArgs^ e)
{
alphaBlend = 250;
repaint = true;
TabPictureBox->Visible = true;
int posX = tabControl1->Location.X + tabControl1->TabPages[tabControl1->SelectedIndex]->Margin.Left + 1;
int posY = tabControl1->Location.Y + tabControl1->ItemSize.Height + 1 +
tabControl1->TabPages[tabControl1->SelectedIndex]->Margin.Top;
TabPictureBox->Location = System::Drawing::Point(posX, posY);
TabPictureBox->Size = tabControl1->TabPages[tabControl1->SelectedIndex]->Size;
TabBitmap = gcnew Bitmap(tabControl1->TabPages[tabControl1->SelectedIndex]->Width,
tabControl1->TabPages[tabControl1->SelectedIndex]->Height);
tabControl1->TabPages[tabControl1->SelectedIndex]->DrawToBitmap(TabBitmap,
Rectangle(0, 0, tabControl1->TabPages[tabControl1->SelectedIndex]->Width,
tabControl1->TabPages[tabControl1->SelectedIndex]->Height));
while (alphaBlend > 0)
{
alphaBlend = alphaBlend - 25;
TabPictureBox->Refresh();
}
TabPictureBox->Visible = false;
repaint = false;
}
System::Void TabPictureBox_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e)
{
if (repaint)
{
if (TabBitmap != nullptr)
{
Bitmap^ temp = gcnew Bitmap(TabBitmap);
Graphics^ bitmapGraphics = Graphics::FromImage(temp);
if (alphaBlend < 0) alphaBlend = 0;
SolidBrush^ greyBrush = gcnew SolidBrush(Color::FromArgb(alphaBlend, Color::White));
bitmapGraphics->CompositingMode = CompositingMode::SourceOver;
bitmapGraphics->FillRectangle(greyBrush, Rectangle(0, 0, TabBitmap->Width, TabBitmap->Height));
e->Graphics->CompositingQuality = CompositingQuality::GammaCorrected;
e->Graphics->DrawImage(temp, 0, 0);
}
}
}
The problem is that each time I select the tabPage, my CPU usage spike to 70 - 80% and the fading effect is kind of slow. Is there any solution to fix it so that I can draw the bitmap faster and with minimum CPU usage (less than 50%)?
Thanks.
|
|
|
|
|
I need a program in PHP which can extract the hidden biders from ebay.I will have to insert the item number and the program must show me all bidders of the auction and their emails.
Also the same program must extract the registered contact information for any specified bidder specially the email address
I will pay for this
If someone can help please contact me at totos_back@yahoo.com
Thanks
|
|
|
|
|
In an app that is used to capture images, the information used to store the image data is referenced by a pointer to HGLOBAL. That is then used to re-create the image so it can be written to disk with a call to GlobalFree.
I have two problems:
I need to attach a file header so I can write the image to a file.
The image data is of native type DWORD so I cant use the .NET classes without massaging the image data.
What I have done thus far:
BITMAPINFOHEADER* imgHeader = (BITMAPINFOHEADER*)GlobalLock(gPtrImage);
array<BYTE>^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage);
if (!imgHeader)
{
GlobalFree(gPtrImage);
return;
}
BITMAPFILEHEADER* bfh = new BITMAPFILEHEADER();
bfh->bfType = 0x4d42; //BM
bfh->bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + imgHeader->biSize + imgHeader->biSizeImage);
bfh->bfReserved1 = 0;
bfh->bfReserved2 = 0;
bfh->bfOffBits = (DWORD) (bfh->bfSize - imgHeader->biSizeImage);
LPDWORD Pixels = (LPDWORD)((LPBYTE)imgHeader + sizeof(BITMAPINFOHEADER));
Marshal::Copy(static_cast<IntPtr>(bfh), imgArray, 0, sizeof(BITMAPFILEHEADER));
Marshal::Copy(static_cast<IntPtr>(Pixels), imgArray, (sizeof(BITMAPFILEHEADER) + 1), imgHeader->biSizeImage);
GlobalUnlock(gPtrImage);
System::Environment::SpecialFolder saveFolder = System::Environment::SpecialFolder::MyDocuments;
SaveFileDialog^ sfd = gcnew SaveFileDialog();
sfd->InitialDirectory = System::Environment::GetFolderPath(saveFolder);
sfd->Filter = "Device Independent Bitmaps |*.bmp|All files|*.*";
if (sfd->ShowDialog() == System::Windows::Forms::DialogResult::OK)
{
System::IO::Stream^ imgFileStream = sfd->OpenFile();
if (imgFileStream != nullptr)
{
imgFileStream->Write(imgArray, 0, imgArray->Length);
imgFileStream->Close();
}
}
GlobalFree(gPtrImage);
gPtrImage = NULL;
The file size is a lot larger than I would expect for a 640*480 32 bit BMP and it can't be displayed.
The image is fine when written to a picturebox, the data is then unlocked but NOT freed. As a test, I save the image of the picturebox i.e PictureBox->Image and that is fine. The data needs to to be handed off to another application that requires native code so I need to re-create the image from the pointer to HGLOBAL and that's where it falls down
What might be wrong with this?
Thanks for any help
|
|
|
|