文章目录
- 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采用了非常优秀的开源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 partsQPixmap 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 imageui->backgroundLabel->setPixmap(QPixmap(":/images/water_1080.png"));ui->backgroundLabel->setScaledContents(true);// Set the initial fish imagesui->fishLabel->setPixmap(fishPixmaps[0]);ui->fishLabel->setScaledContents(true);ui->fish2Label->setPixmap(fish2Pixmaps[0]);ui->fish2Label->setScaledContents(true);// Set the diver imageui->diverLabel->setPixmap(diverPixmaps[0]);ui->diverLabel->setScaledContents(true);// Create timers for moving the fishmoveTimer = 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 fishanimateTimer = 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 diveranimateTimer3 = 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 bubblesbubbleTimer = 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 5for (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 50int xspeed = 0;int yspeed = -QRandomGenerator::global()->bounded(5, 20); // Random speed between 1 and 10Bubble* 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 speedif (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 EXAMPLEDATAadd_search_path(EXAMPLEDATA);
#endifMainWindow win;vector<Sprite*> sprites;#define SPRITE1
#ifdef SPRITE1Sprite 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 SPRITE2Sprite 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);
#endifsprites.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 SPRITE1PropertyAnimator 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 SPRITE2PropertyAnimator 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();
#endifLabel 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();
}