Open GL Super Bible

Previous Table of Contents Next


Printing the Bitmap

Because Windows provides several convenient functions for printing within an application, it only makes sense to be able to print from our bitmap viewing program. For this example program, you will be using the standard GDI printing services.

The first thing you do is display a standard Windows print dialog using PrintDlg, as shown here:

memset(&pd, 0, sizeof(pd));
pd.lStructSize = sizeof(pd);
pd.hwndOwner   = owner;
pd.Flags       = PD_RETURNDC;
pd.hInstance   = NULL;
if (!PrintDlg(&pd))
  return (0);

If the PrintDlg function returns 0, the user has clicked the Cancel button. Otherwise, the PRINTDLG structure will contain a device context (HDC) handle that we can use for printing.

Next, you need to start the print job.

di.cbSize      = sizeof(DOCINFO);
di.lpszDocName = "OpenGL Image";
di.lpszOutput  = NULL;
StartDoc(pd.hDC, &di);

After this, you draw the bitmap using the StretchBlt function and end the print job.

StretchBlt(pd.hDC, xoffset, yoffset, xsize, ysize,
           hdc, 0, 0, info->bmiHeader.biWidth,
           info->bmiHeader.biHeight, SRCCOPY);

EndPage(pd.hDC);
EndDoc(pd.hDC);

We compute the first 4 parameters to StretchBlt based on the size of the output page. Basically, we want to scale the image to the page yet keep the aspect ratio (width/height) the same.

xsize = rect.right;
ysize = xsize * info->bmiHeader.biHeight / info->bmiHeader.biWidth;
if (ysize > rect.bottom)
{
  ysize = rect.bottom;
  xsize = ysize * info->bmiHeader.biWidth / info->bmiHeader.biHeight;
};

The offsets are computed by taking half of the difference of widths and heights:

xoffset = (rect.right - xsize) / 2;
yoffset = (rect.bottom - ysize) / 2;

Normally you might pop up a “busy printing” dialog for the user, but in this case printing happens so fast it wouldn’t be useful.

The final code for the PrintDIBitmap function is in Listing 11-11.

Listing 11-11 PrintDIBitmap function

int
PrintDIBitmap(HWND       owner, /* I - Owner/parent window */
              BITMAPINFO *info, /* I - Bitmap information */
              void       *bits) /* I - Bitmap pixel bits */
{
  PRINTDLG pd;                  /* Print dialog information */
  long     xsize,               /* Size of printed image */
           ysize,
           xoffset,             /* Offset from edges for image */
           yoffset;
  RECT     rect;                /* Page rectangle */
  DOCINFO  di;                  /* Document info */
  HDC      hdc;                 /* Device context for bitmap */
  HBITMAP  bitmap;              /* Bitmap image */
  HBRUSH   brush;               /* Background brush for page */
  HCURSOR  busy,                /* Busy cursor */
           oldcursor;           /* Old cursor */
 /*
  * Range check…
  */

  if (info == NULL || bits == NULL)
    return (0);

 /*
  * Initialize a PRINTDLG structure before displaying a standard Windows
  * print dialog…
  */

  memset(&pd, 0, sizeof(pd));
  pd.lStructSize = sizeof(pd);
  pd.hwndOwner   = owner;
  pd.Flags       = PD_RETURNDC;
  pd.hInstance   = NULL;
  if (!PrintDlg(&pd))
    return (0);                   /* User chose 'cancel'… */

 /*
  * OK, user wants to print, so set the cursor to 'busy' and start the
  * print job…
  */

  busy      = LoadCursor(NULL, IDC_WAIT);
  oldcursor = SetCursor(busy);

  SetMapMode(pd.hDC, MM_TEXT);
  di.cbSize      = sizeof(DOCINFO);
  di.lpszDocName = "OpenGL Image";
  di.lpszOutput  = NULL;

  StartDoc(pd.hDC, &di);
  StartPage(pd.hDC);

 /*
  * Clear the background to white…
  */

  rect.top    = 0;
  rect.left   = 0;
  rect.right  = GetDeviceCaps(pd.hDC, HORZRES);
  rect.bottom = GetDeviceCaps(pd.hDC, VERTRES);
  brush       = CreateSolidBrush(0x00ffffff);
  FillRect(pd.hDC, &rect, brush);

 /*
  * Stretch the bitmap to fit the page…
  */

  hdc    = CreateCompatibleDC(pd.hDC);
  bitmap = CreateDIBitmap(hdc, &(info->bmiHeader), CBM_INIT, bits, info,
                          DIB_RGB_COLORS);
  SelectObject(hdc, bitmap);

  xsize = rect.right;
  ysize = xsize * info->bmiHeader.biHeight / info->bmiHeader.biWidth;
  if (ysize > rect.bottom)
  {
    ysize = rect.bottom;
    xsize = ysize * info->bmiHeader.biWidth / info->bmiHeader.biHeight;
  };

  xoffset = (rect.right - xsize) / 2;
  yoffset = (rect.bottom - ysize) / 2;

  StretchBlt(pd.hDC, xoffset, yoffset, xsize, ysize,
             hdc, 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight,
             SRCCOPY);

 /*
  * That's it.  End the print job and free anything we allocated…
  */

  EndPage(pd.hDC);
  EndDoc(pd.hDC);
  DeleteDC(pd.hDC);

  DeleteObject(bitmap);
  DeleteObject(brush);
  DeleteObject(busy);
  DeleteDC(hdc);

 /*
  * Restore the cursor and return…
  */

  SetCursor(oldcursor);

  return (1);
}

Displaying the Bitmap

The OpenGL part of our example program begins with displaying the .BMP file. Like most OpenGL programs, this one starts out by setting the current viewport and viewing transformations.

glViewport(0, 0, rect->right, rect->bottom);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, rect->right - 1.0, 0.0, rect->bottom - 1.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);

After this, you draw the bitmap. Here we are scaling the image to fit the current window while maintaining a 1:1 aspect ratio. The following code should look very familiar—you used it in the PrintDIBitmap function above:

xsize = rect->right;
ysize = BitmapInfo->bmiHeader.biHeight * xsize /
        BitmapInfo->bmiHeader.biWidth;
if (ysize > rect->bottom)
{
  ysize = rect->bottom;
  xsize = BitmapInfo->bmiHeader.biWidth * ysize /
          BitmapInfo->bmiHeader.biHeight;
};

xscale  = (float)xsize / (float)BitmapInfo->bmiHeader.biWidth;
yscale  = (float)ysize / (float)BitmapInfo->bmiHeader.biHeight;

xoffset = (rect->right - xsize) * 0.5;
yoffset = (rect->bottom - ysize) * 0.5;

glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelZoom(xscale, yscale);
glRasterPos2i(xoffset, yoffset);
glDrawPixels(BitmapInfo->bmiHeader.biWidth,
             BitmapInfo->bmiHeader.biHeight,
             GL_RGB, GL_UNSIGNED_BYTE, BitmapBits);


Previous Table of Contents Next