【Qt OpenGL教程】29:Blitter函数

第29课:Blitter函数 (参照NeHe)

这次教程中,我们将介绍类似于DirectDraw的blit(其实blit函数在许多绘图库都有),我们将用代码自己来实现它。它的作用非常简单,就是把一块纹理的贴到另一块纹理上。想想,有了这个函数,我们就可以自由拼接纹理了,是不是很棒?

这一课中,我们不但会实现blit函数,我们还将讲解如何来加载特定的*.raw图片。raw格式的图片,可以理解为CMOS或者CCD图像感应器将捕捉到的光源信号转化为数字信号的原始数据。可悲的是,Qt并没有提供加载raw图片的方法,所以我们只能自己写了(这是处理图像的一课,与OpenGL关系不大)!

程序运行时效果如下:

下面进入教程:

我们这次将在第06课的基础上修改代码,我们只讲解新的内容,旧的内容到这里大家应该掌握得很好了才对。首先打开myglwidget.h文件,将类声明修改如下:

#ifndef MYGLWIDGET_H#define MYGLWIDGET_H#include <QWidget>#include <QGLWidget>typedef struct Texture_Image//图像结构体{int width;int height;int format;//格式(图像每一像素内存)unsigned char *data;//储存图像数据}* P_TEXTURE_IMAGE;class MyGLWidget : public QGLWidget{Q_OBJECTpublic:explicit MyGLWidget(QWidget *parent = 0);~MyGLWidget();protected://对3个纯虚函数的重定义void initializeGL();void resizeGL(int w, int h);void paintGL();void keyPressEvent(QKeyEvent *event);//处理键盘按下事件private://为图像结构体分配内存P_TEXTURE_IMAGE allocateTextureBuffer(GLuint w, GLuint h, GLuint f);void deallocateTexture(P_TEXTURE_IMAGE t);//释放图像结构体内存//读取图像结构体数据void loadRAWData(const char *filename, P_TEXTURE_IMAGE buffer);GLuint buildTexture(P_TEXTURE_IMAGE tex);//建立纹理//将一个纹理贴到另一个纹理上void blit(P_TEXTURE_IMAGE src, P_TEXTURE_IMAGE dst, int src_xstart,int src_ystart, int src_width, int src_height,int dst_xstart, int dst_ystart, bool blend, int alpha);private:bool fullscreen;//是否全屏显示GLfloat m_xRot;//绕x轴旋转的角度GLfloat m_yRot;//绕y轴旋转的角度GLfloat m_zRot;//绕z轴旋转的角度P_TEXTURE_IMAGE t1, t2;//图像结构体指针GLuint m_Texture;//储存一个纹理};#endif // MYGLWIDGET_H首先我们新定义了一个结构体Texture_Image,把其指针重命名为P_TEXTURE_IMAGE。结构体中width、height指图像像素的宽和高;format指每一个像素的位深,也就是每一个像素所占的内存大小;data指向用于储存图像数据的内存。接着我们定义了该结构体的两个指针t1、t2。最后,我们声明了5个新的函数,函数的作用大家先看注释,后面定义的时候再一起解释。

接下来,我们需要打开myglwidget.cpp,加上声明#include <QMessageBox>,对构造函数进行修改,很简单不多解释,具体代码如下:

MyGLWidget::MyGLWidget(QWidget *parent) :QGLWidget(parent){fullscreen = false;m_xRot = 0.0f;m_yRot = 0.0f;m_zRot = 0.0f;QTimer *timer = new QTimer(this);//创建一个定时器//将定时器的计时信号与updateGL()绑定connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));timer->start(10);//以10ms为一个计时周期}

下面,我们来看allocateTextureBuffer()、deallocateTexture()函数的定义,具体代码如下:

P_TEXTURE_IMAGE MyGLWidget::allocateTextureBuffer(GLuint w, GLuint h, GLuint f){P_TEXTURE_IMAGE ti = NULL;unsigned char *c = NULL;ti = (P_TEXTURE_IMAGE)malloc(sizeof(Texture_Image));//分配图像结构体内存if (ti != NULL){ti->width = w;//设置宽度ti->height = h;//设置高度ti->format = f;//设置格式(位深/8)c = (unsigned char *)malloc(w * h *f);//分配w*h*f字节来存放图像数据if (c != NULL ){ti->data = c;}else{QMessageBox::warning(this, "内存不足", "分配图像内存错误", QMessageBox::Ok);exit(1);}}else{QMessageBox::warning(this, "内存不足", "分配图像结构体内存错误", QMessageBox::Ok);exit(1);}return ti;//返回图像结构体指针}void MyGLWidget::deallocateTexture(P_TEXTURE_IMAGE t){if (t != NULL){if (t->data != NULL){free(t->data);//释放存放图像数据的内存}free(t);//释放图像结构体的内存}}

首先是allocateTextureBuffer()函数,这个函数用来为图像结构体分配内存。函数中,我们为ti和c分别分配内存,其中为c分配时,其大小是w*h*f;然后如果分配不成功,则利用QMessageBox来报告错误,并退出程序。然后是deallocateTexture()函数,,这个函数用来释放我们分配的内存。函数中,我们把t和data指向的内存都释放掉。

继续,我们来看loadRAWData()和buildTexture()函数的定义,具体代码如下:

void MyGLWidget::loadRAWData(const char *filename, P_TEXTURE_IMAGE buffer){int stride = buffer->width * buffer->format;//记录每一行的字节数FILE *f = fopen(filename, "rb");//打开文件if (f != NULL)//如果文件存在{for (int i=buffer->height-1; i>=0; i–)//循环所有的行,从最下面的行开始读入{unsigned char *p = buffer->data + (i*stride);for (int j=0; j<buffer->width; j++)//读取每一行的数据{for (int k=0; k<buffer->format-1; k++, p++){*p = fgetc(f);//读取一个字节}*p = 255;//把255储存在alpha通道中p++;}}fclose(f);//关闭文件}else{QMessageBox::warning(this, "不能打开文件", "图像错误", QMessageBox::Ok);exit(1);}}GLuint MyGLWidget::buildTexture(P_TEXTURE_IMAGE tex){GLuint texture;glGenTextures(1, &texture);//创建纹理空间,并记录其地址glBindTexture(GL_TEXTURE_2D, texture);//绑定纹理//设置过滤器为线性过滤glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//在内存中创建一个纹理gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, tex->width, tex->height,GL_RGBA, GL_UNSIGNED_BYTE, tex->data);return texture;//返回纹理地址}心中有愿望一定要去闯,努力实现最初的梦想,

【Qt OpenGL教程】29:Blitter函数

相关文章:

你感兴趣的文章:

标签云: