C++ QT生成GIF,处理原始图像RGBA数据,窗口生成简单的动画

发布于:2025-05-24 ⋅ 阅读:(21) ⋅ 点赞:(0)
1.窗口自己的 paint事件 使用QPainter
2.使用gif.h 生成 gif
3.处理原始图像RGBA数据
4.动画代码实现就是曲线函数,三角函数,x,y坐标位置变化,然后更新显示
5.注意窗口 x,y轴 横是 x ,竖是 y,向下为正,向右为正!
6.坐标和函数方程带入计算,图形轨迹没问题,不会溢出。

//黄色小球运动就是 x,y轴使用三角函数,来回运行,
//三角函数无论多大的数都会转化到 -1~1之间

for (int i = 0; i < 1000; i++) {
        auto f = new Food(nextRandom(800),nextRandom(600),10,10,":/pic/bean.png");
        foods.push_back(f);

        f->mf.initialParams.push(nextRandom(30));
        f->mf.initialParams.push(nextRandom(10));
        f->mf.initialParams.push(nextRandom(30));
        f->mf.initialParams.push(nextRandom(10));
        f->mf.initialParams.push(nextRandom(60));

        f->mf.f = [f, d]() {
            static double t = 0;
            double A = f->mf.initialParams[0];
            double w = f->mf.initialParams[1];
            double q = f->mf.initialParams[2];
            double a = f->mf.initialParams[3];
            auto freq = f->mf.initialParams[4];


            f->ry() = f->rOriginY() +A*qCos(2*M_PI*w*freq*t+q)+a;

            f->rx() = f->rOriginX()
                    +nextRandom(30)
                    *qSin(2*M_PI*nextRandom(12)*freq*t
                          +nextRandom(30))
                    +nextRandom(10);

            t += d;
        };
    }

 

//定时器 每隔一段时间 计算 小球的位置,然后 发送更新请求 窗口,窗口刷新显示


connect(&heroTimer, &QTimer::timeout, [this, getRgbData, getImgData2]() {
        //        static double angle = 20;
        //        int a = 100;
        //        int x = a*qPow(qCos(qDegreesToRadians(angle)),3) + 300;
        //        int y = a*qPow(qSin(qDegreesToRadians(angle)),3) + 300;

        //        hero->setPos(x,y);

        //        angle += 5;

        for(auto &food:foods){
            food->mf.f();
            //            qDebug() << food->rx() << "," << food->ry();
        }

        update();

        if (frames > fIdx) {
            g.GifWriteFrame(
                &writer,
                //                            grab().toImage().convertToFormat(QImage::Format_ARGB32).bits(),
                //getRgbData(
                //  grab().toImage()), //必须要自己转化下to data原始数据,img的bits()接口数据有问题
                getImgData2(grab().toImage()), //Ok
                //grab().toImage().bits(),//blue ? what's wrong?
                width(),
                height(),
                2);
            fIdx++;
        } else {
            g.GifEnd(&writer);
            if (imgData) {
                delete[] imgData;
                imgData = nullptr;
                qDebug() << "gif grab window over";
            }
        }
    });

    heroTimer.start();

//QT接口自带的 原始数据接口返回数据始终输出偏蓝色的图片,
//自己转化后的 图像 data 数组就没问题
//没有特殊计算 就是 一个数组 复制 到另一个数组
//奇怪的很!!!

//必须要自己转化下to data原始数据,img的bits()接口数据有问题
    //原始QImage bits() 数据输出颜色不对!!!
    int dataSize = width() * height() * 4;
    imgData = new uint8_t[dataSize];
    auto getRgbData = [this, dataSize](auto img) -> auto
    {
        //zero data array
        memset(imgData, 0, dataSize);

        // 2. 遍历像素
        for (int y = 0; y < img.height(); y++) {
            for (int x = 0; x < img.width(); x++) {
                // 3. 获取像素的 QRgb 值
                QRgb pixel = img.pixel(x, y);

                // RGBA A is last!!!
                // 4. 提取 R, G, B 分量
                int r = qRed(pixel);
                int g = qGreen(pixel);
                int b = qBlue(pixel);

                int idx = y * img.width() + x;
                idx = idx * 4; //RGBA 4 bytes

                imgData[idx] = r;
                imgData[idx + 1] = g;
                imgData[idx + 2] = b;
                imgData[3 + idx] = qAlpha(pixel); //
                                                  //                qDebug() << idx;
                // 5. 打印或处理 RGB
                //                qDebug() << "Pixel at (" << x << "," << y << "): R=" << r << " G=" << g
                //                         << " B=" << b;
            }
        }
        return imgData;
    };

    //OK two!!
    auto getImgData2 = [this, dataSize](auto image) -> auto
    {
        //zero data array
        memset(imgData, 0, dataSize);

        const uchar *bits = image.bits(); // 获取原始数据指针
        int width = image.width();
        int height = image.height();
        int bytesPerLine = image.bytesPerLine(); // 每行字节数

        for (int y = 0; y < height; y++) {
            const QRgb *line = reinterpret_cast<const QRgb *>(bits + y * bytesPerLine);
            for (int x = 0; x < width; x++) {
                QRgb pixel = line[x];
                int r = qRed(pixel);
                int g = qGreen(pixel);
                int b = qBlue(pixel);

                // 处理 RGB 数据...
                int idx = y * image.width() + x;
                idx = idx * 4; //RGBA 4 bytes

                imgData[idx] = r;
                imgData[idx + 1] = g;
                imgData[idx + 2] = b;
                imgData[3 + idx] = qAlpha(pixel); //
            }
        }
        return imgData;
    };
