OpenGL加载、显示、截图、保存位图文件

1.加载与显示BMP位图文件

位图,有时候也被称之为栅栏图、栅格图,是一种保存着图像每一位像素的格式。姑且这么说吧。以此相对的有矢量图,矢量图是用数学公式保存着每一点、每一条线条的方式来存储图像的。


在windows操作系统中,我们都可以使用微软定义的bmp位图(Bitmap)文件。位图有压缩的,也有没压缩的。我们这里主要看没压缩的。


Bitmap位图文件的结构主要由三部分组成。首先在文件的头部是BITMAPFILEHEADER,接着的是BITMAPINFOHEADER,下面是相关结构体的声明。

typedef struct tagBITMAPFILEHEADER { 
  WORD    bfType;      //指定文件类型,对于位图文件,这里必须为BM即 0x4D42
  DWORD   bfSize;      // BITMAPFILEHEADER结构体的字节大小
  WORD    bfReserved1;   //必须为0
  WORD    bfReserved2;  //必须为0
  DWORD   bfOffBits;     //像素的偏移地址
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
    DWORD  biSize;        // BITMAPINFOHEADER结构体的字节大小
    LONG   biWidth;       //图像的宽度
    LONG   biHeight;       //图像的高度
    WORD   biPlanes;       //目标设备平面数目,必须为1
    WORD   biBitCount;       //每个像素的颜色位数,可取1,4,8,16,24,32
    DWORD  biCompression;     //压缩类型,我们主要是关注没有压缩的,即BI_RGB
    DWORD  biSizeImage;     //图像数据的字节大小(不包括两个头部)
    LONG   biXPelsPerMeter; 
    LONG   biYPelsPerMeter;
    DWORD  biClrUsed;
    DWORD  biClrImportant;
} BITMAPINFOHEADER;

在上面的两个结构体重,我们知道了怎么辨别一个位图文件,也知道了像素的开始偏移,图像的宽度、高度,图像数据的字节大小。如此,就好办了。


有一个需要注意下一,位图文件保存的颜色数据是BGR的顺序,而我们在内存中通常使用的是RGB的顺序,所以必要时,我们需要手动转换下顺序。


下面是加载位图文件的图像像素数据的示例函数


// LoadBitmapFile
//加载filename指定的位图文件于内存中,并通过参数bitmapInfoHeader返回BITMAPINFOHEADER头部
//返回值:图像数据,使用完之后需自行释放内存
//不支持8bits的位图文件
unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
	FILE *filePtr;								// the file pointer
	BITMAPFILEHEADER	bitmapFileHeader;		// bitmap file header
	unsigned char		*bitmapImage;			// bitmap image data
	int					imageIdx = 0;			// image index counter
	unsigned char		tempRGB;				// swap variable

	// open filename in "read binary" mode
	filePtr = fopen(filename, "rb");
	if (filePtr == NULL)
		return NULL;

	// read the bitmap file header
	fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);

	// verify that this is a bitmap by checking for the universal bitmap id
	if (bitmapFileHeader.bfType != BITMAP_ID)
	{
		fclose(filePtr);
		return NULL;
	}

	// read the bitmap information header
	fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);

	// move file pointer to beginning of bitmap data
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

	// allocate enough memory for the bitmap image data
	bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

	// verify memory allocation
	if (!bitmapImage)
	{
		free(bitmapImage);
		fclose(filePtr);
		return NULL;
	}

	// read in the bitmap image data
	fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);

	// make sure bitmap image data was read
	if (bitmapImage == NULL)
	{
		fclose(filePtr);
		return NULL;
	}

	// swap the R and B values to get RGB since the bitmap color format is in BGR
	for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx+=3)
	{
		tempRGB = bitmapImage[imageIdx];
		bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
		bitmapImage[imageIdx + 2] = tempRGB;
	}

	// close the file and return the bitmap image data
	fclose(filePtr);
	return bitmapImage;
}


加载完图像数据之后,就可以显示出来了。


显示图像,在opengl中我们有glDrawPixels,可以将图像像素绘制出来。


void glDrawPixels(
  GLsizei width,
  GLsizei height,
  GLenum format,
  GLenum type,
  const GLvoid *pixels   //图像数据
);

看到这里,我们发现,只有指定宽度跟高度。起点位置似乎没有办法在这个函数里头指定。


类似于glTranslate函数,我们可以使用glRasterPos来指定图像的起始位置(左下角)。需要注意一下,opengl是以左下角为原点,以右、上分别为x、y轴的正方向。以屏幕正对向外(也即向着用户)的方向为z轴的正方向。


void glRasterPos2i(
  GLint x,
  GLint y
);

至此,还有个问题。有时候当我们将一个程序从台式电脑移到笔记本电脑上,执行速度可能会不一样。排除硬件速度的不一样外,可能也跟一个内存对齐问题有关。所以这里还需要使用glPixelStorei指定下内存对齐大小。默认是4KB。

void glPixelStorei(
  GLenum pname,
  GLint param
);

pname取GL_UNPACK_ALIGNMENT时,代表读取内存时的对齐大小。一直相对的GL_PACK_ALIGNMENT,代表加载进内存时的对齐大小。


当pname取以上两个值之一时,param可取1,2,4,8,分别代表对齐大小为1,2,4,8字节。


如此,下面看下绘图的函数。


// DrawBitmap
// 在(0,0)位置绘制width长,height宽的图像
//bitmapImage是图像的数据
void DrawBitmap(long width, long height, unsigned char* bitmapImage)
{
	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
	//于原点绘图
	glRasterPos2i(0,0);	
	glDrawPixels(width, height, GL_RGB, GL_UNSIGNED_BYTE, bitmapImage);
}

有此两个函数,程序已能从文件里头加载位图文件,并显示出来了。


2.保存位图文件

在上面,我们知道了位图的文件结构。那么,我们想将一段图像数据保存为位图文件,也已经没了什么难度了。下面看下代码。

//WriteBitmapFile
//根据bitmapData的(RGB)数据,保存bitmap
//filename是要保存到物理硬盘的文件名(包括路径)
BOOL WriteBitmapFile(char * filename,int width,int height,unsigned char * bitmapData)
{
	//填充BITMAPFILEHEADER
	BITMAPFILEHEADER bitmapFileHeader;
	memset(&bitmapFileHeader,0,sizeof(BITMAPFILEHEADER));
	bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
	bitmapFileHeader.bfType = 0x4d42;	//BM
	bitmapFileHeader.bfOffBits =sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	//填充BITMAPINFOHEADER
	BITMAPINFOHEADER bitmapInfoHeader;
	memset(&bitmapInfoHeader,0,sizeof(BITMAPINFOHEADER));
	bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitmapInfoHeader.biWidth = width;
	bitmapInfoHeader.biHeight = height;
	bitmapInfoHeader.biPlanes = 1;
	bitmapInfoHeader.biBitCount = 24;
	bitmapInfoHeader.biCompression = BI_RGB;
	bitmapInfoHeader.biSizeImage = width * abs(height) * 3;

	//////////////////////////////////////////////////////////////////////////
	FILE * filePtr;			//连接要保存的bitmap文件用
	unsigned char tempRGB;	//临时色素
	int imageIdx;

	//交换R、B的像素位置,bitmap的文件放置的是BGR,内存的是RGB
	for (imageIdx = 0;imageIdx < bitmapInfoHeader.biSizeImage;imageIdx +=3)
	{
		tempRGB = bitmapData[imageIdx];
		bitmapData[imageIdx] = bitmapData[imageIdx + 2];
		bitmapData[imageIdx + 2] = tempRGB;
	}

	filePtr = fopen(filename,"wb");
	if (NULL == filePtr)
	{
		return FALSE;
	}

	fwrite(&bitmapFileHeader,sizeof(BITMAPFILEHEADER),1,filePtr);

	fwrite(&bitmapInfoHeader,sizeof(BITMAPINFOHEADER),1,filePtr);

	fwrite(bitmapData,bitmapInfoHeader.biSizeImage,1,filePtr);

	fclose(filePtr);
	return TRUE;
}

有了上面的基础,这里也不需要多说什么了。

 

接下来,就是读取窗口中的图像数据了。在opengl中,有个glReadPixels可以帮到我们。glReadPixels能够读取窗口块的图像数据


void glReadPixels(
  GLint x,      //
  GLint y,      //起始位置
  GLsizei width,      //宽度
  GLsizei height,      //高度
  GLenum format,
  GLenum type,
  GLvoid *pixels      //保存的内存地址
);

注意,在使用此函数之前,我们需要先申请足够的内存地址用来保存图像数据。

 

下面是读取窗口图像数据的函数。


//SaveScreenShot
//保存窗口客户端的截图
//窗口大小* 600
void SaveScreenShot()
{
	int clnWidth,clnHeight;	//client width and height
	static void * screenData;
	RECT rc;
	int len = 800 * 600 * 3;
	screenData = malloc(len);
	memset(screenData,0,len);
	glReadPixels(0, 0, 800, 600, GL_RGB, GL_UNSIGNED_BYTE, screenData);

	//生成文件名字符串,以时间命名
	time_t tm = 0;
	tm = time(NULL);
	char lpstrFilename[256] = {0};
	sprintf_s(lpstrFilename,sizeof(lpstrFilename),"%d.bmp",tm);

	WriteBitmapFile(lpstrFilename,800,600,(unsigned char*)screenData);
	//释放内存
	free(screenData);

}

至此,已经可以读取、显示、截图、保存位图文件了。下面看一下完整的代码。


#include "stdafx.h"

#define WIN32_LEAN_AND_MEAN		// trim the excess fat from Windows

////// Defines
#define BITMAP_ID 0x4D42		// the universal bitmap ID

////// Includes
#include <windows.h>			// standard Windows app include
#include <stdio.h>
#include <gl/gl.h>				// standard OpenGL include
#include <gl/glu.h>				// OpenGL utilties
#include <time.h>
#include <windef.h>
#include <string.h>

////// Global Variables
HDC g_HDC;						// global device context
bool fullScreen = false;				// true = fullscreen; false = windowed
bool keyPressed[256];				// holds true for keys that are pressed		

////// Bitmap Information
BITMAPINFOHEADER	bitmapInfoHeader;	// bitmap info header
unsigned char*		bitmapData;		// the bitmap data
//////////////////////////////////////////////////////////////////////////
//全局函数声明

//WriteBitmapFile
//根据bitmapData的(RGB)数据,保存bitmap
//filename是要保存到物理硬盘的文件名(包括路径)
BOOL WriteBitmapFile(char * filename,int width,int height,unsigned char * bitmapData);
//SaveScreenShot
//保存窗口客户端的截图
//窗口大小* 600
void SaveScreenShot();


//////////////////////////////////////////////////////////////////////////

// DrawBitmap
// desc: draws the bitmap image data in bitmapImage at the location
//		 (350,300) in the window. (350,300) is the lower-left corner
//		 of the bitmap.
void DrawBitmap(long width, long height, unsigned char* bitmapImage)
{
// 	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
// 	glRasterPos2i(200,200);
// 	glDrawPixels(width, height, GL_RGB, GL_UNSIGNED_BYTE, bitmapImage);

	glPixelStorei(GL_UNPACK_ALIGNMENT,4);
	glRasterPos2i(0,0);
	glDrawPixels(width,height,GL_RGB,GL_UNSIGNED_BYTE,bitmapImage);

}


// LoadBitmapFile
// desc: Returns a pointer to the bitmap image of the bitmap specified
//       by filename. Also returns the bitmap header information.
//		 No support for 8-bit bitmaps.
unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
	FILE *filePtr;								// the file pointer
	BITMAPFILEHEADER	bitmapFileHeader;		// bitmap file header
	unsigned char		*bitmapImage;			// bitmap image data
	int					imageIdx = 0;			// image index counter
	unsigned char		tempRGB;				// swap variable

	// open filename in "read binary" mode
	filePtr = fopen(filename, "rb");
	if (filePtr == NULL)
		return NULL;

	// read the bitmap file header
	fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);

	// verify that this is a bitmap by checking for the universal bitmap id
	if (bitmapFileHeader.bfType != BITMAP_ID)
	{
		fclose(filePtr);
		return NULL;
	}

	// read the bitmap information header
	fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);

	// move file pointer to beginning of bitmap data
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

	// allocate enough memory for the bitmap image data
	bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

	// verify memory allocation
	if (!bitmapImage)
	{
		free(bitmapImage);
		fclose(filePtr);
		return NULL;
	}

	// read in the bitmap image data
	fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);

	// make sure bitmap image data was read
	if (bitmapImage == NULL)
	{
		fclose(filePtr);
		return NULL;
	}

	// swap the R and B values to get RGB since the bitmap color format is in BGR
	for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx+=3)
	{
		tempRGB = bitmapImage[imageIdx];
		bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
		bitmapImage[imageIdx + 2] = tempRGB;
	}

	// close the file and return the bitmap image data
	fclose(filePtr);
	return bitmapImage;
}


// Initialize
// desc: initializes OpenGL
void Initialize()
{
	// enable depth buffer, and backface culling
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);

	// Clear background to black
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	// load our bitmap file
	bitmapData = LoadBitmapFile("test.bmp", &bitmapInfoHeader);
}

// Render
// desc: handles drawing of scene
void Render()
{
	// clear screen and depth buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);												

	// draw the bitmap image
	DrawBitmap(bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight, bitmapData);

	glFlush();
	SwapBuffers(g_HDC);			// bring backbuffer to foreground
}

// function to set the pixel format for the device context
void SetupPixelFormat(HDC hDC)
{
	int nPixelFormat;					// our pixel format index

	static PIXELFORMATDESCRIPTOR pfd = {
		sizeof(PIXELFORMATDESCRIPTOR),	// size of structure
		1,								// default version
		PFD_DRAW_TO_WINDOW |			// window drawing support
		PFD_SUPPORT_OPENGL |			// OpenGL support
		PFD_DOUBLEBUFFER,				// double buffering support
		PFD_TYPE_RGBA,					// RGBA color mode
		32,								// 32 bit color mode
		0, 0, 0, 0, 0, 0,				// ignore color bits, non-palettized mode
		0,								// no alpha buffer
		0,								// ignore shift bit
		0,								// no accumulation buffer
		0, 0, 0, 0,						// ignore accumulation bits
		16,								// 16 bit z-buffer size
		0,								// no stencil buffer
		0,								// no auxiliary buffer
		PFD_MAIN_PLANE,					// main drawing plane
		0,								// reserved
		0, 0, 0 };						// layer masks ignored

		nPixelFormat = ChoosePixelFormat(hDC, &pfd);	// choose best matching pixel format

		SetPixelFormat(hDC, nPixelFormat, &pfd);		// set pixel format to device context
}

// the Windows Procedure event handler
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HGLRC hRC;					// rendering context
	static HDC hDC;						// device context
	int width, height;					// window width and height

	switch(message)
	{
	case WM_CREATE:					// window is being created

// 		hDC = GetDC(hwnd);			// get current window's device context
// 		g_HDC = hDC;
// 		SetupPixelFormat(hDC);		// call our pixel format setup function
// 
// 		// create rendering context and make it current
// 		hRC = wglCreateContext(hDC);
// 		wglMakeCurrent(hDC, hRC);
// 
// 		return 0;

		g_HDC = GetDC(hwnd);
		SetupPixelFormat(g_HDC);
		hRC = wglCreateContext(g_HDC);
		wglMakeCurrent(g_HDC,hRC);

		break;
	case WM_DESTROY:
	case WM_CLOSE:					// windows is closing

		// deselect rendering context and delete it
		wglMakeCurrent(hDC, NULL);
		wglDeleteContext(hRC);

		ExitProcess(NULL);

		// send WM_QUIT to message queue
		PostQuitMessage(0);

		return 0;
		break;

	case WM_SIZE:
		width = LOWORD(lParam);
		height = HIWORD(lParam);
		glViewport(0,0,width,height);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		//2D otho projection
		glOrtho(0.0f, width - 1.0, 0.0, height - 1.0, -1.0, 1.0);
		//		glOrtho(0.0f,width,0.0f,height,-1.0f,1.0f);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		return 0;


		break;

	case WM_KEYDOWN:					// is a key pressed?
		keyPressed[wParam] = true;
		return 0;
		break;

	case WM_KEYUP:
		keyPressed[wParam] = false;
		return 0;
		break;

	default:
		break;
	}

	return (DefWindowProc(hwnd, message, wParam, lParam));
}

// the main windows entry point
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	WNDCLASSEX windowClass;		// window class
	HWND	   hwnd;			// window handle
	MSG		   msg;				// message
	bool	   done;			// flag saying when our app is complete
	DWORD	   dwExStyle;		// Window Extended Style
	DWORD	   dwStyle;			// Window Style
	RECT	   windowRect;

	// temp var's
	int width = 800;
	int height = 600;
	int bits = 32;

	//fullScreen = TRUE;

	windowRect.left=(long)0;						// Set Left Value To 0
	windowRect.right=(long)width;					// Set Right Value To Requested Width
	windowRect.top=(long)0;							// Set Top Value To 0
	windowRect.bottom=(long)height;					// Set Bottom Value To Requested Height

	// fill out the window class structure
	windowClass.cbSize			= sizeof(WNDCLASSEX);
	windowClass.style			= CS_HREDRAW | CS_VREDRAW;
	windowClass.lpfnWndProc		= WndProc;
	windowClass.cbClsExtra		= 0;
	windowClass.cbWndExtra		= 0;
	windowClass.hInstance		= hInstance;
	windowClass.hIcon			= LoadIcon(NULL, IDI_APPLICATION);	// default icon
	windowClass.hCursor			= LoadCursor(NULL, IDC_ARROW);		// default arrow
	windowClass.hbrBackground	= NULL;								// don't need background
	windowClass.lpszMenuName	= NULL;								// no menu
	windowClass.lpszClassName	= "MyClass";
	windowClass.hIconSm			= LoadIcon(NULL, IDI_WINLOGO);		// windows logo small icon

	// register the windows class
	if (!RegisterClassEx(&windowClass))
		return 0;

	if (fullScreen)								// fullscreen?
	{
		DEVMODE dmScreenSettings;					// device mode
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
		dmScreenSettings.dmSize = sizeof(dmScreenSettings);	
		dmScreenSettings.dmPelsWidth = width;		// screen width
		dmScreenSettings.dmPelsHeight = height;		// screen height
		dmScreenSettings.dmBitsPerPel = bits;		// bits per pixel
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		// 
		if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
		{
			// setting display mode failed, switch to windowed
			MessageBox(NULL, "Display mode failed", NULL, MB_OK);
			fullScreen=FALSE;	
		}
	}

	if (fullScreen)								// Are We Still In Fullscreen Mode?
	{
		dwExStyle=WS_EX_APPWINDOW;				// Window Extended Style
		dwStyle=WS_POPUP;						// Windows Style
		ShowCursor(FALSE);						// Hide Mouse Pointer
	}
	else
	{
		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;	// Window Extended Style
		dwStyle=WS_OVERLAPPEDWINDOW;					// Windows Style
	}

	AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);		// Adjust Window To True Requested Size

	// class registered, so now create our window
	hwnd = CreateWindowEx(NULL,									// extended style
		"MyClass",							// class name
		"Load and Save Bitmap",		// app name
		dwStyle | WS_CLIPCHILDREN |
		WS_CLIPSIBLINGS,
		0, 0,									// x,y coordinate
		windowRect.right - windowRect.left,
		windowRect.bottom - windowRect.top,	// width, height
		NULL,									// handle to parent
		NULL,									// handle to menu
		hInstance,							// application instance
		NULL);								// no extra params

	// check if window creation failed (hwnd would equal NULL)
	if (!hwnd)
		return 0;

	ShowWindow(hwnd, SW_SHOW);			// display the window
	UpdateWindow(hwnd);					// update the window

	done = false;						// intialize the loop condition variable
	Initialize();						// initialize OpenGL

	// main message loop
	while (!done)
	{
		PeekMessage(&msg, hwnd, NULL, NULL, PM_REMOVE);

		if (msg.message == WM_QUIT)		// do we receive a WM_QUIT message?
		{
			done = true;				// if so, time to quit the application
			break;
		}
		else
		{
			if (keyPressed['s'] || keyPressed['S'])
			{
				SaveScreenShot();
				keyPressed['s'] = false;
				keyPressed['S'] = false;
			}

			if (keyPressed[VK_ESCAPE])
			{
				done = true;
				break;
			}
			else
			{
				Render();

				TranslateMessage(&msg);		// translate and dispatch to event queue
				DispatchMessage(&msg);
			}
		}
	}

	free(bitmapData);

	if (fullScreen)
	{
		ChangeDisplaySettings(NULL,0);		// If So Switch Back To The Desktop
		ShowCursor(TRUE);					// Show Mouse Pointer
	}

	return msg.wParam;
}

//WriteBitmapFile
//根据bitmapData的(RGB)数据,保存bitmap
//filename是要保存到物理硬盘的文件名(包括路径)
BOOL WriteBitmapFile(char * filename,int width,int height,unsigned char * bitmapData)
{
	//填充BITMAPFILEHEADER
	BITMAPFILEHEADER bitmapFileHeader;
	memset(&bitmapFileHeader,0,sizeof(BITMAPFILEHEADER));
	bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
	bitmapFileHeader.bfType = 0x4d42;	//BM
	bitmapFileHeader.bfOffBits =sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	//填充BITMAPINFOHEADER
	BITMAPINFOHEADER bitmapInfoHeader;
	memset(&bitmapInfoHeader,0,sizeof(BITMAPINFOHEADER));
	bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitmapInfoHeader.biWidth = width;
	bitmapInfoHeader.biHeight = height;
	bitmapInfoHeader.biPlanes = 1;
	bitmapInfoHeader.biBitCount = 24;
	bitmapInfoHeader.biCompression = BI_RGB;
	bitmapInfoHeader.biSizeImage = width * abs(height) * 3;

	//////////////////////////////////////////////////////////////////////////
	FILE * filePtr;			//连接要保存的bitmap文件用
	unsigned char tempRGB;	//临时色素
	int imageIdx;

	//交换R、B的像素位置,bitmap的文件放置的是BGR,内存的是RGB
	for (imageIdx = 0;imageIdx < bitmapInfoHeader.biSizeImage;imageIdx +=3)
	{
		tempRGB = bitmapData[imageIdx];
		bitmapData[imageIdx] = bitmapData[imageIdx + 2];
		bitmapData[imageIdx + 2] = tempRGB;
	}

	filePtr = fopen(filename,"wb");
	if (NULL == filePtr)
	{
		return FALSE;
	}

	fwrite(&bitmapFileHeader,sizeof(BITMAPFILEHEADER),1,filePtr);

	fwrite(&bitmapInfoHeader,sizeof(BITMAPINFOHEADER),1,filePtr);

	fwrite(bitmapData,bitmapInfoHeader.biSizeImage,1,filePtr);

	fclose(filePtr);
	return TRUE;
}

//SaveScreenShot
//保存窗口客户端的截图
//窗口大小* 600
void SaveScreenShot()
{
	int clnWidth,clnHeight;	//client width and height
	static void * screenData;
	RECT rc;
	int len = 800 * 600 * 3;
	screenData = malloc(len);
	memset(screenData,0,len);
	glReadPixels(0, 0, 800, 600, GL_RGB, GL_UNSIGNED_BYTE, screenData);

	//生成文件名字符串,以时间命名
	time_t tm = 0;
	tm = time(NULL);
	char lpstrFilename[256] = {0};
	sprintf_s(lpstrFilename,sizeof(lpstrFilename),"%d.bmp",tm);

	WriteBitmapFile(lpstrFilename,800,600,(unsigned char*)screenData);
	
	free(screenData);

}





阅读更多
个人分类: OpenGL Windows
上一篇堆和栈的区别
下一篇OpenGL加载与显示Targa位图文件
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