Solved

To display a huge image... how to do it?

Posted on 2002-06-16
10
307 Views
Last Modified: 2013-11-20
I want to display a huge image (usually over 1G), I cannot load it to memory because of its huge size. The only way is to display a portion of the image, then pan it. Do you have any idea about how to to make it run well? Can I use "FileMapping"? Thanks a lot for your opinion, I appreciate it.
0
Comment
Question by:VCFan
  • 5
  • 5
10 Comments
 
LVL 49

Expert Comment

by:DanRollins
ID: 7083124
Yes, it appears that you can use FileMapping.  It  involves using CreateFileMapping and MapViewOfFile and there is a trick having to do with forcing the dibsection data to be aligned on a DWORD boundary.  

Alas, I can't find a pat answer for you.  Perhaps someone else can locate some sample code.  If you up the points, I'll take a whack at it.

-- Dan
0
 

Author Comment

by:VCFan
ID: 7083159
Sure, I am waiting for your further help. :)
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7084493
OK Here's a FIRST DRAFT -- it is for testing purposes ONLY:

THere is a big problem that I'm not certain how to attack:  The image area (the bits) do not normally end up on a DWORD-aligned boundary -- and that is a requirement for using the CreateDIBSection API which seems to be the logical way to attack this problem.

As a proof-of-concept, I just added an offset so that the bits are aligned (I had to make the images shorter to avoid a related error).  The result is that I can access the data, but it is color-shifted (RGB RGB RGB is interpetted as GBR GBR GBR).  

Note: I woked on a 2MB file (the largest I could find!) and the girl looks like one of those Intel 'bluemen' lol.

This code shows how to do the file mapping and so forth and actually displays a specified portion of a large bitmap.  I'd like you to try it and single-step through the code. and examine all variables ate each step.  Perhaps you can see something different.

Do you have access to the program that creates these whopping GB+ files?  Is there anything on on earth that can read them?  If you have access to the program, you could add a couple of filler bytes ate one place in the file and the work would be done.

Otherwise, I'll need to either shift the entire file forward by 2 bytes (probably not feasible) or do manual operations of copying subsets of the data into a temporary buffer.  That much coding will take me hours...

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void GetLastErrorStr( CString& sDest );
HBITMAP GetImgSubSet( HBITMAP hbmSrc, RECT* prcSrc, BITMAPINFO* pBMI, void* pBits ) ;

void CD06Dlg::OnButton1()
{
      HANDLE            hFile;
      HANDLE            hFileMapping;
      BITMAPFILEHEADER* pBMFH;
      BITMAPINFO*       pBMI;    // bfOffBits is not divisible by 4.
      BITMAPINFOHEADER* pBMIH;

      hFile=        CreateFile( "C:\\temp\\large.bmp",GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,NULL);
      hFileMapping= CreateFileMapping( hFile,NULL,PAGE_READWRITE,0,0,0 );

      CString s;
      GetLastErrorStr( s );

      pBMFH= (BITMAPFILEHEADER*)MapViewOfFile( hFileMapping,FILE_MAP_ALL_ACCESS,0,0,0);  // entire file
      pBMI=  (BITMAPINFO*)     (pBMFH+1);

      GetLastErrorStr( s );

      DWORD dwOffset= pBMFH->bfOffBits;

      if ( (dwOffset & 3 ) != 0 ) {
            MessageBox("Problem mapping: bit data not dword-aligned!" );
            int nAdjust= dwOffset % 4;  // usually 2
            dwOffset += nAdjust;
            pBMI->bmiHeader.biHeight--; // discared last scanline for this test
      }

      HDC dc= CreateCompatibleDC(NULL);

      void* pBits=    0;

      HBITMAP hBmp= CreateDIBSection(dc,pBMI,DIB_RGB_COLORS, &pBits, hFileMapping, dwOffset );
      if (hBmp == 0 ) {
            MessageBox("CreateDIBSection failed!" );
      }
      GetLastErrorStr( s );  // useless

      pBMIH= (BITMAPINFOHEADER*)pBMI;
      int nWidth= pBMIH->biWidth;
      int nHigh=  pBMIH->biHeight;

      RECT rc= {100,100,400,400 };
      static HBITMAP hBmpNew= GetImgSubSet( hBmp, &rc, pBMI, pBits) ;

      m_ctlBmpShow.SetBitmap( hBmpNew );

      DeleteDC(dc);
      CloseHandle( hFile ); // or just exit the testing program
}

HBITMAP GetImgSubSet( HBITMAP hbmSrc, RECT* prcSrc, BITMAPINFO* zpBMI, void* pBits )
{
      HWND hWnd= AfxGetMainWnd()->m_hWnd;
      int nHigh= prcSrc->bottom - prcSrc->top;
      int nWide= prcSrc->right  - prcSrc->left;

      //---------------------------- create the destination bitmap
      HDC hDCMem= ::CreateCompatibleDC( NULL );
      HBITMAP hBmp= NULL;
      HDC hDC= ::GetDC( hWnd );
      hBmp= CreateCompatibleBitmap( hDC, nWide, nHigh );
      ::ReleaseDC( hWnd, hDC );

      HGDIOBJ hOldBmp= SelectObject( hDCMem, hBmp );

      BITMAP rBmp;
      GetObject( hbmSrc, sizeof(BITMAP), &rBmp );

      DIBSECTION rDS;
      GetObject( hbmSrc, sizeof(DIBSECTION), &rDS );


      BITMAPINFO* pBMI= (BITMAPINFO*)&rDS.dsBmih;
      
      int nScansCopied=
            StretchDIBits( hDCMem,
            0,0,                       // dest location
            nWide, nHigh,              // dest size
            prcSrc->left, prcSrc->top, // src Location
            nWide, nHigh,              // src size
            pBits,
            pBMI,
            DIB_RGB_COLORS,
            SRCCOPY
      );

      ::SelectObject( hDCMem, hOldBmp );
      ::DeleteObject( hDCMem );

      return( hBmp );
}
0
 

Author Comment

by:VCFan
ID: 7084858
Hello Dan, thanks a lot for your help. I tried your code, some of which is ingenious, but it failed at "MapViewOfFile" cuz I used a whopping image file (over 900M) which made it memory insufficient. How could I get it over?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7085427
On second thought, this may not work.  I can't find a single reference about how to handle this.

How are most of these bitmaps laid out?  Very wide and not too tall?   Is there someplace I could download one for testing?

If the monster BMP file is certain to be a DIB with standard 24-bit color, it is possible to just open the file and read in carefully-selected chunks of it to form a DIBSection in RAM.  From that, I could extract a specified rectangle and return an HBITMAP of it.

If you bump the points again, I'll take a crack at that.

-- Dan
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

Author Comment

by:VCFan
ID: 7085957
Okay, I think it's tough while interesting, it's worth a study :)
0
 
LVL 49

Accepted Solution

by:
DanRollins earned 300 total points
ID: 7094930
This version processes the image data by seeking around in the file -- it never tries to load the entire file at once.

You can make this a lot faster in several ways:

1) No need to create a new DIBSection each time.  Just create it once and pass it into the (modified) GetImgSubset() fn.

2) Create a larger 'cache' DIB (say 2000x1000) then pan around in that (use BitBlit orStretchDiBits to copy part of it to the screen) until you get near the edge, then read the file for another chunk.

3) When the viewer has panned to the edge, there is no need to re-read the whole cache image.  For instance, when the user is near the right side of the 'cache' move the image data to the right and fill in the 'hole' with data from the file.  As the user keeps moving, new data is there for viewing.

4) Updating the cache DIB could be done in a worker thread.  You could keep track of how close the user is coming to the edge and be ready with a new cach DIB when it is needed (so the user does not need to wait).


