??xml version="1.0" encoding="utf-8" standalone="yes"?> http://blog.csdn.net/ghost129/archive/2009/08/04/4409565.aspx BMP文g是一U像素文Ӟ它保存(sh)(jin)一q图象中所有的像素。这U文件格式可以保存单色位图?6色或256色烦(ch)引模式像素图?4位真彩色图象Q每U模式种单一像素的大分别ؓ(f)1/8字节Q?/2字节Q?字节?字节。目前最常见的是256色BMP?4位色BMP。这U文件格式还定义?jin)像素保存的几种?gu)Q包括不压羃、RLE压羃{。常见的BMP文g大多是不压羃的?br>q里Z(jin)单v见,我们仅讨?4位色、不使用压羃的BMP。(如果你用Windows自带的画囄序,很容易绘制出一个符合以上要求的BMPQ?br>Windows所使用的BMP文gQ在开始处有一个文件头Q大ؓ(f)54字节。保存(sh)(jin)包括文g格式标识、颜色数、图象大、压~方式等信息Q因为我们仅讨论24位色不压~的BMPQ所以文件头中的信息基本不需要注意,只有“大小”q一对我们比较有用。图象的宽度和高度都是一?2位整敎ͼ在文件中的地址分别?x0012?x0016Q于是我们可以用以下代码来d图象的大信息:(x) 54个字节以后,如果?6色或256色BMPQ则q有一个颜色表Q但24位色BMP没有q个Q我们这里不考虑。接下来是实际的像素数据了(jin)?4位色的BMP文g中,每三个字节表CZ个像素的颜色?br>注意QOpenGL通常使用RGB来表C颜Ԍ但BMP文g则采用BGRQ就是说Q顺序被反过来了(jin)?br>另外需要注意的地方是:(x)像素的数据量q不一定完全等于图象的高度乘(sh)宽度乘(sh)每一像素的字节数Q而是可能略大于这个倹{原因是BMP文g采用?jin)一U?#8220;寚w”的机Ӟ每一行像素数据的长度若不?的倍数Q则填充一些数据它是4的倍数。这样一来,一?7*15?4位BMP大小应该是834字节Q每?7个像素,?1字节Q补充ؓ(f)52字节Q乘?sh)?5得到像素数据总长?80Q再加上文g开始的54字节Q得?34字节Q。分配内存时Q一定要心(j)Q不能直接?#8220;图象的高度乘?sh)宽度乘(sh)每一像素的字节数”来计分配空间的长度Q否则有可能D分配的内存空间长度不I造成界讉KQ带来各U严重后果?br>一个很单的计算数据长度的方法如下:(x) int LineLength, TotalLength; qƈ不是效率最高的Ҏ(gu)Q但׃q个修正本nq算量ƈ不大Q用频率也不高Q我们就不需要再考虑更快的方法了(jin)?br>2、简单的OpenGL像素操作 3、glReadPixels的用法和举例 注意QglReadPixels实际上是从缓冲区中读取数据,如果使用?jin)双~冲区,则默认是从正在显C的~冲Q即前缓Ԍ(j)中读取,而绘制工作是默认l制到后~冲区的。因此,如果需要读取已l绘制好的像素,往往需要先交换前后~冲?/p>
再看前面提到的BMP文g中两个需要注意的地方Q?br>3.2 解决OpenGL常用的RGB像素数据与BMP文g的BGR像素数据序不一致问?br>可以使用一些代码交换每个像素的W一字节和第三字节,使得RGB的数据变成BGR的数据。当然也可以使用另外的方式解决问题:(x)新版本的OpenGL除了(jin)可以使用GL_RGBd像素的红、绿、蓝数据外,也可以用GL_BGR按照相反的顺序依ơ读取像素的蓝、绿、红数据Q这样就与BMP文g格式相吻合了(jin)。即使你的gl/gl.h头文件中没有定义q个GL_BGRQ也没有关系Q可以尝试用GL_BGR_EXT。虽然有的OpenGL实现Q尤其是旧版本的实现Qƈ不能使用GL_BGR_EXTQ但我所知道的Windows环境下各UOpenGL实现都对GL_BGR提供?jin)支持,毕竟Windows中各U表C颜色的数据几乎都是使用BGR的顺序,而非RGB的顺序。这可能与IBM-PC的硬件设计有兟?/p>
3.3 消除BMP文g?#8220;寚w”带来的媄(jing)?br>实际上OpenGL也支持用了(jin)q种“寚w”方式的像素数据。只要通过glPixelStore修改“像素保存时对齐的方式”可以了(jin)。像q样Q?br>int alignment = 4; 现在Q我们已l可以把屏幕上的像素d到内存(sh)(jin)Q如果需要的话,我们q可以将内存?sh)的数据保存到文件。正的对照BMP文g格式Q我们的E序可以把屏幕中的图象保存?sh)BMP文gQ达到屏q截囄效果?br>我们q没有详l介lBMP文g开头的54个字节的所有内容,不过q无伤大雅。从一个正的BMP文g中读取前54个字节,修改其中的宽度和高度信息Q就可以得到新的文g头了(jin)。假设我们先建立一?*1大小?4位色BMPQ文件名为dummy.bmpQ又假设新的BMP文g名称为grab.bmp。则可以~写如下代码Q?/p>
FILE* pOriginFile = fopen("dummy.bmp", "rb); /* 先在q里讄好图象的宽度和高度,即width和height的|q计像素的总长?*/ // ddummy.bmp中的?4个字节到数组 // 修改其中的大信?br>fseek(pGrabFile, 0x0012, SEEK_SET); // Ud到文件末,开始写入像素数?br>fseek(pGrabFile, 0, SEEK_END); /* 在这里写入像素数据到文g */ fclose(pOriginFile); 我们l出完整的代码,演示如何把整个窗口的图象抓取出来q保存(sh)ؓ(f)BMP文g?/p>
#define WindowWidth 400 #include <stdio.h> /* 函数grab // 计算像素数据的实际长?br> i = WindowWidth * 3; // 得到每一行的像素数据长度 // 分配内存和打开文g pDummyFile = fopen("dummy.bmp", "rb"); pWritingFile = fopen("grab.bmp", "wb"); // d像素 // 把dummy.bmp的文件头复制为新文g的文件头 // 写入像素数据 // 释放内存和关闭文?br> fclose(pDummyFile); 把这D代码复制到以前M评的样例程序中Q在l制函数的最后调用grab函数Q即可把图象内容保存?sh)BMP文g?jin)。(在我写这个教E的时候,不少地方都用q样的代码进行截囑ַ作,q段代码一旦写好,q行h是很方便的。)(j) #include <gl/glut.h> #define FileName "Bliss.bmp" static GLint ImageWidth; #include <stdio.h> void display(void) // l制像素 // 完成l制 int main(int argc, char* argv[]) // d图象的大信?br> fseek(pFile, 0x0012, SEEK_SET); // 计算像素数据长度 // d像素数据 fseek(pFile, 54, SEEK_SET); // 关闭文g // 初始化GLUTq运?br> glutInit(&argc, argv); // 释放内存 return 0; q里仅仅是一个简单的昄24位BMP图象的程序,如果读者对BMP文g格式比较熟?zhn)Q也可以写出适用于各UBMP图象的显C程序,在像素处理时Q它们所使用的方法是cM的?br>OpenGL在绘制像素之前,可以对像素进行若q处理。最常用的可能就是对整个像素图象q行攑֤/~小。用glPixelZoom来设|放?~小的系敎ͼ该函数有两个参数Q分别是水^方向pL和垂直方向系数。例如设|glPixelZoom(0.5f, 0.8f);则表C水qx(chng)向变?sh)原来?0%大小Q而垂直方向变?sh)原来?0%大小。我们甚臛_以用负的系敎ͼ使得整个图象q行水^方向或垂直方向的{Q默认像素从左绘制到叻I但翻转后从右绘制到左。默认像素从下绘制到上,但翻转后从上绘制到下。因此,glRasterPos*函数讄?#8220;开始位|?#8221;不一定就是矩形的左下角)(j)?br>5、glCopyPixels的用法和举例 void display(void) // l制 // 完成l制Qƈ抓取图象保存?sh)BMP文g 结Q?br>本课l合Windowspȝ常见的BMP图象格式Q简单介l了(jin)OpenGL的像素处理功能。包括用glReadPixelsd像素、glDrawPixelsl制像素、glCopyPixels复制像素?br>本课仅介l了(jin)像素处理的一些简单应用,但相信大家已l可以体?x)到Q围l这三个像素处理函数Q还存在一?#8220;外围”函数Q比如glPixelStore*QglRasterPos*Q以?qing)glPixelZoom{。我们仅使用?jin)这些函数的一部分功能?br>本课内容q不多,例子_丰富Q三个像素处理函数都有例子,大家可以l合例子来体?x)?/p>
附录(其它位色的BMP文g?: BMP文gl成 位图信息? 颜色? 位图数据
1?nbsp; BMP文g格式单介l?/p>
GLint width, height; // 使用OpenGL的GLintcdQ它?2位的?br> // 而C语言本n的int则不一定是32位的?br>FILE* pFile;
// 在这里进?#8220;打开文g”的操?br>fseek(pFile, 0x0012, SEEK_SET); // Ud?x0012位置
fread(&width, sizeof(width), 1, pFile); // d宽度
fseek(pFile, 0x0016, SEEK_SET); // Ud?x0016位置
// ׃上一句执行后本就应该?x0016位置
// 所以这一句可省略
fread(&height, sizeof(height), 1, pFile); // d高度
LineLength = ImageWidth * BytesPerPixel; // 每行数据长度大致为图象宽度乘?sh)?br> // 每像素的字节?br>while( LineLength % 4 != 0 ) // 修正LineLength使其?的倍数
++LineLenth;
TotalLength = LineLength * ImageHeight; // 数据总长 = 每行长度 * 图象高度
OpenGL提供?jin)简z的函数来操作像素:(x)
glReadPixelsQ读取一些像素。当前可以简单理解ؓ(f)“把已l绘制好的像素(它可能已l被保存到显卡的昑֭?sh)?j)d到内?#8221;?br>glDrawPixelsQ绘制一些像素。当前可以简单理解ؓ(f)“把内存(sh)一些数据作为像素数据,q行l制”?br>glCopyPixelsQ复制一些像素。当前可以简单理解ؓ(f)“把已l绘制好的像素从一个位|复制到另一个位|?#8221;。虽然从功能上看Q好象等价于先读取像素再l制像素Q但实际上它不需要把已经l制的像素(它可能已l被保存到显卡的昑֭?sh)?j)转换为内存数据,然后再由内存数据q行重新的绘Ӟ所以要比先d后绘制快很多?br>q三个函数可以完成简单的像素d、绘制和复制dQ但实际上也可以完成更复杂的d。当前,我们仅讨Z些简单的应用。由于这几个函数的参数数目比较多Q下面我们分别介l?/p>
3.1 函数的参数说?br>该函数d有七个参数。前四个参数可以得到一个矩形,该矩形所包括的像素都?x)被d出来。(W一、二个参数表CZ(jin)矩Ş的左下角横、纵坐标Q坐标以H口最左下角ؓ(f)Ӟ最右上角ؓ(f)最大|W三、四个参数表CZ(jin)矩Ş的宽度和高度Q?br>W五个参数表C取的内容Q例如:(x)GL_RGB׃(x)依次d像素的红、绿、蓝三种数据QGL_RGBA则会(x)依次d像素的红、绿、蓝、alpha四种数据QGL_RED则只d像素的红色数据(cM的还有GL_GREENQGL_BLUEQ以?qing)GL_ALPHAQ。如果采用的不是RGBA颜色模式Q而是采用颜色索引模式Q则也可以用GL_COLOR_INDEX来读取像素的颜色索引。目前仅需要知道这些,但实际上q可以读取其它内容,例如深度~冲区的深度数据{?br>W六个参数表C取的内容保存到内存时所使用的格式,例如QGL_UNSIGNED_BYTE?x)把各种数据保存(sh)GLubyteQGL_FLOAT?x)把各种数据保存(sh)GLfloat{?br>W七个参数表CZ个指针,像素数据被读取后Q将被保存到q个指针所表示的地址。注意,需要保证该地址有够的可以使用的空_(d)以容U取的像素数据。例如一q大ؓ(f)256*256的图象,如果d其RGB数据Q且每一数据被保存(sh)ؓ(f)GLubyteQd就是:(x)256*256*3 = 196608字节Q即192千字节。如果是dRGBA数据Q则d就?56*256*4 = 262144字节Q即256千字节?/p>
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
W一个参数表C?#8220;讄像素的对齐?#8221;Q第二个参数表示实际讄为多。这里像素可以单字节寚wQ实际上是不用对齐)(j)、双字节寚wQ如果长度ؓ(f)奇数Q则再补一个字节)(j)、四字节寚wQ如果长度不是四的倍数Q则补ؓ(f)四的倍数Q、八字节寚w。分别对应alignment的gؓ(f)1, 2, 4, 8。实际上Q默认的值是4Q正好与BMP文g的对齐方式相d?br>glPixelStorei也可以用于设|其它各U参数。但我们q里q不需要深入讨Z(jin)?/p>
FILE* pGrabFile = fopen("grab.bmp", "wb");
char BMP_Header[54];
GLint width, height;
fread(BMP_Header, sizeof(BMP_Header), 1, pOriginFile);
// 把数l内容写入到新的BMP文g
fwrite(BMP_Header, sizeof(BMP_Header), 1, pGrabFile);
fwrite(&width, sizeof(width), 1, pGrabFile);
fwrite(&height, sizeof(height), 1, pGrabFile);
fclose(pGrabFile);
#define WindowHeight 400
#include <stdlib.h>
* 抓取H口中的像素
* 假设H口宽度为WindowWidthQ高度ؓ(f)WindowHeight
*/
#define BMP_Header_Length 54
void grab(void)
{
FILE* pDummyFile;
FILE* pWritingFile;
GLubyte* pPixelData;
GLubyte BMP_Header[BMP_Header_Length];
GLint i, j;
GLint PixelDataLength;
while( i%4 != 0 ) // 补充数据Q直到i是的倍数
++i; // 本来q有更快的算法,
// 但这里仅q求直观Q对速度没有太高要求
PixelDataLength = i * WindowHeight;
pPixelData = (GLubyte*)malloc(PixelDataLength);
if( pPixelData == 0 )
exit(0);
if( pDummyFile == 0 )
exit(0);
if( pWritingFile == 0 )
exit(0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glReadPixels(0, 0, WindowWidth, WindowHeight,
GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);
fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile);
fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);
fseek(pWritingFile, 0x0012, SEEK_SET);
i = WindowWidth;
j = WindowHeight;
fwrite(&i, sizeof(i), 1, pWritingFile);
fwrite(&j, sizeof(j), 1, pWritingFile);
fseek(pWritingFile, 0, SEEK_END);
fwrite(pPixelData, PixelDataLength, 1, pWritingFile);
fclose(pWritingFile);
free(pPixelData);
}
4、glDrawPixels的用法和举例
glDrawPixels函数与glReadPixels函数相比Q参数内容大致相同。它的第一、二、三、四个参数分别对应于glReadPixels函数的第三、四、五、六个参敎ͼ依次表示图象宽度、图象高度、像素数据内宏V像素数据在内存?sh)的格式。两个函数的最后一个参C是对应的QglReadPixels中表C像素读取后存放在内存(sh)的位|,glDrawPixels则表C用于绘制的像素数据在内存(sh)的位|?br>注意到glDrawPixels函数比glReadPixels函数了(jin)两个参数Q这两个参数在glReadPixels中分别是表示图象的v始位|。在glDrawPixels中,不必昑ּ的指定绘制的位置Q这是因为绘制的位置是由另一个函数glRasterPos*来指定的。glRasterPos*函数的参CglVertex*cMQ通过指定一个二l?三维/四维坐标QOpenGL自动计出该坐标对应的屏幕位置Qƈ把该位置作ؓ(f)l制像素的v始位|?br>很自然的Q我们可以从BMP文g中读取像素数据,q用glDrawPixelsl制到屏q上。我们选择Windows XP默认的桌面背景Bliss.bmp作ؓ(f)l制的内容(如果你用的是Windows XPpȝQ很可能可以在硬盘(sh)搜烦(ch)到这个文件。当然你也可以用其它BMP文g来代替,只要它是24位的BMP文g。注意需要修改代码开始部分的FileName的定义)(j)Q先把该文g复制一份放到正的位置Q我们在E序开始时Q就d该文Ӟ从而获得图象的大小后,Ҏ(gu)该大来创徏合适的OpenGLH口Qƈl制像素?br>l制像素本来是很单的q程Q但是这个程序在骨架上与前面的各U示例程序稍有不同,所以我q是打算l出一份完整的代码?/p>
static GLint ImageHeight;
static GLint PixelLength;
static GLubyte* PixelData;
#include <stdlib.h>
{
// 清除屏幕q不必要
// 每次l制Ӟ画面都覆盖整个屏q?br> // 因此无论是否清除屏幕Q结果都一?br> // glClear(GL_COLOR_BUFFER_BIT);
glDrawPixels(ImageWidth, ImageHeight,
GL_BGR_EXT, GL_UNSIGNED_BYTE, PixelData);
glutSwapBuffers();
}
{
// 打开文g
FILE* pFile = fopen("Bliss.bmp", "rb");
if( pFile == 0 )
exit(0);
fread(&ImageWidth, sizeof(ImageWidth), 1, pFile);
fread(&ImageHeight, sizeof(ImageHeight), 1, pFile);
PixelLength = ImageWidth * 3;
while( PixelLength % 4 != 0 )
++PixelLength;
PixelLength *= ImageHeight;
PixelData = (GLubyte*)malloc(PixelLength);
if( PixelData == 0 )
exit(0);
fread(PixelData, PixelLength, 1, pFile);
fclose(pFile);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(ImageWidth, ImageHeight);
glutCreateWindow(FileName);
glutDisplayFunc(&display);
glutMainLoop();
// 实际上,glutMainLoop函数永远不会(x)q回Q这里也永远不会(x)到达
// q里写释攑ֆ存只是出于一U个Z(fn)?br> // 不用担心(j)内存无法释放。在E序l束时操作系l会(x)自动回收所有内?br> free(PixelData);
}
从效果上看,glCopyPixelsq行像素复制的操作,{h(hun)于把像素d到内存,再从内存l制到另一个区域,因此可以通过glReadPixels和glDrawPixelsl合来实现复制像素的功能。然而我们知道,像素数据通常数据量很大,例如一q?024*768的图象,如果使用24位BGR方式表示Q则需要至?024*768*3字节Q即2.25兆字节。这么多的数据要q行一ơ读操作和一ơ写操作Qƈ且因为在glReadPixels和glDrawPixels中设|的数据格式不同Q很可能涉及(qing)到数据格式的转换。这对CPU无疑是一个不的负担。用glCopyPixels直接从像素数据复制出新的像素数据Q避免了(jin)多余的数据的格式转换Qƈ且也可能减少一些数据复制操作(因ؓ(f)数据可能直接由显卡负责复Ӟ不需要经q主内存Q,因此效率比较高?br>glCopyPixels函数也通过glRasterPos*pd函数来设|绘制的位置Q因Z需要涉?qing)到d存,所以不需要指定数据在内存?sh)的格式Q也不需要用Q何指针?br>glCopyPixels函数有五个参敎ͼW一、二个参数表C复制像素来源的矩Ş的左下角坐标Q第三、四个参数表C复制像素来源的举行的宽度和高度Q第五个参数通常使用GL_COLORQ表C复制像素的颜色Q但也可以是GL_DEPTH或GL_STENCILQ分别表C复制深度缓冲数据或模板~冲数据?br>值得一提的是,glDrawPixels和glReadPixels中设|的各种操作Q例如glPixelZoom{,在glCopyPixels函数中同h效?br>下面看一个简单的例子Q绘制一个三角Ş后,复制像素Qƈ同时q行水^和垂直方向的{Q然后羃?yu)?f)原来的一半,q绘制。绘制完毕后Q调用前面的grab函数Q将屏幕中所有内容保存(sh)ؓ(f)grab.bmp。其中WindowWidth和W(xu)indowHeight是表C窗口宽度和高度的常量?/p>
{
// 清除屏幕
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(0.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(0.5f, 1.0f);
glEnd();
glPixelZoom(-0.5f, -0.5f);
glRasterPos2i(1, 1);
glCopyPixels(WindowWidth/2, WindowHeight/2,
WindowWidth/2, WindowHeight/2, GL_COLOR);
glutSwapBuffers();
grab();
}
BMP文g由文件头、位图信息头、颜色信息和囑Ş数据四部分组成?
BMP文g?
BMP文g头数据结构含有BMP文g的类型、文件大和位图起始位置{信息?
其结构定义如?
typedef struct tagBITMAPFILEHEADER
{
WORDbfType; // 位图文g的类型,必须为BM
DWORD bfSize; // 位图文g的大,以字节ؓ(f)单位
WORDbfReserved1; // 位图文g保留字,必须?
WORDbfReserved2; // 位图文g保留字,必须?
DWORD bfOffBits; // 位图数据的v始位|,以相对于位图
// 文g头的偏移量表C,以字节ؓ(f)单位
} BITMAPFILEHEADER;
BMP位图信息头数据用于说明位囄寸{信息?br> typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本结构所占用字节?br> LONGbiWidth; // 位图的宽度,以像素ؓ(f)单位
LONGbiHeight; // 位图的高度,以像素ؓ(f)单位
WORD biPlanes; // 目标讑֤的别,必须?
WORD biBitCount// 每个像素所需的位敎ͼ必须?(双色),
// 4(16?Q?(256??4(真彩?之一
DWORD biCompression; // 位图压羃cdQ必L 0(不压~?,
// 1(BI_RLE8压羃cd)?(BI_RLE4压羃cd)之一
DWORD biSizeImage; // 位图的大,以字节ؓ(f)单位
LONGbiXPelsPerMeter; // 位图水^分L率,每米像素?br> LONGbiYPelsPerMeter; // 位图垂直分L率,每米像素?br> DWORD biClrUsed;// 位图实际使用的颜色表中的颜色?br> DWORD biClrImportant;// 位图昄q程中重要的颜色?br> } BITMAPINFOHEADER;
颜色表用于说明位图中的颜Ԍ它有若干个表,每一个表Ҏ(gu)一个RGBQUADcd的结构,定义一U颜艌ӀRGBQUADl构的定义如?
typedef struct tagRGBQUAD {
BYTErgbBlue;// 蓝色的亮?D围ؓ(f)0-255)
BYTErgbGreen; // l色的亮?D围ؓ(f)0-255)
BYTErgbRed; // U色的亮?D围ؓ(f)0-255)
BYTErgbReserved;// 保留Q必Mؓ(f)0
} RGBQUAD;
颜色表中RGBQUADl构数据的个数有biBitCount来确?
当biBitCount=1,4,8Ӟ分别?,16,256个表?
当biBitCount=24Ӟ没有颜色表项?br> 位图信息头和颜色表组成位图信息,BITMAPINFOl构定义如下:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位图信息?br> RGBQUAD bmiColors[1]; // 颜色?br> } BITMAPINFO;
位图数据记录?jin)位囄每一个像素|记录序是在扫描行内是从左到?扫描行之间是从下C。位囄一个像素值所占的字节?
当biBitCount=1Ӟ8个像素占1个字?
当biBitCount=4Ӟ2个像素占1个字?
当biBitCount=8Ӟ1个像素占1个字?
当biBitCount=24?1个像素占3个字?
Windows规定一个扫描行所占的字节数必L
4的倍数(即以long为单?,不的以0填充Q?br> 一个扫描行所占的字节数计方?
DataSizePerLine= (biWidth* biBitCount+31)/8;
// 一个扫描行所占的字节?br> DataSizePerLine= DataSizePerLine/4*4; // 字节数必L4的倍数
位图数据的大?不压~情况下):
DataSize= DataSizePerLine* biHeight;
那么BMP文g中的像素逐个d数据~冲再显C,是否p正常昄?jin)呢Q显然不是的。前面说?jin),在文件开头有一块头部信息区Q如果把q些数据也一赯q缓Ԍ那么囑փ是无法最l显C的。因此,通常载入BMP囑փ的首要Q务是要知道这块头部信息区的大,也就是数据区的偏U量offsetQ再利用fseekd到这个位|,再进行逐个像素的解析。而头部信息区的大D似在头部信息区的某个字节中有的,只需要读取该字节Q便可知道。对?4位和32位的BMP囑փ而言Q这个offsetgؓ(f)0x36Q也是头部信息区的大小?4个字节?/p>
如果你要把加载图像的函数做的通用一些的话,那么囄的宽度和高度也是需要获取的Q对?4位BMP囑փQ这两个值可以分别在0x12?x16q行d得到Q注意fread需要读双字Q即4字节Q否则会(x)出错?/p>
对于24位BMP囑փ而言Q最需要注意的一Ҏ(gu)Q规定了(jin)每条行扫描线的数据大必L4的整数倍,如果不是4的整数倍,那么需要在行末端进行补0Q否则数据读取将出现偏移Q直接导致的l果Q会(x)加蝲Z张倾斜的图像。这个补0的操作叫做数据宽度对齐?/p>
很抽象?举个例子Q对?00×400?4位BMP囑փ而言Q行扫描?00pxQ是4的整数倍,那么无需q行?操作。如果是30×38Q本每张麻牌素材的尺寸)(j)Q那么行扫描昄不是4的整数倍。对?4位BMP而言Q一个像素由RGB三字节组成,那么一个行扫描的total字节?0 × 3 = 90字节Q进行数据宽度对齐之后,行扫描的实际total字节?0 × 3 + 2 = 92字节。如此,才能?整除。换句话_(d)每一行多?个无用的字节。这在编E中Q需要进行处理丢弃,否则囑փ再次变Ş?/p>
最后一个需要注意的细节是QBMP囑փ的原点坐标,都是以左下角为基准,向右、向上增加。所以,在编E时Q需要对y轴数据做一些小变换。否则将?x)得Cq颠倒的囑փQ此外,BMP的三原色序是BGRQ注意编E中的处理?/p>
原文地址QBMP文g的结构,从大的分cL看,可以分成2部分Q头部信息区和图像数据区。其中头部信息区中,保存?sh)(jin)图像的各种属性,如文件格式,囄宽度Q调色板{等。过?jin)头部信息区之后Q就是用来呈现图像的真正的数据区?jin)。BMP文g属于像素文gQ也是_(d)数据Z的数据,其实是记录?jin)图像中每一个像素的颜色Q以32位BMP囄来说Q每一个像素由ARGB四个字节保存光Ԍ其中A是透明度。那么,一?00×400?2位BMP囑փQ就?60000个像素组成,则,数据区大?160000×4byte = 640000byte = 640kbQ所以也不难怪,BMP囑փ文glh的印象就是文仉常大?
那么BMP文g中的像素逐个d数据~冲再显C,是否p正常昄?jin)呢Q显然不是的。前面说?jin),在文件开头有一块头部信息区Q如果把q些数据也一赯q缓Ԍ那么囑փ是无法最l显C的。因此,通常载入BMP囑փ的首要Q务是要知道这块头部信息区的大,也就是数据区的偏U量offsetQ再利用fseekd到这个位|,再进行逐个像素的解析。而头部信息区的大D似在头部信息区的某个字节中有的,只需要读取该字节Q便可知道。对?4位和32位的BMP囑փ而言Q这个offsetgؓ(f)0x36Q也是头部信息区的大小?4个字节?/p>
如果你要把加载图像的函数做的通用一些的话,那么囄的宽度和高度也是需要获取的Q对?4位BMP囑փQ这两个值可以分别在0x12?x16q行d得到Q注意fread需要读双字Q即4字节Q否则会(x)出错?/p>
对于24位BMP囑փ而言Q最需要注意的一Ҏ(gu)Q规定了(jin)每条行扫描线的数据大必L4的整数倍,如果不是4的整数倍,那么需要在行末端进行补0Q否则数据读取将出现偏移Q直接导致的l果Q会(x)加蝲Z张倾斜的图像。这个补0的操作叫做数据宽度对齐?/p>
很抽象?举个例子Q对?00×400?4位BMP囑փ而言Q行扫描?00pxQ是4的整数倍,那么无需q行?操作。如果是30×38Q本每张麻牌素材的尺寸)(j)Q那么行扫描昄不是4的整数倍。对?4位BMP而言Q一个像素由RGB三字节组成,那么一个行扫描的total字节?0 × 3 = 90字节Q进行数据宽度对齐之后,行扫描的实际total字节?0 × 3 + 2 = 92字节。如此,才能?整除。换句话_(d)每一行多?个无用的字节。这在编E中Q需要进行处理丢弃,否则囑փ再次变Ş?/p>
最后一个需要注意的细节是QBMP囑փ的原点坐标,都是以左下角为基准,向右、向上增加。所以,在编E时Q需要对y轴数据做一些小变换。否则将?x)得Cq颠倒的囑փQ此外,BMP的三原色序是BGRQ注意编E中的处理?/p>
原文地址Q?a >http://naozifangde.blog.163.com/blog/static/1280042642009101614938531/