How to Get a OpenGL image from window?

How to use glReadPixels() to read a Opengl window, write the pixel data to a DIB, and submit the DIB for printing or save as bmp image?

I just did this a few days ago. Here is the code I used but it’s all in windows API (which I’m assuming cause you use the word DIB, if I’m wrong I apologize). This code takes the OpenGL image in the window hwndMap, asks the user for a file name, and saves the image to the file:

OPENFILENAME fileData;
char fileName[MAX_PATH];

memset(&fileData,0,sizeof(OPENFILENAME));
memset(fileName,0,sizeof(fileName));

fileData.lStructSize = sizeof(OPENFILENAME);
fileData.hwndOwner = hwndMap;
fileData.lpstrFilter = “Bitmaps (.bmp)\0.bmp\0\0”;
fileData.lpstrFile = fileName;
fileData.nMaxFile = MAX_PATH;
fileData.lpstrTitle = “Save bitmap file name”;
fileData.Flags = OFN_ENABLESIZING | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
fileData.lpstrDefExt = “bmp”;

if ( ! GetSaveFileName(&fileData) ) {
return FALSE;
}

HBITMAP hBitmap;
BITMAP bitMap;
HDC hdc,hdcCompatible;
WORD cClrBits;
BYTE* lpBits;

BITMAPINFO* pBitmapInfo;
BITMAPFILEHEADER bitmapFileHeader;

HANDLE hFile;
DWORD cb,dwTmp,dwTotal;

hdc = ::GetDC(hwndMap);
hdcCompatible = ::CreateCompatibleDC(hdc);

memset(&bitMap,0,sizeof(BITMAP));

hBitmap = CreateCompatibleBitmap(hdc,rectWindow.right - rectWindow.left,rectWindow.bottom - rectWindow.top);

SelectObject(hdcCompatible,hBitmap);

BitBlt(hdcCompatible,0,0,rectWindow.right - rectWindow.left,rectWindow.bottom - rectWindow.top,hdc,0,0,SRCCOPY);

GetObject(hBitmap,sizeof(BITMAP),&bitMap);

cClrBits = (WORD)(bitMap.bmPlanes * bitMap.bmBitsPixel);
if ( cClrBits == 1 )
cClrBits = 1;
else if ( cClrBits <= 4 )
cClrBits = 4;
else if ( cClrBits <= 8 )
cClrBits = 8;
else if ( cClrBits <= 16 )
cClrBits = 16;
else if ( cClrBits <= 24 )
cClrBits = 24;
else
cClrBits = 32;

if ( cClrBits != 24 )
cb = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits);
else
cb = sizeof(BITMAPINFOHEADER);

pBitmapInfo = reinterpret_cast<BITMAPINFO *>(new BYTE[cb]);

memset(pBitmapInfo, 0, cb);

BITMAPINFOHEADER *pbmHeader = &pBitmapInfo -> bmiHeader;
pbmHeader -> biSize = sizeof(BITMAPINFOHEADER);
pbmHeader -> biWidth = bitMap.bmWidth;
pbmHeader -> biHeight = bitMap.bmHeight;
pbmHeader -> biPlanes = bitMap.bmPlanes;
pbmHeader -> biBitCount = bitMap.bmBitsPixel;
if ( cClrBits < 24 )
pbmHeader -> biClrUsed = (1 << cClrBits);
pbmHeader -> biCompression = BI_RGB;
pbmHeader -> biSizeImage = ((pbmHeader -> biWidth * cClrBits +31) & ~31) * pbmHeader -> biHeight / 8;

lpBits = new BYTE[pbmHeader -> biSizeImage];

GetDIBits(hdcCompatible,hBitmap,0,pbmHeader -> biHeight,lpBits,pBitmapInfo,DIB_RGB_COLORS);

hFile = CreateFile(fileName, GENERIC_READ | GENERIC_WRITE, (DWORD) 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);

