当前位置: 首页 > article >正文

比QT更高效的一款开源嵌入式图形工具EGT-Ensemble Graphics Toolkit

文章目录

    • EGT-Ensemble Graphics Toolkit介绍
    • EGT具备非常高的图形渲染效率
      • EGT采用了非常优秀的开源2D图形处理引擎-Cairo
      • 开源2D图形处理引擎Cairo的优势
      • Cairo 2D图像引擎的性能
      • Cairo 2D图像引擎的实际应用案例
      • 彩蛋 - 开源EDA软件KiCAD也在使用Cairo
    • EGT高效的秘诀还有哪些
      • Cairo需要依赖Pixman
      • Pixman针对不同平台有优化
    • EGT vs QT5实际效果PK
    • 代码贴图

很多介绍资料直接来自豆包,仅代表个人意见和理解,不喜勿喷

  • EGT-Ensemble Graphics Toolkit

EGT-Ensemble Graphics Toolkit介绍

The Ensemble Graphics Toolkit (EGT)是MIcrochip针对旗下ARM9、SAMA5处理器推出来的一款运行于嵌入式Linux的C++ GUI开发工具套件。EGT(嵌入式图形工具包)提供了现代化的图形用户界面(GUI)功能、外观样式,并在嵌入式 Linux 应用中尽可能贴近底层硬件的同时最大限度地提升性能。关键词是开源、免费商用

官方介绍可以点击这里
官方给出的EGT组件依赖框图

EGT具备非常高的图形渲染效率

EGT采用了非常优秀的开源2D图形处理引擎-Cairo

在这里插入图片描述
在这里插入图片描述

开源2D图形处理引擎Cairo的优势

在这里插入图片描述
在这里插入图片描述

Cairo 2D图像引擎的性能

在这里插入图片描述

Cairo 2D图像引擎的实际应用案例

在这里插入图片描述
在这里插入图片描述

彩蛋 - 开源EDA软件KiCAD也在使用Cairo

在这里插入图片描述

EGT高效的秘诀还有哪些

Cairo使用Pixman来加速底层像素的操作,Pixman能够提供图像的合成、alpha 通道处理、色彩空间转换等基本的像素级别的操作

Cairo需要依赖Pixman

在这里插入图片描述

Pixman针对不同平台有优化

Pixman针对ARM SIMD架构、带NEON或者MIPS、X86等架构,都有专门针对性的优化代码,来尽可能利用处理器硬件特性,加速像素的处理速度在这里插入图片描述

EGT vs QT5实际效果PK

在Microchip SAMA5D27开发板上,跑Microchip EGT提供的潜水员例程,该例程会有潜水员的动态图片,同时也有2条鱼在界面上从左到右在游动,另外还不断有泡泡从海底喷出。同时将该例程功能移植到QT5上,然后两者在实际硬件上运行,对比CPU占用率的差异
在这里插入图片描述
视频对比
基于EGT开发的demo,全程CPU占用率在22%左右,而QT5基本都在60%以上

代码贴图

个人对QT开发不是很熟悉,欢迎提出更好的优化方案
以下是QT5

