Solved

MFC: How to Write() and Read() a CImage into a file?

Posted on 2010-11-24
16
1,203 Views
Last Modified: 2012-05-10
Dear experts,

I have an MFC application. I use VS 2008.
Could you please give me an example how I can write an image to a binary file and read from it?
I would like to use CFile's Write() and Read() methods, and probably CImage.

Thanks.
0
Comment
Question by:tantormedia
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 7
16 Comments
 
LVL 5

Expert Comment

by:Xper4net
ID: 34205079
You don't have to use CFile. CImage::Save and CImage::Load do the job.

For example :

CImage myImg ;

   myImg.Load(_T("c:\\temp\MyPicture.jpg")) ;

CImage myNewImg ;

   myNewImg.Attach(hBmp) ; // Attach a bitmap currently displayed somewhere
   myNewImage.Save() ;
   m_myNewImage.Save(_T("c:\\temp\\mynewimg.bmp")) ;
   myNewImg.Detach() ;


0
 
LVL 5

Expert Comment

by:Xper4net
ID: 34205086
Oops, I called Save() twice... of course first call is wrong.
0
 

Author Comment

by:tantormedia
ID: 34205096
I am afraid, that I have to use CFile, because CImage is not the only thing I am saving, my file contains a lot of various data. So the call to Write() my image is only one of many other Write() calls.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 5

Expert Comment

by:Xper4net
ID: 34205183
So, in this case, instead of CFile, maybe you could use IStream versions of Save and Load. But I'm not sure that it could work (in fact, I'm almost sure that it couldn't).
Then, my advice would be to to use separate files, and an external tool (like dzip/dunzip) to gather them in an unique file.
> In OnOpenDocument, you unzip the file in a temporary folder.
> During work, you populate/update this temprary folder.
> In OnSaveDocument, you zip the temporary folder's content.
I've use this solution in many projects, it works very fine.
Let me know if you need more details.
0
 

Author Comment

by:tantormedia
ID: 34205208
Thank you for your answer.
I cannot replace CFile with something else, as it would require too much change in a huge project.
Could you give me an example on your next suggestion?
What I need can be simplified in this way: An image in some form is stored among other things using CFile's Write() method.
On using Read(), the image is read into memory from where it can be used to be displayed in a View.
Thanks.
0
 
LVL 5

Accepted Solution

by:
Xper4net earned 500 total points
ID: 34206864
So, I guess that zip files solution doesn't meet your requirements.

Then, what you could do is the following:

//---------------  Saving part

CImage myNewImg ;

   myNewImg.Attach(hBmp) ; // Attach a bitmap currently displayed somewhere
   m_myNewImage.Save(_T("c:\\temp\\mynewimg.bmp")) ;

// Let assume that you already have your main file myMainFile opened in writing mode 

   // Don't forget to flush it before asking for its current position
   myMainFile.Flush() ;
   ULONGLONG lActual myMainFile.GetPosition() ; // This information must be stored somewhere, for further image's retrieving

   CFile myImgFile(_T("c:\\temp\\mynewimg.bmp"), CFile::modeRead | CFile::shareDenyNone);
   
   ULONGLONG  lSize, lStart = 0, lEnd = myImgFile.GetLength() ;
   const UINT uiBufSize = 2048 ; // Increase it improve performance, half of images' average size is a good value.
   char chBuf[uiBufSize] ; 
   while (lStart < lEnd )
   {
       lSize = __min((lEnd - lStart), (ULONGLONG)uiBufSize) ;
       myMainFile.Write(chBuf, myImgFile.Read(chBuf, lSize)) ;
       lStart += lSize ;
   }
   // As lActual, lImgSize (= lEnd) must be stored somewhere

Open in new window


//------------------------ Loading part

CImage myNewImg ;

   myNewImg.Attach(hBmp) ; // Attach a bitmap currently displayed somewhere
   m_myNewImage.Save(_T("c:\\temp\\mynewimg.bmp")) ;

  // Let assume that you already have your main file myMainFile opened in reading mode, and that you've retrieved lActual and lImgSize 

   // Don't forget to flush it before asking for its current position
   myMainFile.Seek(lActual, CFile::begin) ;

    CFileException e;
    CFile myImgFile ;
    TCHAR* pszFileName = _T("c:\\temp\\mynewimg.bmp");
    if(! myImgFile Open(pszFileName, CFile::modeCreate | CFile::modeWrite, &e))
   {
     TRACE(_T("File could not be opened %d\n"), e.m_cause);
   }
  
   ULONGLONG  lSize, lStart = lActual, lEnd = lActual+lImgSize   ;
   const UINT uiBufSize = 2048 ; // Increase it improve performance, half of images' average size is a good value.
   char chBuf[uiBufSize] ; 
   while (lStart < lEnd )
   {
       lSize = __min((lEnd - lStart), (ULONGLONG)uiBufSize) ;
       myImgFile.Write(chBuf, myMainFile.Read(chBuf, lSize)) ;
       lStart += lSize ;
   }