//------------------------------------------------------
HBITMAP GetImgSubset( CFile& cfBmp, CRect& rc )
{
                      //  only needs to be sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO)
     BYTE buf[1000];  //  but handy for eyeball check
     cfBmp.Seek(0, CFile::begin);
     cfBmp.Read(buf, 1000); // Read header info TBD: check error

     BITMAPFILEHEADER* pBMFH= (BITMAPFILEHEADER*)buf;
     BITMAPINFO*       pBMI=  (BITMAPINFO*)     (pBMFH+1);    

     DWORD nBitsFileOffset= pBMFH->bfOffBits;  // first RGB (x,y)=(0,0) is here

     int nSrcWide= pBMI->bmiHeader.biWidth;
     int nSrcHigh= pBMI->bmiHeader.biHeight;

     int nSrcBytesPerLine= ((nSrcWide * 3)+3) & 0xfffffffc;  // divible by 4

     int nDstWide= rc.Width();
     int nDstHigh= rc.Height();

     pBMI->bmiHeader.biHeight= -nDstHigh;  // fixes upside-down-ness
     pBMI->bmiHeader.biWidth=  nDstWide;

     //---------------------------------- create the destination bitmap
     void* pBits=    0;
     HWND    hWnd=   AfxGetMainWnd()->m_hWnd;
     HDC     hDC=    ::GetDC( hWnd );
     HBITMAP hbmRet= CreateDIBSection( hDC, pBMI,DIB_RGB_COLORS, &pBits, 0,0 );
     ::ReleaseDC( hWnd, hDC );

     if ( hbmRet == 0 ) {
          AfxMessageBox("CreateDIBSection failed!" );
     }
     //----------------------------
     DIBSECTION rDS;
     GetObject( hbmRet, sizeof(DIBSECTION), &rDS );
     
     int nDstBytesPerLine= rDS.dsBm.bmWidthBytes;

     BYTE* pDstBits= (BYTE*)pBits;
     for (int y= rc.top; y<rc.bottom; y++ ) {
          int j= (nSrcHigh-1) - y;   // fixes upside-down-ness
          int nFileOffset= nBitsFileOffset + (j*nSrcBytesPerLine);

          nFileOffset += rc.left * 3;

          cfBmp.Seek( nFileOffset, CFile::begin);
          cfBmp.Read( pDstBits, nDstBytesPerLine );
          pDstBits += nDstBytesPerLine;
     }
     return( hbmRet );
}


CRect   m_rc;
CFile   m_cfBmp;
HBITMAP m_hBmp;

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void CBigBmpDlg::OnButton1()
{
     m_cfBmp.Open( "C:\\temp\\large.bmp", CFile::modeRead );
     m_rc= CRect(0,0, 500,500 );
     m_hBmp= GetImgSubset( m_cfBmp, m_rc );
     m_ctlBmpShow.SetBitmap( m_hBmp );
}

void CBigBmpDlg::OnRight()
{
     m_rc.OffsetRect(10,0);

     ::DeleteObject(m_hBmp);
     m_hBmp= GetImgSubset( m_cfBmp, m_rc );
     m_ctlBmpShow.SetBitmap( m_hBmp );
}

void CBigBmpDlg::OnDown()
{
     m_rc.OffsetRect(0,10);

     ::DeleteObject(m_hBmp);
     m_hBmp= GetImgSubset( m_cfBmp, m_rc );
     m_ctlBmpShow.SetBitmap( m_hBmp );
}
0
 

Author Comment

by:VCFan
ID: 7098998
Dan, thank you for your hard working, I appreciate it. I think your answer meet my requirement basically, I will improve it by myself. Thanks. Chances are I will have more questions in the future, please give me your Email address in order to let you know that I have new questions about a display of big image. :) Thanks again!
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7099248
I worked many hours to help you for free.  I solved a problem that no other expert would touch.  A problem that is considered impossible to solve.  Then you give me a B!
 
>>please give me your Email address in order to let you know that I have new questions

This has made me laugh quite hard.  My side hurts.

-- Dan
0
 

Author Comment

by:VCFan
ID: 7099697
I am so sorry, Dan. I don't know much of this website and I must give you an "A" if I can choose again.

I will take my words back. Sorry
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Windows Drag & Drop Location 2 85
Process filename extension 3 156
how to monitor remote shell execution on linux 9 93
Making an alias 7 54
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

760 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

24 Experts available now in Live!

Get 1:1 Help Now