文章目录
- 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 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();
}