Open in new window



0
 

Author Comment

by:tantormedia
ID: 34206930
Thank you for your answer.
I have some questions.
- "Attach a bitmap currently displayed somewhere" - I don't have such a bitmap. All I have is a path to my image file. Can I just use Load() instead?
- I seems like in your code, we read and write data byte by byte. Isn't it possible first to read the whole data into a buffer and then write it at once into the main file?
- The loading part of your code is identical to the saving part.
0
 
LVL 5

Expert Comment

by:Xper4net
ID: 34206951
In second code view, first line must be removed, and the following added at the end :

myImgFile.Close() ;
m_myNewImage.Load(_T("c:\\temp\\mynewimg.bmp")) ;
0
 
LVL 5

Expert Comment

by:Xper4net
ID: 34206984
> "currently displayed somewhere" figures "An image in some form" in your requirement. If your image is already in a file, you don't need CImage for the saving part.
> You've read the code too quickly ;) it uses a buffer (initialized a 2048, but you can increase it).
0
 

Author Comment

by:tantormedia
ID: 34207289
May I ask why we cannot use a buffer of the size returned by myImgFile.GetLength()?
0
 
LVL 5

Expert Comment

by:Xper4net
ID: 34210562
You could, but with large images (> 1 Mo), it could lead to a long time process for memory allocation request taking  (system needs to allocate in one continuous block, and if memory is fragmented, it needs to de-fragment it before).
To optimize, you can do the following :

uiBufSize = _min((UINT)myImgFile.GetLength(), 0x100000) ; // Allocates for file length if < 1 Mo, or at least 1 Mo
char chBuf = new char[uiBufSize] ;
...

// don't forget buffer deletion after process
delete [] chBuf ;
0
 

Author Comment

by:tantormedia
ID: 34229657
Thank you for your help. Something goes wrong though. I am trying to do it simply by allocating the whole memory at once so far:

            CFile fTemp(ImpDlg.GetPathName(), CFile::modeRead | CFile::shareDenyNone);

            ULONGLONG size = fTemp.GetLength();

            BYTE* buf = new BYTE[size];

            int TotSize = 4 + sizeof(ULONGLONG) + size;

            pPara->m_WaveLoc = pc->AllocFileBlock(TotSize);
            cf->Seek(pPara->m_WaveLoc,CFile::begin);

            cf->Write("PNG ",4);
            cf->Write(&size, sizeof(size));
            cf->Write(buf, fTemp.Read(buf, size)) ;

            fTemp.Close();

Now I am reading the file thus:

                  if(pPara->m_pImg != NULL)
                  {
                        delete(pPara->m_pImg);
                  }

                  cf->Seek(pPara->m_WaveLoc,CFile::begin);
                  char tp[4];
                  cf->Read(tp,4);
                  //ASSERT(!strncmp(tp,"PNG ",4));
                  if(!strncmp(tp,"PNG ",4))
                  {
                        ULONGLONG s;
                        cf->Read(&s,sizeof(s));
                        BYTE * pData = new BYTE[s];
                        cf->Read(pData, s);

                        CFile fTemp("temp.png", CFile::modeCreate|CFile::modeReadWrite);
                        fTemp.Write(pData, s);
                        pPara->m_pImg = new CImage();
                        HRESULT res = pPara->m_pImg->Load("C:\\BookProgram\\Book\\temp.png");
                        fTemp.Close();
                  }

res is E_FAIL, though temp.png is created, and I can open it and see the right image. Why cannot I load it?
Thanks.
0
 
LVL 5

Assisted Solution

by:Xper4net
Xper4net earned 500 total points
ID: 34229945
Your temporary file must be close BEFORE any attempt to load it :

                        fTemp.Write(pData, s);
                        fTemp.Close();
                        pPara->m_pImg = new CImage();
                        HRESULT res = pPara->m_pImg->Load("C:\\BookProgram\\Book\\temp.png");

It's because file's buffer is not flushed until you've called Close or Flush method.



0
 

Author Comment

by:tantormedia
ID: 34229972
Thank you very much, now it worked.
Could you please explain me what you wrote in your previous answer:
To optimize, you can do the following :

uiBufSize = _min((UINT)myImgFile.GetLength(), 0x100000) ; // Allocates for file length if < 1 Mo, or at least 1 Mo
char chBuf = new char[uiBufSize] ;

Here we allocate a buffer for the first 1 MB, but what happens with the rest? Should I go in a loop as you showed before?
Thanks.
0
 
LVL 5

Expert Comment

by:Xper4net
ID: 34230035
Yes, you're right.
For example, if image size is 2.3 Mo, there will be three read/write loops (1 + 1 + 0.3).
As I said, this kind of approach is faster (and more secured) than a great allocation.
0
 

Author Closing Comment

by:tantormedia
ID: 34230056
Thank you very much.
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

635 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question