C Q&A: Displaying a JPG in your MFC Application

来源:百度文库 编辑:神马文学网 时间:2024/04/30 05:27:59






















HomeTopicsColumnsDownloadsSubscribeRSS  C++ Q&A;: Displaying a JPG in your MFC Applicat...

We were unable to locate this content in zh-cn.

Here is the same content in en-us.

C++ Q&A;: Displaying a JPG in your MFC Application Displaying a JPG in your MFC Application Paul DiLascia Download the code for this article: CQA0110.exe (202KB)
Browse the code for this article at Code Center: ImgView

Q In Visual Basic, I can display a JPG or GIF file by creating a picture control, but how can I display a JPG in my MFC app?
Many Readers
A Good question! Sometimes programmers who use Visual Basic® seem to have it so easy. Just plop a picture control in your form and awaaay you go...while C++ programmers have to fret and strut. What are we supposed to do, write our own JPG decompression functions?
      Of course not! In fact, C/C++ programmers can use the very same (almost) picture control their Visual Basic counterparts use. I kid you not. The Visual Basic picture control is based on a system COM class, IPicture (see Figure 1). IPicture manages a picture object and its properties. Picture objects provide an abstraction for bitmaps. Windows® provides a standard implementation that knows how to handle BMP, JPG, and GIF bitmaps. All you need to do is instantiate IPicture and call Render. Instead of calling CoCreateInstance the normal way, you call a special function called OleLoadPicture.
IStream* pstm = // needs a stream                                        IPicture* pIPicture;                                        hr = OleLoadPicture(pstm, 0, FALSE,                                        IID_IPicture, (void**)&pIPicture);                                        
OleLoadPicture loads the picture from the stream and creates a new IPicture object you can use to display the picture.
rc = // rect to display in                                        // convert rc to HIMETRIC                                        spIPicture->Render(pDC, rc);                                        
      IPicture does all the nasties to figure out whether the image is a Windows bitmap, JPEG, or GIF file—it even does icons and metafiles! Naturally, the details are a little tricky, so I wrote a little demo program called ImgView (see Figure 2) to put it all together.


Figure 2 ImgView

ImgView is a typical MFC doc/view app that uses a class I wrote, CPicture (see Figure 3), to encapsulate IPicture. CPicture maps cumbersome COM-style parameters into types more friendly to MFC programmers. For example, CPicture lets you load a picture directly from a file name, CFile, or CArchive, instead of dealing with streams; and CPicture::Render does all the yucky HIMETRIC coordinate conversions that IPicture wants—so you don't have to. CPicture even has a Load function to load an image from your resource data, so all you have to do to display a resource picture is write:
   CPicture pic(ID_MYPIC); // load pic                                        CRect rc(0,0,0,0);      // use default rc                                        pic.Render(pDC, rc);    // display it                                        
      What could be easier? CPicture::Render takes a rectangle, the one you want to display your picture in. IPicture stretches the image appropriately. If you pass an empty rect, CPicture uses the image's native size—no stretching. As for the image itself, CPicture looks for a resource type called "IMAGE", so you have to code your RC file like so:
   IDR_MYPIC IMAGE MOVEABLE PURE "res\\MyPic.jpg"                                        
      Overall, CPicture is pretty brainless. It holds an ATL CComQIPtr smart pointer to the IPicture interface, which the various Load functions initialize by calling OleLoadPicture. CPicture provides the usual wrapper functions to call the underlying IPicture. CPicture encapsulates only those IPicture methods I needed to write ImgView; this being because I am such a lazy programmer. If you ever need to call IPicture::get_Handle or some other rare IPicture method, you'll have to add the wrapper yourself, sorry. At least the code is trivial.
      By the way, I should point out that after writing CPicture, I discovered a little-known MFC class called CPictureHolder that does much of the same thing. You can find it in afxctl.h.
      As I mentioned earlier, ImgView is a typical MFC doc/view application with classes CPictureDoc and CPictureView for doc and view. Figure 4 shows the view. CPictureDoc is trivial; it uses CPicture to hold the picture—
class CPictureDoc : public CDocument {                                        protected:                                        CPicture m_pict; // the picture                                        };                                        
and CPictureDoc::Serialize calls CPicture::Load to read the picture from the archive MFC sets up.
void CPictureDoc::Serialize(CArchive& ar)                                        {                                        if (ar.IsLoading()) {                                        m_pict.Load(ar);                                        }                                        }                                        
      Just for fun, CPictureDoc::OnNewDocument loads a pretty NASA photo from the program's resource data. To display the pictures, CPictureView::OnDraw calls CPicture::Render.
void CPictureView::OnDraw(CDC* pDC)                                        {                                        CPictureDoc* pDoc = GetDocument();                                        CPicture* ppic = pDoc->GetPicture();                                        CRect rc;                                        GetImageRect(rc);                                        ppic->Render(pDC,rc);                                        }                                        
GetImageRect is a CPictureView function that retrieves the proper image rectangle, depending on the current ImgView zoom ratio. (ImgView lets you view the image at 25, 33, 50, 75, or 100 percent, or "to fit".) GetImageRect calls CPicture::GetImageSize to get the true image size, then scales appropriately.
      The rest of CPictureView is typical CScrollView stuff, with code to initialize the view and set scroll sizes, handle commands, and so on. The only funny business with IPicture is that, as I suggested earlier, IPicture::Render likes its coordinates in HIMETRIC units, whereas vanilla MFC apps use the default MM_TEXT mapping mode. Not to worry, CPicture::Render and CPicture::GetImageSize do the magic conversions, so you don't have to overtax your poor noggin worrying about such mundane and weary trivia.
      CPictureView has one message handler worth mentioning: OnEraseBkgnd. This is required to draw the blank area in case the image is smaller than the view's client area (see Figure 5). OnEraseBkgnd creates a clipping rectangle equal to the image rectangle, then fills the client rectangle with black. Clipping avoids flicker when you size the window—FillRect doesn't draw inside the clipped rectangle. This is standard Windows Graphics 101.


Figure 5 OnEraseBkgnd Fills the Clipped Image

      IPicture/CPicture really makes displaying images easy. It even takes care of palette realization and all that horrible stuff. You can throw away all your old DIB-drawing code that loads palettes, BitBlts, StretchBlts, and so on—IPicture is the way to go. If you're not using IPicture to display images, start doing so now!
      Well, all that was so easy I decided to write another class just for fun. CPictureView is fine and dandy if you want to write an image viewer, but what if you want to add a picture to a dialog or some other window? For that, I wrote another class, CPictureCtrl (see Figure 6). CPictureCtrl lets you plop a picture in any dialog or window, as a child control. For example,
class CAboutDialog : public CDialog {                                        protected:                                        CPictureCtrl m_wndPict;                                        virtual BOOL OnInitDialog();                                        };                                        BOOL CAboutDialog::OnInitDialog()                                        {                                        m_wndPict.SubclassDlgItem(IDC_MYIMAGE,this);                                        return CDialog::OnInitDialog();                                        }                                        
      This assumes you have a static control in your dialog, with ID=IDC_IMAGE and an IMAGE resource with the same ID. I derived CPictureCtrl from my ubiquitous CStaticLink, so you can specify a URL hyperlink if you like (or just create a string resource with the same ID as the control and picture). If you specify a URL, clicking the mouse on the picture will launch your browser to that link. Amazing. CPicture holds a CPicture object and overrides WM_PAINT to call CPicture::Render instead of the normal static control thing. For more details, download the source from the link at the top of this article—and use it with my blessing!

Send questions and comments for Paul to cppqa@microsoft.com. Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++(Addison-Wesley, 1992). Paul can be reached at askpd@pobox.com or http://www.dilascia.com.

From the October 2001 issue of MSDN Magazine.

 

 

Current Issue

 

 

Browse All MSDN Magazines

 

Tip: Click the printer button in your browser toolbar to get the printer friendly version of this article.

 

MSDN Magazine Blog

Gone With The Week (Mar 15 – 19, 2010)
Gone With the Week (Mar 8 – 12, 2010)
Gone With The Week (Mar 1 – 5, 2010)
Writing for MSDN Magazine: A Primer
A Tentative Content Plan for 2010

 

个人信息中心|法律信息|MSDN Flash 中心|联 系我们 © 2010 Microsoft Corporation 版权所有。 保留所有权利 | 商 标 | 隐私权声明