Click here to Skip to main content
Click here to Skip to main content
Go to top

Rendering GIF, JPEG, Icon, or Bitmap Files with OleLoadPicture

, 26 Sep 2000
Rate this:
Please Sign up or sign in to vote.
Easy way to do basic rendering of image files into a view window
<!-- Article image -->

Sample Image - render.gif

<!-- Main HTML starts here -->

Introduction

This article describes a simple way to do basic rendering of various types of image files into a view window.

It uses a rendering method described in the MSDN article Q218972, LOADPIC.EXE.

I ran across this sample application purely by accident. After reviewing it, I re-wrote the guts of the functionality for use in an MFC document-view environment.

The re-write consists of three functions:

  • DrawImage(), which draws the image contained in an image file.
  • GetImageSize(), which returns the height and width of the image in the image file.
  • LoadPictureFile(), which loads the image from disk and readies it for display via OleLoadPicture().

Header Definitions:

BOOL DrawImage(CDC*    pDC,         //Device context
               CString csFile,      //Pathname to image file
               CPoint  ptCenter,    //Center point for image
               HWND    hWnd,        //Handle to view window
               CRect   rectImage);  //Display area rectangle

DrawImage() will render the image whose pathname is passed in in csFile into the area of the rectangle in rectImage.

rectImage must be in device units.

Important: These functions assume a DC mapping mode other than MM_TEXT. That is, negative Y is downward.

If you are going to use MM_TEXT, you will need to change the signs of the height variables in the Render() function call.

BOOL GetImageSize(CDC*    pDC,       //Device context
                  LPCTSTR szFile,    //Pathname to image file
                  int*    pnHeight,  //Height to return
                  int*    pnWidth);  //Width to return

GetImageSize() returns the height and width of the image in szFile. These values are in device units for the DC passed in to the function.

It is good to use this function to fetch the true size of the image, in case you want to let the user reset the image display.

BOOL LoadPictureFile(LPCTSTR    szFile,      //Image file pathname
                     LPPICTURE* pgpPicture); //Ptr to PICTURE for image

LoadPictureFile() loads the image bytes in szFile into pgpPicture.

Includes:

The LOADPIC project has a reference to olectl.h in its stdafx.h file.

However, I am using these functions in an MFC application with no special includes.

MSDN does not mention any need for OleLoadPicture.

Source:

BOOL DrawImage(CDC*    pDC,
               CString csFile,
               CPoint  ptCenter,
               HWND    hWnd,
               CRect   rectImage)
{
    if (pDC == NULL || csFile.IsEmpty() || hWnd == NULL)
        return FALSE;
    
    LPPICTURE gpPicture = NULL;
        
    if (LoadPictureFile((LPCTSTR)csFile,&gpPicture))
    {
        //Get width and height of picture
        long hmWidth  = 0;
        long hmHeight = 0;

        gpPicture->get_Width (&hmWidth);
        gpPicture->get_Height(&hmHeight);

        //Use to get height and width for display
        CRect rectI = rectImage;
        rectI.NormalizeRect();

        int nWidth = rectI.Width();
        int nHeight= rectI.Height();

        CPoint ptUL(ptCenter.x-(nWidth /2),
                    ptCenter.y+(nHeight/2));

		RECT rc;
		GetClientRect(hWnd, &rc);

        HRESULT hrP = NULL;
        
        hrP =
        gpPicture->Render(pDC->m_hDC,
                          ptUL.x,
                          ptUL.y,
                          nWidth,
                          -nHeight,
                          0,
                          hmHeight,
                          hmWidth,
                          -hmHeight,
                          &rc);

        gpPicture->Release();

        if (SUCCEEDED(hrP))
            return TRUE;
    }

    return FALSE;
}

BOOL GetImageSize(CDC*       pDC,
                  LPCTSTR    szFile,
                  int*       pnHeight,
                  int*       pnWidth)
{
    LPPICTURE gpPicture = NULL;

    if (!LoadPictureFile(szFile,&gpPicture))
        return FALSE;

    //Get width and height of picture
    long hmWidth  = 0;
    long hmHeight = 0;

    gpPicture->get_Width (&hmWidth);
    gpPicture->get_Height(&hmHeight);

    int nPixX = pDC->GetDeviceCaps(LOGPIXELSX);
    int nPixY = pDC->GetDeviceCaps(LOGPIXELSY);

    //For printers, pixels per inch is really DPI, so be careful!
    if (pDC->IsPrinting())
    {
		nPixX = 96;
        nPixY = 96;
    }

    //Convert to device units
    *pnWidth  = MulDiv(hmWidth,  nPixX, HIMETRIC_INCH);
    *pnHeight = MulDiv(hmHeight, nPixY, HIMETRIC_INCH);

    return TRUE;
}

BOOL LoadPictureFile(LPCTSTR    szFile,
                     LPPICTURE* pgpPicture)
{
    // open file
    HANDLE hFile = CreateFile(szFile,
                              GENERIC_READ,
                              0,
                              NULL,
                              OPEN_EXISTING,
                              0,
                              NULL);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        AfxMessageBox ("Could not read file");
        return FALSE;
    }

    // get file size
    DWORD dwFileSize = GetFileSize(hFile, NULL);

    if (dwFileSize == (DWORD)-1)
    {
        CloseHandle(hFile);
        AfxMessageBox ("File seems to be empty");
        return FALSE;
    }

    LPVOID pvData = NULL;

    // alloc memory based on file size
    HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);

    if (hGlobal == NULL)
    {
        CloseHandle(hFile);
        AfxMessageBox ("Could not allocate memory for image");
        return FALSE;
    }

    pvData = GlobalLock(hGlobal);

    if (pvData == NULL)
    {
        GlobalUnlock(hGlobal);
        CloseHandle(hFile);
        AfxMessageBox ("Could not lock memory for image");
        return FALSE;
    }

    DWORD dwBytesRead = 0;

    // read file and store in global memory
    BOOL bRead = ReadFile(hFile,
                          pvData,
                          dwFileSize,
                          &dwBytesRead,
                          NULL);

    GlobalUnlock(hGlobal);
    CloseHandle(hFile);

    if (!bRead)
    {
        AfxMessageBox ("Could not read file");
        return FALSE;
    }

    LPSTREAM pstm = NULL;

    // create IStream* from global memory
    HRESULT hr = CreateStreamOnHGlobal(hGlobal,
                                       TRUE,
                                       &pstm);

    if (!(SUCCEEDED(hr)))
    {
        AfxMessageBox ("CreateStreamOnHGlobal() failed");

        if (pstm != NULL)
            pstm->Release();
            
        return FALSE;
    }

    else if (pstm == NULL)
    {
        AfxMessageBox ("CreateStreamOnHGlobal() failed");
        return FALSE;
    }

	// Create IPicture from image file
	if (*pgpPicture)
		(*pgpPicture)->Release();

    hr = ::OleLoadPicture(pstm,
                          dwFileSize,
                          FALSE,
                          IID_IPicture,
                          (LPVOID *)&(*pgpPicture));

    if (!(SUCCEEDED(hr)))
    {
    	pstm->Release();
        AfxMessageBox("Could not load image (hr failure)");
        return FALSE;
    }

    else if (*pgpPicture == NULL)
    {
    	pstm->Release();
        AfxMessageBox("Could not load image (pgpPicture failure)");
        return FALSE;
    }

    pstm->Release();

    return TRUE;  //Made it ...!
}

Example of Use:

The picture above shows use of these functions in a map-making program I have been working on.

Here is how the program uses them:

Document Class:

First a handler for the "Map" menu allows a user to navigate to the desired image file and open it. This simply saves off the file path for use later, and sets the program into "rendering images" mode: Wherever the user clicks, a stroke "image" object will be created to display the image.

The CStroke class handles this (remember Scribble?).

View Class:

Once the program is in "rendering images" mode, the view class handles the left mouse button click. Here is the code block from OnLButtonDown():

void CGameMaprView::OnLButtonDown(UINT nFlags, CPoint point) 
{
    CGameMaprDoc* pDoc = GetDocument();
    CRect rectTemp;

    CClientDC dc(this);
    OnPrepareDC(&dc);
    dc.DPtoLP(&point);

    ...


    //---------------------------------------
    else if (pDoc->DrawType() == DRAW_IMAGE_FILE)
    {
        m_pStrokeCur = pDoc->NewStroke(FALSE,
                                       &dc,
                                       point.x,
                                       point.y,
                                       pDoc->BitmapResourceName()); //Pathname to image file

        if (m_pStrokeCur != NULL)
        {
            m_pStrokeCur->DrawStroke(&dc,FALSE,this);

            //Invalidate for re-display
            pDoc->RedrawArea(m_pStrokeCur->m_rectBounding);
        }
    }

    ...

    return;
}