#include <QApplication>
#include <QPainter>
#include <QTimer>
#include <QTime>
#include <QLabel>
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , fishFrame(0)
    , fish2Frame(0)
    , diverFrame(0)
    , bubblePixmap(":/images/smallbubble.png")
{
    ui->setupUi(this);

    // Load fish images and split them into 6 parts
    QPixmap fishPixmap(":/images/fish.png");
    QPixmap fish2Pixmap(":/images/fish2.png");
    QPixmap diverPixmap(":/images/diver.png");

    int frameWidth  = fishPixmap.width() /4;
    int frameHeight = fishPixmap.height()/2;

    for (int j = 0; j < 2; j++) {
        for (int i = 0; i < 4; ++i) {
            fishPixmaps.push_back(fishPixmap.copy(i * frameWidth, frameHeight * j, frameWidth, frameHeight));
        }
    }

    frameWidth  = fish2Pixmap.width() /2;
    frameHeight = fish2Pixmap.height()/3;

    for (int j = 0; j < 3; j++) {
        for (int i = 0; i < 2; ++i) {
            fish2Pixmaps.push_back(fish2Pixmap.copy(i * frameWidth, frameHeight * j, frameWidth, frameHeight));
        }
    }

    frameWidth  = diverPixmap.width();
    frameHeight = diverPixmap.height()/16;

    for (int i = 0; i < 16; ++i) {
        diverPixmaps.push_back(diverPixmap.copy(0, frameHeight * i, frameWidth, frameHeight));
    }

    // Set the background image
    ui->backgroundLabel->setPixmap(QPixmap(":/images/water_1080.png"));
    ui->backgroundLabel->setScaledContents(true);

    // Set the initial fish images
    ui->fishLabel->setPixmap(fishPixmaps[0]);
    ui->fishLabel->setScaledContents(true);

    ui->fish2Label->setPixmap(fish2Pixmaps[0]);
    ui->fish2Label->setScaledContents(true);

    // Set the diver image
    ui->diverLabel->setPixmap(diverPixmaps[0]);
    ui->diverLabel->setScaledContents(true);

    // Create timers for moving the fish
    moveTimer = new QTimer(this);
    connect(moveTimer, &QTimer::timeout, this, &MainWindow::moveFish);
    moveTimer->start(50);

    moveTimer2 = new QTimer(this);
    connect(moveTimer2, &QTimer::timeout, this, &MainWindow::moveFish2);
    moveTimer2->start(50);

    // Create timers for animating the fish
    animateTimer = new QTimer(this);
    connect(animateTimer, &QTimer::timeout, this, &MainWindow::animateFish);
    animateTimer->start(100);

    animateTimer2 = new QTimer(this);
    connect(animateTimer2, &QTimer::timeout, this, &MainWindow::animateFish2);
    animateTimer2->start(100);

    // Create timers for animating the diver
    animateTimer3 = new QTimer(this);
    connect(animateTimer3, &QTimer::timeout, this, &MainWindow::animateDiver);
    animateTimer3->start(100);

    cpuTimer = new QTimer(this);
    connect(cpuTimer, &QTimer::timeout, this, &MainWindow::updateClock);
    cpuTimer->start(1000); // 每秒更新一次时钟

    // Create timer for creating bubbles
    bubbleTimer = new QTimer(this);
    connect(bubbleTimer, &QTimer::timeout, this, &MainWindow::createBubble);
    bubbleTimer->start(1000);

    // 显示 CPU 使用率的标签
    cpuLabel = new QLabel(this);
    cpuLabel->setGeometry(580, 5, 200, 40);
    cpuLabel->setStyleSheet("font-size: 20px; color: red;");
    cpuLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

    cpuUsage = new CPUUsage(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::moveFish()
{
    int x = ui->fishLabel->x() + 5;
    if (x > this->width()) {
        x = -ui->fishLabel->width();
        int y = QRandomGenerator::global()->bounded(this->height() - ui->fishLabel->height());
        ui->fishLabel->move(x, y);
    } else {
        ui->fishLabel->move(x, ui->fishLabel->y());
    }
}

void MainWindow::moveFish2()
{
    int x = ui->fish2Label->x() + 5;
    if (x > this->width()) {
        x = -ui->fish2Label->width();
        int y = QRandomGenerator::global()->bounded(this->height() - ui->fish2Label->height());
        ui->fish2Label->move(x, y);
    } else {
        ui->fish2Label->move(x, ui->fish2Label->y());
    }
}

void MainWindow::animateFish()
{
    fishFrame = (fishFrame + 1) % fishPixmaps.size();
    ui->fishLabel->setPixmap(fishPixmaps[fishFrame]);
}

void MainWindow::animateFish2()
{
    fish2Frame = (fish2Frame + 1) % fish2Pixmaps.size();
    ui->fish2Label->setPixmap(fish2Pixmaps[fish2Frame]);
}

void MainWindow::animateDiver()
{
    diverFrame = (diverFrame + 1) % diverPixmaps.size();
    ui->diverLabel->setPixmap(diverPixmaps[diverFrame]);
}

void MainWindow::updateClock()
{
    cpuLabel->setText(QString("CPU Usage: %1%").arg(cpuUsage->getCPUUsage(), 0, 'f', 2));
    update();
}

void MainWindow::createBubble()
{
    int bubbleCount = QRandomGenerator::global()->bounded(1, 4); // Random number of bubbles between 1 and 5
    for (int i = 0; i < bubbleCount; ++i) {
        int x = QRandomGenerator::global()->bounded(this->width());
        int y = this->height();
        int size = QRandomGenerator::global()->bounded(5, 32); // Random size between 10 and 50
        int xspeed = 0;
        int yspeed = -QRandomGenerator::global()->bounded(5, 20); // Random speed between 1 and 10
        Bubble* bubble = new Bubble(bubblePixmap, xspeed, yspeed, QPoint(x, y), size, this);
        bubbles.push_back(bubble);
        bubble->show();
        if (bubble->getcount() >= 20) {
            break;
        }
    }
}

以下是EGT

/*
 * Copyright (C) 2018 Microchip Technology Inc.  All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <chrono>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <egt/ui>
#include <random>
#include <sstream>
#include <string>
#include <vector>

using namespace std;
using namespace egt;

class Bubble : public ImageLabel
{
public:
    Bubble(int xspeed, int yspeed, const Point& point) noexcept
        : ImageLabel(Image("file:smallbubble.png"), "", Rect(point, Size())),
          m_xspeed(xspeed),
          m_yspeed(yspeed)
    {
        flags().set(Widget::Flag::no_layout);
    }

    Bubble(const Bubble&) = default;
    Bubble(Bubble&&) = default;
    Bubble& operator=(const Bubble&) = default;
    Bubble& operator=(Bubble&&) = default;
    virtual ~Bubble() = default;

    bool animate()
    {
        bool visible = Rect(Point(0, 0), Application::instance().screen()->size()).intersect(box());

        if (visible)
        {
            Point to(box().point());
            to += Point(m_xspeed, m_yspeed);
            move(to);
        }

        return visible;
    }

private:

    Bubble() = delete;

    int m_xspeed;
    int m_yspeed;
};

class MainWindow : public TopWindow
{
public:
    MainWindow()
        : TopWindow(Size()),
          e1(r())
    {
        background(Image("file:water.png"));

        m_label = make_shared<Label>("Objects: 0",
                                     Rect(Point(10, 10),
                                          Size(150, 40)),
                                     AlignFlag::left | AlignFlag::center_vertical);
        m_label->color(Palette::ColorId::text, Palette::white);
        m_label->color(Palette::ColorId::bg, Palette::transparent);
        add(top(left(m_label)));

        m_sprite = make_shared<Sprite>(Image("file:diver.png"), Size(390, 312), 16, Point(0, 0));
        m_sprite->no_layout(true);
        add(m_sprite);
        m_sprite->show();
    }

    void handle(Event& event) override
    {
        TopWindow::handle(event);

        switch (event.id())
        {
        case EventId::raw_pointer_move:
            spawn(display_to_local(event.pointer().point));
            break;
        default:
            break;
        }
    }

    void spawn(const Point& p)
    {
        auto xspeed = 0;
        auto yspeed = speed_dist(e1);
        auto offset = offdist(e1);
        auto size = size_dist(e1);

        // has to move at some speed
        if (yspeed == 0)
            yspeed = 1;

        m_images.emplace_back(make_shared<Bubble>(xspeed, yspeed, p));
        auto& image = m_images.back();
        add(image);
        image->image_align(AlignFlag::expand_horizontal | AlignFlag::expand_vertical);
        image->resize_by_ratio(size);
        image->move(Point(p.x() - image->box().width() / 2 + offset,
                          p.y() - image->box().height() / 2 + offset));
        objects_changed();
    }

    void animate()
    {
        for (auto x = m_images.begin(); x != m_images.end();)
        {
            auto& image = *x;
            if (!image->animate())
            {
                image->detach();
                x = m_images.erase(x);
                objects_changed();
            }
            else
            {
                x++;
            }
        }
    }

    void objects_changed()
    {
        ostringstream ss;
        ss << "Objects: " << m_images.size();
        m_label->text(ss.str());
    }

    vector<shared_ptr<Bubble>> m_images;
    shared_ptr<Label> m_label;
    shared_ptr<Sprite> m_sprite;

    std::random_device r;
    std::default_random_engine e1;
    std::uniform_int_distribution<int> speed_dist{-20, -1};
    std::uniform_int_distribution<int> offdist{-20, 20};
    std::uniform_int_distribution<int> size_dist{10, 100};
};

int main(int argc, char** argv)
{
    Application app(argc, argv, "water");
#ifdef EXAMPLEDATA
    add_search_path(EXAMPLEDATA);
#endif
    MainWindow win;

    vector<Sprite*> sprites;

#define SPRITE1
#ifdef SPRITE1
    Sprite sprite1(Image("file:fish.png"), Size(252, 209), 8, Point(0, 0));
    sprite1.no_layout(true);
    win.add(sprite1);
    sprite1.show();
    sprites.push_back(&sprite1);
#endif

#define SPRITE2
#ifdef SPRITE2
    Sprite sprite2(Image("file:fish2.png"), Size(100, 87), 6, Point(0, 0));
    sprite2.no_layout(true);
    win.add(sprite2);
    sprite2.show();
    sprites.push_back(&sprite2);
#endif

    sprites.push_back(win.m_sprite.get());

    PeriodicTimer animatetimer(std::chrono::milliseconds(30));
    animatetimer.on_timeout([&win]()
    {
        win.animate();
    });
    animatetimer.start();

    PeriodicTimer animatetimer2(std::chrono::milliseconds(100));
    animatetimer2.on_timeout([&sprites]()
    {
        for (auto& sprite : sprites)
            sprite->advance();
    });
    animatetimer2.start();

    PeriodicTimer spawntimer(std::chrono::seconds(1));
    spawntimer.on_timeout([&win]()
    {
        if (win.m_images.size() > 30)
            return;

        static std::uniform_int_distribution<int> xoffdist(-win.width() / 2, win.width() / 2);
        int offset = xoffdist(win.e1);

        static std::uniform_int_distribution<int> count_dist(1, 10);
        int count = count_dist(win.e1);

        Point p(win.box().center());
        p.y(win.box().height());
        p.x(p.x() + offset);

        while (count--)
            win.spawn(p);
    });
    spawntimer.start();

#ifdef SPRITE1
    PropertyAnimator a1(-sprite1.size().width(), Application::instance().screen()->size().width(),
                        std::chrono::milliseconds(10000),
                        easing_linear);
    a1.on_change([&sprite1](PropertyAnimator::Value value){
            sprite1.x(value);
        });
    a1.start();

    PeriodicTimer floattimer(std::chrono::milliseconds(1000 * 12));
    floattimer.on_timeout([&a1, &sprite1, &win]()
    {

        static std::uniform_int_distribution<int> yoffdist(0, win.height() - sprite1.size().height());
        int y = yoffdist(win.e1);

        sprite1.move(Point(-sprite1.size().width(), y));
        a1.start();
    });
    floattimer.start();
#endif

#ifdef SPRITE2
    PropertyAnimator a2(-sprite2.size().width(), Application::instance().screen()->size().width(),
                        std::chrono::milliseconds(12000),
                        easing_linear);
    a2.on_change([&sprite2](PropertyAnimator::Value value){
            sprite2.x(value);
        });
    a2.start();

    PeriodicTimer floattimer2(std::chrono::milliseconds(1000 * 15));
    floattimer2.on_timeout([&a2, &sprite2, &win]()
    {
        static std::uniform_int_distribution<int> yoffdist(0, win.height() - sprite2.size().height());
        int y = yoffdist(win.e1);

        sprite2.move(Point(-sprite2.size().width(), y));
        a2.start();
    });
    floattimer2.start();
#endif

    Label label1("CPU: ----");
    label1.color(Palette::ColorId::text, Palette::red);
    label1.color(Palette::ColorId::bg, Palette::transparent);
    win.add(bottom(left(label1)));

    egt::experimental::CPUMonitorUsage tools;
    PeriodicTimer cputimer(std::chrono::seconds(1));
    cputimer.on_timeout([&label1, &tools]()
    {
        tools.update();
        ostringstream ss;
        ss << "CPU: " << static_cast<int>(tools.usage()) << "%";
        label1.text(ss.str());
    });
    cputimer.start();

    win.show();

    return app.run();
}


http://www.kler.cn/a/474220.html

相关文章:

  • spring mvc源码学习笔记之九
  • C语言基本知识复习浓缩版:标识符、函数、进制、数据类型
  • Ungoogled Chromium127 编译指南 MacOS篇(八)- 开始编译
  • 『SQLite』如何使用索引来查询数据?
  • flutter 专题二十四 Flutter性能优化在携程酒店的实践
  • 基于Python的投资组合收益率与波动率的数据分析
  • 【gRPC】对称与非对称加解密和单向TLS与双向TLS讲解与go案例
  • vue 点击按钮复制文本功能(同时解决http不安全问题)
  • c# readonly 和 const的区别和使用场景
  • Android配件应用默认启动与USB权限申请区别
  • CODESYS MODBUS TCP通信(禾川Q1 PLC作为MODBUS TCP从站)
  • 【mysql】流程控制
  • 【前端,TypeScript】TypeScript速成(八):Promise
  • 机器学习的组成
  • PDFMathTranslate: Star13.8k,一款基于AI的PDF文档全文双语翻译PDF文档全文双语翻译,保留格式神器,你应该需要它
  • R语言的语法
  • 《鸿蒙系统AI技术:筑牢复杂网络环境下的安全防线》
  • 模型评估指标总结(预测指标、分类指标、回归指标)
  • 【开源免费】基于Vue和SpringBoot的贸易行业crm系统(附论文)
  • TVbox 手机、智能电视节目一网打尽
  • HarmonyOS Next系列之华为账号一键登录功能实现(十四)
  • 【MySQL】九、表的内外连接
  • C# 实现 gRPC 进程间通讯:两台设备的数据交换之道
  • 前端笔记:获取leetcode题目
  • 【复杂网络演化博弈_01】理论部分+代码应用
  • [Unity]MacOS下开发Unity