Delphi使用OpenGL2d绘图之画图片Bmp的方法
Delphi结合OpenGL绘制BMP图片需额外下载Gl.pas及Glaux.pas辅助库。实现流程分加载BMP、转换为纹理、绘制三步。两种方法:glDrawPixels直接绘制,位置固定于窗口左下角;纹理映射可通过四边形控制位置与缩放,更灵活。利用TBitmap加载图片,注意数据格式为BGR。
用Delphi结合OpenGL做2D绘图,尤其是想把一张BMP图片画到屏幕上,这事儿说起来简单,但上手时总会遇到几个“坑”。首先,Delphi自带的OpenGL单元封装的是1.0版本,有些函数没声明,所以你得额外下载一个Gl.pas。另外,还需要一个Glaux.pas单元和对应的glaux.dll辅助库。这些资源网上都能找到,文末也提供了打包下载。
一、前言

对于Delphi来说,要画图片要先处理一下,需要引用别的单元,而Delphi中没带,需要另外下载Gl.pas。网上常见自带的OpenGl单元封装的是1.0版的,有此函数未声明。网上可以找到Gl.pas单元。另外需要一个Glaux.pas单元与glaux.dll,是辅助库。在本文最后会提供下载。
二、实现流程
那么,具体需要哪几步呢?Windows的绘图机制是基于位图的,所以不管是PNG还是JPG,最终都要转成BMP来操作。大体上分为三步:
1. 加载BMP图片:使用auxDIBImageLoadA或其他函数。
2. 转换为纹理:glGenTextures → glBindTexture → glTexImage2D,glTexParameteri用于设置相关参数。
3. 绘制纹理:glBindTexture → glBegin(GL_QUADS) → glTexCoord2f → glVertex2f → glEnd。
三、利用glDrawPixels函数绘图
先说说一种比较直接的方法——glDrawPixels。它的参数有五个:
width:图像的宽度height:图像的高度format:图像的数据存储格式atype:未知pixels:DIB数据的指针
示例代码如下:
procedure TForm1.Draw; var Bmp: TBitmap; begin Bmp := TBitmap.Create; Bmp.LoadFromFile(ExtractFilePath(ParamStr(0)) + '1.bmp'); // 清空缓冲区 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // TBitmap的图像数据在内存中是按行倒序连续存放的,通过TBitmap.ScanLine[TBitmap.Height-1]可以取得首地址即图像缓冲区地址 // bmp图片的颜色是按b g r存储的,所以要选 GL_BGR_EXT做为参数 glDrawPixels(Bmp.Width, Bmp.Height, GL_BGR_EXT, GL_UNSIGNED_BYTE, Bmp.ScanLine[Bmp.Height - 1]); SwapBuffers(FDC); Bmp.Free; end;
用以上方法绘制图片不需要启用纹理映射,可以通过glPixelZoom函数来缩放图片,显示位置在窗口的左下角(暂时不知道如何改变图像位置)。
三、使用纹理绘图
想要控制图片的显示位置与放大缩小,纹理映射是更灵活的方式。具体怎么做呢?
1. 把图片加载到程序里,获取相关的图片信息
将图片加载到纹理中,可参考这篇文章:https://www.jb51.net/article/52125.htm
在Delphi中加载一张位图很简单,有两种常见方式:
(1)通过辅助库的auxDIBImageLoadA函数加载图片,返回一个PTAUX_RGBImageRec数据指针,DIB数据格式为RGB。
// RGB数据的结构体 TAUX_RGBImageRec = record sizeX, sizeY: GLint; data: pointer; end; PTAUX_RGBImageRec = ^TAUX_RGBImageRec; var p: PTAUX_RGBImageRec; begin p := auxDIBImageLoadA(PAnsiChar(ExtractFilePath(ParamStr(0)) + '1.bmp')); // p 怎么释放? Dispose与Freemem都无法操作这个指针 end;
(2)通过TBitmap.LoadFromFile加载图片。这是Delphi自带的,从效率上对比,与auxDIBImageLoadA性能是一样的,但DIB数据格式为BGR,DIB指针为TBitmap.ScanLine[Bmp.Height - 1]。
var Bmp: TBitmap; begin Bmp := TBitmap.Create; TBitmap.LoadFromFile(ExtractFilePath(ParamStr(0)) + '1.bmp'); // do something // 用完释放 Bmp.Free; end;
2. 创建纹理,其中的glGenTextures与glBindTexture在Gl.pas中。
// 创建纹理区域 glGenTextures(1, @texture); // 绑定纹理区域 glBindTexture(GL_TEXTURE_2D, texture); // 使用位图创建图像纹理 glTexImage2D( GL_TEXTURE_2D, // 纹理是一个2D纹理 GL_TEXTURE_2D 0, // 图像的详细程度 默认 0 3, // 数据的成分数。因为图像是由红,绿,蓝三种组成 默认3 Bmp.Width, // 纹理的宽度 Bmp.Height, // 纹理的高度 0, // 边框的值 默认 0 GL_BGR_EXT, // 数据格式 bmp使用 bgr GL_UNSIGNED_BYTE, // 组成图像的数据是无符号字节类型的 Bmp.ScanLine[Bmp.Height - 1] // DIB数据指针 ); // 下面两行是让opengl在放大原始的纹理大(GL_TEXTURE_MAG_FILTER)或缩小原始纹理(GL_TEXTURE_MIN_FILTER)时OpenGL采用的滤波方式。 // GL_LINEAR 使用线性滤波,可以把图片处理处平滑,但需要更多的内存与CPU glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 线形滤波 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 线形滤波
3. 绘制纹理
绘制纹理之前,必须通知OpenGL开启纹理映射:glEnable(GL_TEXTURE_2D)。开启后,非纹理的绘制将不起作用。用完记得关闭。
// 以下是绘图,利用一个四边形,绘制图片 // 启用纹理映射 if glIsEnabled(GL_TEXTURE_2D) = 0 then glEnable(GL_TEXTURE_2D); // 清空缓冲区 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); l := 10; t := 10; w := 200; // 放大为200*200的图片 // 选择纹理 如果场景中使用多个纹理,不能在glBegin() 和 glEnd() 之间绑定纹理 glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_QUADS); // glTexCoord2f 的第一个参数是X坐标。 // 0.0是纹理的左侧。 0.5是纹理的中点, 1.0是纹理的右侧。 // glTexCoord2f 的第二个参数是Y坐标。 // 0.0是纹理的底部。 0.5是纹理的中点, 1.0是纹理的顶部。 glTexCoord2f(0, 1); glVertex2f(l, t); glTexCoord2f(1, 1); glVertex2f(l + w, t); glTexCoord2f(1, 0); glVertex2f(l + w, t + w); glTexCoord2f(0, 0); glVertex2f(l, t + w); glEnd();
以上绘制就结束了。下面是一份完整的Draw方法代码,可以不引用辅助库Glaux.pas:
procedure TForm1.Draw; var Bmp: TBitmap; texture: GLuint; l, t, w: Integer; begin Bmp := TBitmap.Create; Bmp.LoadFromFile(ExtractFilePath(ParamStr(0)) + '1.bmp'); // 创建纹理区域 glGenTextures(1, @texture); // 绑定纹理区域 glBindTexture(GL_TEXTURE_2D, texture); // 使用位图创建图像纹理 glTexImage2D( GL_TEXTURE_2D, // 纹理是一个2D纹理 GL_TEXTURE_2D 0, // 图像的详细程度 默认 0 3, // 数据的成分数。因为图像是由红,绿,蓝三种组成 默认3 Bmp.Width, // 纹理的宽度 Bmp.Height, // 纹理的高度 0, // 边框的值 默认 0 GL_BGR_EXT, // 数据格式 bmp使用 bgr GL_UNSIGNED_BYTE, // 组成图像的数据是无符号字节类型的 Bmp.ScanLine[Bmp.Height - 1] // DIB数据指针 ); // 下面两行是让opengl在放大原始的纹理大(GL_TEXTURE_MAG_FILTER)或缩小原始纹理(GL_TEXTURE_MIN_FILTER)时OpenGL采用的滤波方式。 // GL_LINEAR 使用线性滤波,可以把图片处理处平滑,但需要更多的内存与CPU glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 以下是绘图,利用一个四边形,绘制图片 // 启用纹理映射 if glIsEnabled(GL_TEXTURE_2D) = 0 then glEnable(GL_TEXTURE_2D); // 清空缓冲区 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); l := 10; t := 10; w := 200; // 放大为200*200的图片 // 选择纹理 如果场景中使用多个纹理,不能在glBegin() 和 glEnd() 之间绑定纹理 glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_QUADS); glTexCoord2f(0, 1); glVertex2f(l, t); glTexCoord2f(1, 1); glVertex2f(l + w, t); glTexCoord2f(1, 0); glVertex2f(l + w, t + w); glTexCoord2f(0, 0); glVertex2f(l, t + w); glEnd(); Bmp.Free; SwapBuffers(FDC); end;
本实例完整代码可点击此处下载。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