memset(&bitmapFileHeader, 0, sizeof(BITMAPFILEHEADER));
bitmapFileHeader.bfType = 0x4d42;
bitmapFileHeader.bfSize = (DWORD)(sizeof(BITMAPFILEHEADER) + pbmHeader -> biSize + pbmHeader -> biClrUsed * sizeof(RGBQUAD) + pbmHeader -> biSizeImage);
bitmapFileHeader.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbmHeader -> biSize + pbmHeader -> biClrUsed * sizeof (RGBQUAD);

WriteFile(hFile, (LPVOID) &bitmapFileHeader, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL);
WriteFile(hFile, (LPVOID) pbmHeader, sizeof(BITMAPINFOHEADER) + pbmHeader -> biClrUsed * sizeof (RGBQUAD), (LPDWORD) &dwTmp, ( NULL));

dwTotal = cb = pbmHeader -> biSizeImage;

WriteFile(hFile, (LPSTR) lpBits, (int) cb, (LPDWORD) &dwTmp,NULL);

CloseHandle(hFile);

DeleteObject(hBitmap);
DeleteDC(hdc);

delete [] lpBits;
delete [] pBitmapInfo;

Here’s some non-Win32 specific code to do the same, in case you’re interested in portability:

#ifndef SCREENSHOT_H
#define SCREENSHOT_H

typedef enum {
SCREENSHOT_PPM,
SCREENSHOT_TGA,
SCREENSHOT_BMP,
} SCREENSHOT_FORMAT;

/*

  • SCREENSHOT_Take will dump the given portion of the GL framebuffer to
  • the given filename. The file will be overwritten
  • x, y, width, height definre the rectangle to capture.
  • 0, 0 is the upper-left corner
  • The image will be stored in the given format
  • Returns negative on failure, zero on success
    */
    int SCREENSHOT_Take(int x, int y, int width, int height,
    const char *fname,
    SCREENSHOT_FORMAT format);

#endif // !SCREENSHOT_H

/*

  • screenshot.c
  • Routines for taking screen shots from GL
  • Ryan T. Sammartino
    */

#include <stdio.h>
#include <malloc.h>
#include <GL/gl.h>

#include “screenshot.h”

static int SCREENSHOT_WriteBMP(const char fname,
const unsigned char *
image,
int w, int h)
{
/
See http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/BMP.txt */
struct BMPHeader {
unsigned short type;
unsigned int size;
unsigned short res1;
unsigned short res2;
unsigned int offset;
} attribute((packed)) header;
struct BMPInfo {
unsigned int size;
unsigned int width;
unsigned int height;
unsigned short planes;
unsigned short bit_count;
unsigned int comp;
unsigned int sizeImage;
unsigned int x_pels_per_meter;
unsigned int y_pels_per_meter;
unsigned int clr_used;
unsigned int clr_important;
} attribute((packed)) info;
FILE *fp;
int ret = 0;

    /* Open file */
    fp = fopen(fname, "wb");
    if (!fp) {
            fprintf(stderr,
                            "Unable to open %s for writing

",
fname);
ret = -1;
} else {
/* Header */
header.type = ‘B’ | ‘M’ << 8;
header.size = sizeof(header) + sizeof(info) + w * h * 3;
header.res1 = header.res2 = 0;
header.offset = sizeof(header) + sizeof(info);
info.size = sizeof(info);
info.width = w;
info.height = h;
info.planes = 1;
info.bit_count = 24;
info.comp = 0;
info.sizeImage = w * h * 3;
info.x_pels_per_meter = info.y_pels_per_meter = 0;
info.clr_used = 0;
info.clr_important = 0;

            fwrite(&header, sizeof(header), 1, fp);
            fwrite(&info, sizeof(info), 1, fp);

            fwrite(image, sizeof(unsigned char), h*w*3, fp);
    }

    if (fp) {
            fclose(fp);
    }

    return ret;

}