//先计算位置和速度等等非RGBA值(think),再刷新显示!!!
void Widget::paintEvent(QPaintEvent *e){
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);



    for(auto &f:foods){
        f->draw(p);
    }

    for(auto &e:enemies){
        e->draw(p);
    }

    hero->draw(p);
}
//设置移动方向,速度


void Widget::keyPressEvent(QKeyEvent *e)
{
    int sd = 20;
    int k = e->key();
    switch (k) {
    case Qt::Key_W:
        hero->setDirection(Direction::UP,sd);
        break;
    case Qt::Key_S:
        hero->setDirection(Direction::DOWN,sd);
        break;
    case Qt::Key_A:
        hero->setDirection(Direction::LEFT,sd);
        break;
    case Qt::Key_D:
        hero->setDirection(Direction::RIGHT,sd);
        break;
    default:
        break;
    }

    update();
}
//实体类
//.h
#ifndef PACMAN_H
#define PACMAN_H

#include <QObject>
#include<QPainter>
#include<QStack>
//#include<iostream>
#include<functional>
//#include<QFunctionPointer>
#include<QHash>
using namespace std;

enum class Direction:int{
    UP,
    DOWN,
    LEFT,
    RIGHT
};

template<class T>
struct MotionFunction
{
public:
    int x;
    int y;
    function<T> f;
    QStack<double> initialParams;
};


class Pacman:public QObject
{
    Q_OBJECT
public:
    Pacman(int x,int y,int w,int h,QString pic);

    virtual void draw(QPainter& p);

    virtual void setPos(int x,int y);

    virtual int &rx();
    virtual int &ry();

    virtual int &rOriginX();
    virtual int &rOriginY();

    virtual void setFace(const Direction &d,QPixmap pic);

    virtual void setDirection(const Direction &d,int distance);

    virtual QHash<Direction,QPixmap> &face();

public:
    MotionFunction<void()> mf;



protected:
    QPixmap pic;
//    QString pic;
    int originX;
    int originY;
    int x;
    int y;
    int width;
    int height;
    Direction direction;


    QHash<Direction,QPixmap> faces;


};

uint qHash(const Direction &d,uint seed);


class Enemy:public Pacman{
    Q_OBJECT
public:
    Enemy(int x,int y,int w,int h,QString pic);
};

class Food:public Pacman{
    Q_OBJECT
public:
    Food(int x,int y,int w,int h,QString pic);



};


#endif // PACMAN_H
//.cpp
#include "pacman.h"

Pacman::Pacman(int x,int y,int w,int h,QString pic):
    x(x)
  ,y(y)
  ,width(w)
  ,height(h)
  ,pic(pic)
  ,direction(Direction::RIGHT)
  ,originX(x)
  ,originY(y)
{

}


void Pacman::draw(QPainter& p){

    p.drawPixmap(x,y,width,height,pic);
}

void Pacman::setPos(int x,int y){
    this->x = x;
    this->y = y;
}

int &Pacman::rx(){
    return x;
}

int &Pacman::ry(){
    return y;
}

int &Pacman::rOriginX(){
    return originX;
}

int &Pacman::rOriginY(){
    return originY;
}


void Pacman::setFace(const Direction &d,QPixmap pic){
    faces[d] = pic;
}

void Pacman::setDirection(const Direction &d,int distance){
    this->pic = faces[d];
    switch (d) {
    case Direction::UP:
        y -= distance;
        break;
    case Direction::DOWN:
        y+=distance;
        break;
    case Direction::LEFT:
        x -= distance;
        break;
    case Direction::RIGHT:
        x += distance;
        break;
    default:
        break;
    }
}


QHash<Direction,QPixmap> &Pacman::face(){
    return faces;
}

uint qHash(const Direction &d,uint seed){
    return static_cast<int>(d) + seed;
}



Enemy::Enemy(int x,int y,int w,int h,QString pic)
    :Pacman(x,y,w,h,pic)
{

}

Food::Food(int x,int y,int w,int h,QString pic)
    :Pacman(x,y,w,h,pic)
{

}


子类可以继承父类的函数和属性,代码复用非常方便

不支持反射的编程语言,只能通过其他工具(程序)生成实体类,
RPC,序列化,等等都是使用工具生成代码,可以方便代码编写,提高编写代码的速度。

 

 


网站公告

今日签到

点亮在社区的每一天
去签到