使用Qt opengles 2.0模块绘制Gif等动态图片以实现简易的帧动画效果,这里程序使用的测试平台是Nokia N9,在其他的能够使用Qt OpenglES 2.0模块的移动终端上用法也是一样的。在这里主要分享一下绘制Gif的绘制思路。
在Qt中有一个QMovie的类,它可以解析动态图片,能够得到动态图片中的对应的每一帧的图片,以及能够获取每帧图片切换到下一帧的时间。这样就对绘制这个gif图片来说就已经够用了。首先用QMovie读取一个gif的图片,然后将获得的每一帧的图片绑定纹理得到一组纹理数组供绘制使用;最后根据帧图片切换的时间间隔来顺序这些图片即可。
在QMovie中,我们这里用到了3个主要的方法:
int QMovie::frameCount();--用来获取gif中图片的总数。
int QMovie::jumpToFrame(int frameNumber);--跳转到指定的帧数索引的那张图片(即是设定当前帧的索引)。
QImage QMovie::currentImage();--获得当前帧的图片,返回值是QImage。
程序见源代码, 部分代码如下所示:
qgifframeanimation.h:
#ifndef QGIFFRAMEANIMATION_H #define QGIFFRAMEANIMATION_H #include <QObject> #include <QMovie> #include <QImage> #include <QGLShaderProgram> #include <QTime> #include <qshapes.h> #include <QtOpenGL> class QGifFrameAnimation : public QObject { Q_OBJECT public: explicit QGifFrameAnimation(QObject *parent = 0); virtual ~QGifFrameAnimation(); void init(GLuint* tex, int frameCount, int frameInterval); void startAnima(); void drawAnimation(QGLShaderProgram& program, float depth); bool isCompleted(); private: bool m_isDraw; int m_curFrameIndex; int m_frameCount; int m_frameInterval; GLuint* m_tex; QTime m_frameTime; }; #endif // QGIFFRAMEANIMATION_H
qgifframeanimation.cpp:
#include "qgifframeanimation.h" QGifFrameAnimation::QGifFrameAnimation(QObject *parent) : QObject(parent) { m_curFrameIndex = 0; m_isDraw = false; } QGifFrameAnimation::~QGifFrameAnimation() { } void QGifFrameAnimation::init(GLuint *tex, int frameCount, int frameInterval) { m_tex = tex; m_frameCount = frameCount; m_frameInterval = frameInterval; } void QGifFrameAnimation::startAnima() { m_curFrameIndex = 0; m_isDraw = true; m_frameTime.start(); } void QGifFrameAnimation::drawAnimation(QGLShaderProgram &program, float depth) { if(!m_isDraw) return; if(m_frameTime.elapsed() > m_frameInterval) { if(m_curFrameIndex > m_frameCount) { m_isDraw = false; return; } m_curFrameIndex++; m_frameTime.start(); } QShapes::drawRect(m_tex[m_curFrameIndex], depth, program, 0, 0, 854, 480); } bool QGifFrameAnimation::isCompleted() { return (m_isDraw && m_curFrameIndex > m_frameCount); }
QGLWidget窗口类:
#ifndef QGLTESTWIDGET_H #define QGLTESTWIDGET_H #include <QGLWidget> #include <QGLShaderProgram> #include <QBasicTimer> #include <QMovie> #include <QTime> #include "qshapes.h" #include "qebtimewheel.h" #include "qgifframeanimation.h" class QGLTestWidget : public QGLWidget { Q_OBJECT public: explicit QGLTestWidget(const QGLFormat& format, QWidget *parent = 0); virtual ~QGLTestWidget(); private: void initializeGL(); void paintGL(); void resizeGL(int w, int h); void timerEvent(QTimerEvent *e); void mouseReleaseEvent(QMouseEvent *e); void initShaders(QGLShaderProgram *program, QString vshader, QString fshader); void initTextures(); void loadTextures(QString texPath, GLuint &tex); void loadStartScreenAnimaTextures(); void renderScore(int score, float depth); void drawStartScreenAnimation(); private: QGLShaderProgram *m_program; QMatrix4x4 m_projection; QBasicTimer m_timer; QMovie* m_startScreenMovie; GLuint* m_gifTex; QGifFrameAnimation* m_startScreenAnima; }; #endif // QGLTESTWIDGET_H
cpp:
#include "qgltestwidget.h" #include <QDebug> const QString ImagesForTest[] = { ":/images/animation_test_1.gif", }; enum EIMAGEINDEX { animationTest1Index, }; QGLTestWidget::QGLTestWidget(const QGLFormat &format, QWidget *parent) : QGLWidget(format, parent) { startTimer(1000.0f / 12); m_program = new QGLShaderProgram(context()); m_timer.start(1000.0f / 100, this); m_startScreenMovie = new QMovie(ImagesForTest[animationTest1Index]); m_startScreenAnima = new QGifFrameAnimation(); } QGLTestWidget::~QGLTestWidget() { m_program->deleteLater(); } void QGLTestWidget::initializeGL() { initShaders(m_program, ":/shaders/v_shader.vsh", ":/shaders/f_shader.fsh"); initTextures(); m_program->bind(); m_startScreenAnima->init(m_gifTex, m_startScreenMovie->frameCount(), m_startScreenMovie->nextFrameDelay()); glEnable(GL_TEXTURE_2D); glEnable(GL_CULL_FACE); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); } void QGLTestWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT); m_program->setUniformValue("u_mvpMatrix", m_projection); m_startScreenAnima->drawAnimation(*m_program, 0.0f); } void QGLTestWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); m_projection.setToIdentity(); m_projection.ortho(0, w, h, 0, -100, 100); m_program->setUniformValue("u_mvpMatrix", m_projection); } void QGLTestWidget::timerEvent(QTimerEvent *e) { Q_UNUSED(e); updateGL(); } void QGLTestWidget::mouseReleaseEvent(QMouseEvent *e) { Q_UNUSED(e); m_startScreenAnima->startAnima(); } void QGLTestWidget::initShaders(QGLShaderProgram *program, QString vshader, QString fshader) { setlocale(LC_NUMERIC, "C"); program->addShaderFromSourceFile(QGLShader::Vertex, vshader); program->addShaderFromSourceFile(QGLShader::Fragment, fshader); program->link(); program->bind(); setlocale(LC_ALL, ""); } void QGLTestWidget::loadTextures(QString texPath, GLuint &tex) { glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); tex = bindTexture(QImage(texPath)); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } void QGLTestWidget::loadStartScreenAnimaTextures() { m_gifTex = new GLuint[m_startScreenMovie->frameCount()]; for(int i = 0; i < m_startScreenMovie->frameCount(); i++) { m_startScreenMovie->jumpToFrame(i); glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); m_gifTex[i] = bindTexture(m_startScreenMovie->currentImage()); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } } void QGLTestWidget::initTextures() { loadStartScreenAnimaTextures(); }