static int SCREENSHOT_WriteTGA(const char *fname,
const unsigned char *
image,
int w, int h)
{
int i;
FILE fp;
int ret = -1;
/
See http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/TGA.txt for spec */
struct TGAHeader {
unsigned char idfield_len;
unsigned char cmap_type;
unsigned char image_type;
unsigned char cmap_spec[5];
unsigned char x_orig[2];
unsigned char y_orig[2];
unsigned char width[2];
unsigned char height[2];
unsigned char pixel_size;
unsigned char image_desc;
} attribute((packed)) header;

    /* Open file */
    fp = fopen(fname, "wb");
    if (!fp) {
            fprintf(stderr,
                            "Unable to open %s for writing

",
fname);
ret = -1;
} else {
/* Construct header /
header.idfield_len = 0;
header.cmap_type = 0;
header.image_type = 2;
for (i = 0; i < 5; i++) {
header.cmap_spec[i] = 0;
}
for (i = 0; i < 2; i++) {
header.x_orig[i] = 0;
header.y_orig[i] = 0;
}
/
Lo bits /
header.width[0] = w & 0xFF;
/
Hi bits */
header.width[1] = (w >> 8) & 0xFF;
header.height[0] = h & 0xFF;
header.height[1] = (h >> 8) & 0xFF;
header.pixel_size = 24;
header.image_desc = 0;

            /* Output header */
            fwrite(&header, sizeof(header), 1, fp);

            /* Output image */
            fwrite(image, sizeof(unsigned char), w*h*3, fp);
    }

    if (fp) {
            fclose(fp);
    }

    return ret;

}

static int SCREENSHOT_WritePPM(const char *fname,
const unsigned char *
image,
int w, int h)
{
unsigned char *row;
int i;
FILE *fp;
int ret = 0;

    /* Open file */
    fp = fopen(fname, "wb");
    if (!fp) {
            fprintf(stderr,
                            "Unable to open %s for writing

",
fname);
ret = -1;
} else {
/* Output header */
fprintf(fp,
"P6
#OpenSpace Screenshot %s
%d %d
%d
",
fname, w, h, 255);

            /* GL returns the data upside down */
            for (i = h - 1; i >= 0; i--) {
                    row = (unsigned char*)image + w * i * 3;
                    fwrite(row, sizeof(unsigned char), w*3, fp);
            }
    }

    if (fp) {
            fclose(fp);
    }

    return ret;

}

/*

  • SCREENSHOT_Take will dump the given portion of the GL framebuffer to

  • the given filename

  • x, y, width, height define the rectangle to capture.

  • The image will be stored in the given format

  • Returns negative on failure, zero on success
    */
    int SCREENSHOT_Take(int x, int y, int width, int height,
    const char *fname,
    SCREENSHOT_FORMAT format)
    {
    unsigned char *image = NULL;
    int ret = 0;

     /* Reserve some memory */
     image = (unsigned char*)malloc(sizeof(unsigned char)*width*height*3);
     if (!image) {
             fprintf(stderr,
                             "Unable to allocate image buffer
    

");
ret = -1;
} else {
switch(format) {
case SCREENSHOT_PPM:
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BY
TE, image);
ret = SCREENSHOT_WritePPM(fname, image, width, height);
break;
case SCREENSHOT_TGA:
glReadPixels(x, y, width, height, GL_BGR, GL_UNSIGNED_BY
TE, image);
ret = SCREENSHOT_WriteTGA(fname, image, width, height);
break;
case SCREENSHOT_BMP:
glReadPixels(x, y, width, height, GL_BGR, GL_UNSIGNED_BY
TE, image);
ret = SCREENSHOT_WriteBMP(fname, image, width, height);
break;
default:
fprintf(stderr, "Invalid format %d
", format);
ret = -1;
break;
}
}

    if (image) {
            free(image);
    }

    return ret;

}