Document Class (again):

Back in the document class, NewStroke() creates a new stroke, and gets the original image size for the initial bounding box:

CStroke* CGameMaprDoc::NewStroke(BOOL bPasting,
                                 CDC  *pDC,
                                 int  nX,
                                 int  nY,
                                 CString csBmp) //Image file in this case...
{

    ...


    else if (m_eDrawType == DRAW_IMAGE_FILE)
    {
        eType = S_TYPE_IMAGE_FILE;
    }

    pStrokeItem = new CStroke( ... );

	...

    //Do bitmap-related processing
    else if (m_eDrawType == DRAW_IMAGE_FILE &&
             !csBmp.IsEmpty())
    {
        int nH = 0;
        int nW = 0;

        //Set the original size of the image
        if (GetImageSize(pDC,(LPCTSTR)csBmp,&nH,&nW))
        {            
            pStrokeItem->m_csBitmapResource = csBmp;
            pStrokeItem->m_rectBounding.SetRect(nX-(nW/2),
                                                nY+(nH/2),
                                                nX+(nW/2),
                                                nY-(nH/2));
        }

        else
        {
            AfxMessageBox("Could not create image object");
            delete pStrokeItem;
            return NULL;
        }
    }

    ...
}

Stroke Class:

In the CStroke class, the DrawStroke() function handles drawing of the image:
BOOL CStroke::DrawStroke(CDC*           pDC,
                         BOOL           bDirectDisplay,
                         CGameMaprView* pView)
{

    ...


    //Render an image file-----------------------
    else if (StrokeType() == S_TYPE_IMAGE_FILE)
    {
        DrawImage(pDC,
                  m_csBitmapResource,
                  m_rectBounding.CenterPoint(),
                  pView->m_hWnd,
                  m_rectBounding);
    }

    ...

}

The stroke class also has an "update properties" method that allows a user to specify a different height and width for the image.

Printing Issues:

For some reason, articles dealing with images (especially bitmaps) never talk about printing issues to consider.

The biggest one is the aspect ratio:

LOGPIXELSX and LOGPIXELSY, to a printer's Device Context (DC) mean the number of Dots Per Inch (DPI). On a 600 DPI printer, these values will be returned as 600.

If you are not careful, this will have "interesting" results when you print!

The second issue is transparency. Hewlett-Packard printers, for example, do not seem to recognize the BitBlt ROP codes properly, making it impossible to render an image transparently by the normally-shown methods.

(This may have been fixed on later models - HP never responded when I tried to contact them about it.)

This limitation also exists in the above functions: You will not be able to print a transparent image (such as the knight in the image sample above) using OleLoadImage(). Non-transparent printing seems to work fine.

To do transparent printing, you would need to turn the image into a bitmap, and then print it using regions of color rather than bit-blitting with the "true mask" method.

Failures to Load:

LOADPIC states that it will open Enhanced metafiles and Windows metafiles (EMF and WMF formats).

I successfully loaded two EMF's; however, when I tried to load c:\windows\system\spbanner.wmf, I got:

"Could not load image (hr failure)". Word 97 would not load this file properly either.

If desired, a reader could add further analysis code to list the detailed cause of such failures.

Conclusion:

I have found these functions very useful; it is my hope that other readers will also find them useful for rendering various types of images.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Wes Rogers

United States United States
No Biography provided

Comments and Discussions

 
QuestionOleLoadPicture not wroking Pinmemberkeyur_patel9-Feb-14 19:21 
GeneralDisplaying animated GIF's PinmemberTushar Jadhav23-May-08 21:45 
GeneralRotating an Image PinmemberAlex Evans4-Jan-05 9:30 
GeneralError C2065 PinmemberMEDJAI12-Nov-04 2:20 
GeneralRe: Error C2065 PinsussAnonymous5-May-05 12:43 
GeneralTo Load Tif PinmemberSagarika16-Feb-04 21:45 
Questionhow can i render a picture and make the picture gray? PinsussAnonymous6-Dec-03 21:44 
GeneralPortable Network Graphics PinmemberDhirendra20-Feb-03 19:03 
GeneralConverting Jpeg's to numbers in C or C++ PinsussAtomRiot24-Oct-02 10:20 
GeneralRe: Converting Jpeg's to numbers in C or C++ PinmemberChristian Graus24-Oct-02 10:33 
GeneralRe: Converting Jpeg's to numbers in C or C++ Pinsussboggs17-Feb-05 14:49 
GeneralUpside down picture PinsussAnonymous27-Sep-02 15:48 
GeneralRe: Upside down picture PinsussAnonymous27-Sep-02 16:01 
GeneralRe: Upside down picture PinsussFrancis Stafki20-Nov-02 11:10 
Questionhow to change .bmp files to .jpg files PinsussLong zhiyi23-Sep-02 19:34 
AnswerRe: how to change .bmp files to .jpg files Pinmemberraygall25-Dec-02 13:15 
GeneralRe: how to change .bmp files to .jpg files Pinsusskaorukamiya6-Jun-03 5:47 
Generalchanging JPEG files in .gif files PinmemberBellinger8-Jun-02 1:23 
GeneralResource leak! PinmemberAnonymous28-Oct-01 20:56 
QuestionDisplay & Edit Bitmap ? PinmemberAn Binh24-Sep-01 4:12 
AnswerRe: Display & Edit Bitmap ? PinmemberkimVong11-Oct-01 8:09 
GeneralRe: Display & Edit Bitmap ? PinmemberAnonymous11-Oct-01 15:17 
GeneralSimpler and easier way! PinmemberJarek Gibek7-Sep-01 3:05 
GeneralRe: Simpler and easier way! PinmemberAnonymous11-Apr-02 20:13 
GeneralRe: Simpler and easier way! PinsussWilfried Soeker16-Jul-04 9:52 
GeneralRe: Simpler and easier way! PinsussAnonymous20-Oct-05 12:03 
GeneralLost in DC's Pinmemberstasonline9-Jul-01 11:27 
GeneralRe: Lost in DC's Pinmemberanonymous9-Jul-01 13:12 
GeneralUpside Down Pinmemberstasonline6-Jul-01 8:21 
GeneralRe: Upside Down PinsussAnonymous27-Sep-02 16:04 
GeneralOleLoadPicture in Windows CE PinmemberVolker Boos12-Jun-01 20:50 
GeneralRe: OleLoadPicture in Windows CE Pinmemberkennethkuan5-Jul-09 15:29 
QuestionThis ain't real fast... any ideas? PinmemberAnonymous24-May-01 12:43 
AnswerRe: This ain't real fast... any ideas? PinmemberChristian Graus24-May-01 12:47 
QuestionDetailed "Example of use" possible? PinmemberAnonymous7-May-01 4:45 
Generalhistogram PinmemberAnonymous17-Apr-01 0:36 
GeneralEven easier loading of files -- and URLs! PinmemberMike Morearty13-Mar-01 11:41 
GeneralRe: Even easier loading of files -- and URLs! Pinmemberstasonline6-Jul-01 8:27 
GeneralRe: Even easier loading of files -- and URLs! Pinmemberstasonline6-Jul-01 8:29 
GeneralRe: Even easier loading of files -- and URLs! PinmemberJarek Gibek4-Sep-01 5:50 
GeneralOleLoadPicturePath leaks PinmemberAndy Hofmann9-Jan-02 23:39 
GeneralRe: Even easier loading of files -- and URLs! PinsussRoman Antonczyk24-Nov-02 1:57 
GeneralgpPicture returns NULL, when passing it through a function argument. PinmemberGlenn Slotte15-Dec-00 3:24 
GeneralRe: gpPicture returns NULL, when passing it through a function argument. PinmemberWesley Rogers17-Jan-01 7:11 
GeneralHere is an easy example how to use the code! PinmemberMichael Menne13-Dec-00 3:50 
Generaltransparent region... PinmemberWillow77-Dec-00 2:56 
GeneralUsing Animated GIF files PinmemberRon Maman25-Nov-00 23:01 
GeneralRe: Using Animated GIF files PinmemberWes Rogers26-Nov-00 4:36 
QuestionThe Sample Please? PinmemberBrian Hart23-Nov-00 5:37 
AnswerRe: The Sample Please? PinmemberWesley Rogers17-Jan-01 7:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140921.1 | Last Updated 27 Sep 2000
Article Copyright 2000 by Wes Rogers
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid