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;
}