Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the size of jpeg from memory (converted using GDI++)

This is my first post here. I have a problem. I need to take a sceenshot of the desktop, convert it to jpeg, store it in a buffer and then manipulate it and send it over the internet.

I have written the code for doing this with GetDC....and GDI+ for converting the HBITMAP to jpeg. The problem I am having now is that I don't know the size of the jpeg that has been saved into the IStream. Here is part of the code that transforms the bitmap referenced by the HBITMAP hBackBitmap into jpeg and save it into pStream. I need to know how many bytes have been written into pStream and how I can use pStream (get a PVOID handle):

Gdiplus::Bitmap bitmap(hBackBitmap, NULL);///loading the HBITMAP
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED, nBlockSize) ;//allocating memory, the size of the current bitmap size. i'm over allocating but i don't think there is any way to get the exact ammount I need to allocate, is there?
if(!hGlobal)
    return;
IStream* pStream = NULL ;
if(CreateStreamOnHGlobal(hGlobal, TRUE, &pStream) != S_OK )
    return;
bitmap.Save(pStream, &clsid);

What I need is: 1. Find out the size of jpeg, how many bytes have been written in the stream 2. How to use the stream. Can I get a PVOID for the data in stream for example?

Thank you.

like image 454
Nemok Avatar asked Jul 07 '09 07:07

Nemok


2 Answers

According to the CreateStreamOnHGlobal documentation, your use of it is incorrent. Quote:

The current contents of the memory block are undisturbed by the creation of the new stream object. Thus, you can use this function to open an existing stream in memory. The initial size of the stream is the size of the memory handle returned by the GlobalSize function.

Therefore, you should replace nBlockSize with 0 to allocate an zero-sized buffer. As the memory buffer must be moveable, you'll also need to replace GMEM_FIXED with GMEM_MOVEABLE:

HGLOBAL gGlobal = GlobalAlloc(GMEM_MOVEABLE, 0);

After saving to the stream, the resulting size will be available as

size_t size = GlobalSize(hGlobal);

To access the JPEG encoded data, you'll need to use GlobalLock to get a pointer to the actual location in memory.

Note that Global and Local functions are marked as deprecated and should not be used anymore, but I don't know a better IStream implementation for your needs without crawling the MSDN documentation. Perhaps somebody else can help here!?

like image 188
Bluehorn Avatar answered Oct 15 '22 04:10

Bluehorn


OK I found the solution to this problem here: http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/6dfc2e62-e2d1-4be3-a93b-a7d97d3f8469

I will also put it here for future reference. To find out the size that has been written to the stream one can use the method Seek of the stream. To get access to the buffer you can use Read.

   // Calculate reasonably safe buffer size
   int stride = 4 * ((image.GetWidth() + 3) / 4);
  size_t safeSize = stride * image.GetHeight() * 4 + sizeof(BITMAPINFOHEADER) +    sizeof(BITMAPFILEHEADER) + 256 * sizeof(RGBQUAD);
  HGLOBAL mem = GlobalAlloc(GHND, safeSize);
  assert(mem);

  // Create stream and save bitmap
  IStream* stream = 0;
  hr = CreateStreamOnHGlobal(mem, TRUE, &stream);
  assert(hr == S_OK);
  hr = image.Save(stream, Gdiplus::ImageFormatBMP);
  assert(hr == S_OK);

  // Allocate buffer for saved image
  LARGE_INTEGER seekPos = {0};
  ULARGE_INTEGER imageSize;
  hr = stream->Seek(seekPos, STREAM_SEEK_CUR, &imageSize);
  assert(hr == S_OK && imageSize.HighPart == 0);
  BYTE* buffer = new BYTE[imageSize.LowPart];

  // Fill buffer from stream
   hr = stream->Seek(seekPos, STREAM_SEEK_SET, 0);
   assert(hr == S_OK);
   hr = stream->Read(buffer, imageSize.LowPart, 0);
   assert(hr == S_OK);


   // Cleanup
  stream->Release();
  delete[] buffer;
  return 0;
like image 2
Nemok Avatar answered Oct 15 '22 06:10

Nemok