Solved

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

Posted on 2010-11-24
16
1,018 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
  • 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
 
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
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
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

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
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.

708 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now