Faceless
Faceless

Reputation: 19

Serialize: CArchive a CImage

I am currently trying to figure out how to properly store a CImage file within a CArchive (JPEG). My current approach to this is the following (pseudo) code:

BOOL CPicture::Serialize(CArchive &ar)
{
  IStream *pStream = NULL;  
  HRESULT hr;
  CImage *img = GetImage();

  if (ar.IsLoading())
  {
    HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, 54262);
    hr = CreateStreamOnHGlobal(hMem, FALSE, &pStream);
    if(SUCCEEDED(hr))
    {
      ar.Read(pStream, 54262);
      img->Load(pStream);
      pStream->Release();
      GlobalUnlock(hMem);
      GlobalFree(hMem);
    }
  }
  else
  {   
    hr = CreateStreamOnHGlobal(0, TRUE, &pStream);
    if (SUCCEEDED(hr))
    {
      hr = img->Save(pStream, Gdiplus::ImageFormatJPEG);
      if (SUCCEEDED(hr))
      ar.Write(pStream, 54262);
    }
  }

...

I am just now getting back into C++ and have only done a little with it in the past. Any help would be very much appreciated.

Thank you very much in advance.

Upvotes: 1

Views: 599

Answers (1)

Bill Heitler
Bill Heitler

Reputation: 199

I'm not an expert on IStream, but I think you may not be using it correctly. The following code seems to work. It currently archives in png format, but it can easily be changed through Gdiplus::ImageFormatPNG. It owes a lot to the tutorial "Embracing IStream as just a stream of bytes" by S. Arman on CodeProject.

void ImageArchive(CImage *pImage, CArchive &ar)
{
    HRESULT hr;
    if (ar.IsStoring())
    {
// create a stream
        IStream *pStream = SHCreateMemStream(NULL, 0);
        ASSERT(pStream != NULL);
        if (pStream == NULL)
            return;

// write the image to a stream rather than file (the stream in this case is just a chunk of memory automatically allocated by the stream itself)
        pImage->Save(pStream, Gdiplus::ImageFormatPNG); // Note: IStream will automatically grow up as necessary.

// find the size of memory written (i.e. the image file size)
        STATSTG statsg;
        hr = pStream->Stat(&statsg, STATFLAG_NONAME);
        ASSERT(hr == S_OK);
        ASSERT(statsg.cbSize.QuadPart < ULONG_MAX);
        ULONG nImgBytes = ULONG(statsg.cbSize.QuadPart);    // any image that can be displayed had better not have more than MAX_ULONG bytes

// go to the start of the stream
        LARGE_INTEGER seekPos;
        seekPos.QuadPart = 0;
        hr = pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
        ASSERT(hr == S_OK);

// get data in stream into a standard byte array
        char *bytes = new char[nImgBytes];
        ULONG nRead;
        hr = pStream->Read(bytes, nImgBytes, &nRead);   // read the data from the stream into normal memory.  nRead should be equal to statsg.cbSize.QuadPart.
        ASSERT(hr == S_OK);
        ASSERT(nImgBytes == nRead);

// write data to archive and finish
        ar << nRead;    // need to save the amount of memory needed for the file, since we will need to read this amount later
        ar.Write(bytes, nRead);     // write the data to the archive file from the stream memory
        pStream->Release();
        delete[] bytes;
    }
    else
    {
// get the data from the archive
        ULONG nBytes;
        ar >> nBytes;
        char *bytes = new char[nBytes]; // ordinary memory to hold data from archive file
        UINT nBytesRead = ar.Read(bytes, nBytes);   // read the data from the archive file
        ASSERT(nBytesRead == UINT(nBytes));

// make the stream
        IStream *pStream = SHCreateMemStream(NULL, 0);
        ASSERT(pStream != NULL);
        if (pStream == NULL)
            return;

// put the archive data into the stream
        ULONG nBytesWritten;
        pStream->Write(bytes, nBytes, &nBytesWritten);
        ASSERT(nBytes == nBytesWritten);
        if (nBytes != nBytesWritten)
            return;

// go to the start of the stream
        LARGE_INTEGER seekPos;
        seekPos.QuadPart = 0;
        hr = pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
        ASSERT(hr == S_OK);

// load the stream into CImage and finish
        pImage->Load(pStream);  // pass the archive data to CImage
        pStream->Release();
        delete[] bytes;
    }
}

Upvotes: 2

Related Questions