遗漏的部分
This commit is contained in:
1 parent
84fb370da8
commit
0493383ee8
63 files changed
+28237
-4
No files matched your search
@@ -0,0 +1,79 @@
|
||||
#include <cstdlib>
|
||||
#include "lci/utf8.h"
|
||||
#include "lci/ansi.h"
|
||||
#include "lci/Drawable.h"
|
||||
#include "lci/Area.h"
|
||||
namespace qing {
|
||||
|
||||
/* FIXME: 区域类持有可绘制类的引用 */
|
||||
Area::Area(Drawable *d, int w, int h, int x, int y, int rotate = 0, int fontsize=18)
|
||||
:Drawable(w, h, x, y, rotate, fontsize) {
|
||||
this->d = d;
|
||||
}
|
||||
|
||||
/* 获取持有可绘制对象的颜色 */
|
||||
Color Area::_get(int x, int y) {
|
||||
return d->get_color(this->x + x, this->y + y);
|
||||
}
|
||||
|
||||
/* 绘制到持有可绘制对象 */
|
||||
void Area::_p(int x, int y, Color &clr) {
|
||||
d->point(this->x + x, this->y + y, clr);
|
||||
}
|
||||
|
||||
/* 持有可绘制对象区域移动 */
|
||||
// void Area::_up(int x, int y, int w, int h, int level) {
|
||||
// d->up(this->x + x, this->y + y, w, h, level);
|
||||
// }
|
||||
//
|
||||
// void Area::_down(int x, int y, int w, int h, int level) {
|
||||
// d->down(this->x + x, this->y + y, w, h, level);
|
||||
// }
|
||||
//
|
||||
// void Area::_left(int x, int y, int w, int h, int level) {
|
||||
// d->left(this->x + x, this->y + y, w, h, level);
|
||||
// }
|
||||
//
|
||||
// void Area::_right(int x, int y, int w, int h, int level) {
|
||||
// d->right(this->x + x, this->y + y, w, h, level);
|
||||
// }
|
||||
|
||||
/* 将另一个可绘制对象刷新到这里 */
|
||||
void Area::flush(Drawable *n, bool border) {
|
||||
Rectangle size = n->get_size();
|
||||
Rectangle pos = n->get_pos();
|
||||
Color &clr = n->getFontsColor();
|
||||
if (border) {
|
||||
rectangle(pos.w-1, pos.h-1, size.w+1, size.h+1, clr);
|
||||
}
|
||||
|
||||
/* FIXME: Area无需复制?如果是两块区域还是需要的 */
|
||||
Area *p = dynamic_cast<Area*>(n);
|
||||
if (p != nullptr) return;
|
||||
|
||||
// too slow
|
||||
// 太慢了
|
||||
for (int x = 0; x < size.w; ++x) {
|
||||
for (int y = 0; y < size.h; ++y) {
|
||||
Color color = n->get_color(x, y);
|
||||
point(pos.w + x, pos.h + y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: 边界检查应该加在父类方法中 不超出边界 */
|
||||
/* FIXME: 将border写入成员中,如果有border连同它一起移动 */
|
||||
/* FIXME: rotate=1时不能正常移动 */
|
||||
void Area::move(int x, int y) {
|
||||
// if (x >= 0)
|
||||
// d->right(this->x, this->y, this->w+x, this->h, x);
|
||||
// else if (x < 0)
|
||||
// d->left(this->x+x, this->y, this->w-x, this->h, x);
|
||||
// if (y >= 0)
|
||||
// d->down(this->x+x, this->y, this->w, this->h+y, y);
|
||||
// else if (y <0)
|
||||
// d->up(this->x+x, this->y+y, this->w, this->h-y, y);
|
||||
this->x += x;
|
||||
this->y += y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* FIXME: 现在输入框无法触底
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "lci/CmdArea.h"
|
||||
#include "lci/utf8.h"
|
||||
#include "lci/ansi.h"
|
||||
constexpr static auto H = 2;
|
||||
namespace qing {
|
||||
/* FIXME(20260113): 将输入框做成大部分时候和输入行在同一行 */
|
||||
CmdArea::CmdArea(Drawable *d, int w, int h, int x, int y, int rotate, int fontsize, bool hasBox)
|
||||
: Area(d, w, h, x, y, rotate, fontsize), hasBox(hasBox) {
|
||||
//auto w_std = w - 2 * x;
|
||||
auto w_std = 5 * fontsize;
|
||||
auto w_tar = 5 * fontsize;
|
||||
if (hasBox) {
|
||||
area = std::make_unique<InputBox>(this, (w_tar < w_std) ? w_tar : w_std, H*fontsize, x, fontsize*2, rotate, fontsize);
|
||||
}
|
||||
}
|
||||
|
||||
void CmdArea::Input(char c) {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
this->input += c;
|
||||
}
|
||||
|
||||
void CmdArea::ClearInput() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
this->input = "";
|
||||
}
|
||||
|
||||
void CmdArea::update_input() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
if (hasBox)
|
||||
area->print((char*)this->input.c_str(), 0);
|
||||
}
|
||||
|
||||
void CmdArea::clearBox() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
auto clr = black;
|
||||
if (hasBox){
|
||||
auto size = area->get_size();
|
||||
area->rectangle_fill(0, 0, size.w-1, size.h, clr);
|
||||
}
|
||||
}
|
||||
|
||||
void CmdArea::delete_input() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
auto l = this->input.length();
|
||||
this->input = this->input.substr(0, l - 1);
|
||||
}
|
||||
|
||||
std::string CmdArea::get_input_and_clear() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
auto cur = this->input;
|
||||
this->input = "";
|
||||
return cur;
|
||||
}
|
||||
|
||||
void CmdArea::print(char *utf8, int d) {
|
||||
auto color = black;
|
||||
auto fontsize = FontSize_get();
|
||||
auto half_f = fontsize / 2;
|
||||
this->lock.lock();
|
||||
|
||||
auto subpos =(hasBox) ? area->get_pos() : Rectangle{0, 0};
|
||||
auto subsize = (hasBox) ? area->get_size() : Rectangle{0, 0};
|
||||
if (hasBox)
|
||||
rectangle_fill(subpos.w-1, subpos.h-1, subsize.w+2, subsize.h+2, color);
|
||||
char *ansi = utf8::to_ansi(utf8);
|
||||
char *p = ansi;
|
||||
Rectangle l = get_size();
|
||||
while (1) {
|
||||
|
||||
//int size = ansi::size_gb18030((unsigned char*)p, 4);
|
||||
/**
|
||||
* 这个老的矢量字库应该是 GB2312 的
|
||||
*/
|
||||
int size = ansi::size_gb2312(*(unsigned char *)p);
|
||||
|
||||
if (*p == '\n') {
|
||||
|
||||
x_f = 0;
|
||||
y_f += fontsize;
|
||||
|
||||
} else if (*p == '\0') {
|
||||
|
||||
break;
|
||||
|
||||
} else if (*p == '\t') {
|
||||
|
||||
int n = x_f / half_f;
|
||||
int m = 8 - n % 8;
|
||||
x_f += m * half_f;
|
||||
|
||||
} else if (*p == '\r') {
|
||||
|
||||
;
|
||||
|
||||
} else if (*p == '\033') {
|
||||
p++;
|
||||
if (*p != '[' && *p != ']') continue;
|
||||
|
||||
if (*p == ']') {
|
||||
p++;
|
||||
while (*p != '\007') {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (*p == '[') {
|
||||
p++;
|
||||
int top = 0, t2 = 0;
|
||||
char buffer[32], buf2[16];
|
||||
while((!(*p > 'a' && *p < 'z') && !(*p >'A' && *p < 'Z')) && (*p != '\0')) {
|
||||
buffer[top++] = *(p++);
|
||||
}
|
||||
if (*p == '\0') break;
|
||||
buffer[top++] = *p;
|
||||
buffer[top] = '\0';
|
||||
//printf("%s\n", buffer);
|
||||
int i = 0;
|
||||
while (i <= top) {
|
||||
if (buffer[i] == ';') {
|
||||
buf2[t2] = '\0';
|
||||
handle_ansi_code(buf2);
|
||||
buf2[0] = '\0';
|
||||
t2 = 0;
|
||||
} else {
|
||||
buf2[t2++] = buffer[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
buf2[t2] = '\0';
|
||||
handle_ansi_code(buf2);
|
||||
}
|
||||
|
||||
} else { // 正常的字
|
||||
|
||||
char c;
|
||||
if (size == 1) {
|
||||
c = *(p + 1);
|
||||
*(p + 1) = '\0';
|
||||
}
|
||||
drawvfont(x_f, y_f, *(unsigned short*)p, clr);
|
||||
if (size == 1) {
|
||||
*(p + 1) = c;
|
||||
}
|
||||
x_f += (size == 1) ? fontsize / 2 : fontsize;
|
||||
|
||||
}
|
||||
|
||||
if (x_f >= l.w - fontsize) {
|
||||
|
||||
y_f += fontsize;
|
||||
x_f = 0;
|
||||
|
||||
}
|
||||
|
||||
if (y_f >= l.h - (d + 3) * fontsize) { /* FIXME: d加上输入框高度 */
|
||||
|
||||
int level = (y_f % fontsize == 0) ? fontsize : y_f % fontsize;
|
||||
y_f -= level;
|
||||
up(0, 0, l.w, l.h - d * fontsize, level);
|
||||
|
||||
}
|
||||
|
||||
p += size;
|
||||
|
||||
}
|
||||
free(ansi);
|
||||
if (hasBox) {
|
||||
subpos.w = ((x_f + fontsize + subsize.w) > l.w - fontsize) ? l.w - fontsize - subsize.w : x_f + fontsize;
|
||||
subpos.h = y_f + fontsize;
|
||||
area->set_pos(subpos);
|
||||
this->lock.unlock();
|
||||
this->update_input();
|
||||
flush(area.get(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void CmdArea::handle_ansi_code(char *buffer) {
|
||||
static std::vector<std::string> v;
|
||||
std::string cur = buffer;
|
||||
char c = '\0';
|
||||
v.push_back(cur);
|
||||
if (!cur.empty() && std::isalpha((c = cur.back()))) {
|
||||
for ( const auto& elem: v ) {
|
||||
|
||||
if (elem == "32m") {
|
||||
Color clr = green;
|
||||
set_color(clr);
|
||||
} else if (elem == "34m") {
|
||||
Color clr = blue;
|
||||
set_color(clr);
|
||||
} else if (elem == "0m" || elem == "00m") {
|
||||
Color clr = white;
|
||||
set_color(clr);
|
||||
} else if (elem == "1m" || elem == "01m") {
|
||||
if (v[0] == "40") {
|
||||
;
|
||||
}
|
||||
|
||||
if (v[1] == "33") {
|
||||
//Color clr = yellow;
|
||||
//set_color(clr);
|
||||
}
|
||||
}
|
||||
std::cout << elem << " ";
|
||||
|
||||
}
|
||||
v.clear();
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "lci/Color.h"
|
||||
namespace qing {
|
||||
const Color black=Color(0,0,0);
|
||||
const Color white=Color(255,255,255);
|
||||
const Color green=Color(0,255,0);
|
||||
const Color blue =Color(0,0,255);
|
||||
const Color red =Color(255,0,0);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include "lci/CommonThread.h"
|
||||
namespace qing {
|
||||
namespace lci{
|
||||
void CommonThread::thread_main() {
|
||||
|
||||
while (chk() != SSHUT) {
|
||||
/*进入回字形循环,如果不是进入关闭态
|
||||
内层循环会一直进行。*/
|
||||
|
||||
if (chk() == SSTOP){
|
||||
/*内层循环,静止状态*/
|
||||
StopEvent();
|
||||
}
|
||||
|
||||
if (chk() == SSTART) {
|
||||
/*由静止状态退出时,如果进入开始状态
|
||||
则进入单次的设置操作*/
|
||||
WakeEvent();
|
||||
}
|
||||
|
||||
if (chk() == SRUNNING) {
|
||||
/*由设置阶段退出时,如果是运行状态,则发出提示*/
|
||||
}
|
||||
|
||||
while (chk() == SRUNNING) {
|
||||
//进入运行态
|
||||
LoopEvent();
|
||||
}
|
||||
|
||||
/*循环末尾,进行一次清理
|
||||
由于之前可能是由静止态直接切换到关闭工作
|
||||
在清理时需要检查成员变量是否是被设置过的*/
|
||||
ClearEvent();
|
||||
}
|
||||
|
||||
}//入口
|
||||
|
||||
CommonThread::CommonThread(std::string name) {
|
||||
/*构造函数,完成数据域的设置。*/
|
||||
this->label = name;
|
||||
//创建线程的函数
|
||||
this->thread = new std::thread(&CommonThread::thread_main, this);
|
||||
}
|
||||
|
||||
//清除线程
|
||||
//使线程自主退出的函数必须放到外面或者子类叶子上,
|
||||
//否则会导致关闭时子类已经被释放,进而导致调用不了
|
||||
CommonThread::~CommonThread(){
|
||||
if (this->thread){
|
||||
delete this->thread;//删除线程类
|
||||
}
|
||||
|
||||
} //销毁线程
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//访问器函数
|
||||
|
||||
void CommonThread::SetLabel(std::string name) {
|
||||
//对线程名字的设置
|
||||
this->label = name;
|
||||
}
|
||||
|
||||
std::string& CommonThread::GetLabel() {
|
||||
//对线程名字的获取
|
||||
return this->label;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//等待线程启动完毕
|
||||
//线程启动失败返回失败。线程启动成功或者现程结束返回成功。
|
||||
bool CommonThread::WaitStart(unsigned long n) {
|
||||
for(unsigned long i=0; i<n; i++){
|
||||
enum Stat stat = this->chk();
|
||||
if (stat == SSTOP) return false;
|
||||
else if (stat == SRUNNING || stat == SSHUT) return true;
|
||||
else if (stat == SSTART) usleep(1);
|
||||
}//按次循环
|
||||
return false;
|
||||
}//等线程开始
|
||||
|
||||
//等线程关闭
|
||||
void CommonThread::WaitClose() {
|
||||
thread->join();
|
||||
}
|
||||
//-----------------------------------------------------------
|
||||
|
||||
void CommonThread::StopEvent(){
|
||||
this->suspend();
|
||||
}
|
||||
void CommonThread::WakeEvent(){
|
||||
this->run();
|
||||
}
|
||||
void CommonThread::LoopEvent(){
|
||||
usleep(10000);
|
||||
}
|
||||
void CommonThread::ClearEvent() {
|
||||
}
|
||||
}//lci
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
#include <cmath>
|
||||
#include "lci/Drawable.h"
|
||||
namespace qing {
|
||||
|
||||
/* FIXME: 字体宽高分开传入 */
|
||||
Drawable::Drawable(int w, int h, int x, int y, int rotate, int fontsize) /* 构造可绘制对象 */
|
||||
: w(w), h(h), x(x), y(y), rotate(rotate), font_w(fontsize), font_h(fontsize) {
|
||||
zn = zzhinumber; /* 初始化字库 */
|
||||
vennum = ttunumber;
|
||||
}
|
||||
|
||||
|
||||
Rectangle Drawable::get_size() { /* 获取表示尺寸的矩形 */
|
||||
if (rotate == 0 || rotate == 2) {
|
||||
return Rectangle(w, h);
|
||||
} else if (rotate == 1 || rotate == 3) {
|
||||
return Rectangle(h, w);
|
||||
}
|
||||
throw Drawable::InvalidRotateMode();
|
||||
}
|
||||
|
||||
Rectangle Drawable::get_pos() { /* 获取表示位置的矩形 */
|
||||
return Rectangle(x, y);
|
||||
}
|
||||
|
||||
void Drawable::set_pos(Rectangle rect) { /* 设置表示位置的矩形 */
|
||||
x = rect.w;
|
||||
y= rect.h;
|
||||
}
|
||||
|
||||
Color Drawable::get_color(int x, int y) { /* 获取颜色 */
|
||||
|
||||
if (rotate == 0)
|
||||
{
|
||||
return _get(x, y);
|
||||
}
|
||||
|
||||
else if (rotate == 1)
|
||||
{
|
||||
return _get(y, h - x - 1);
|
||||
}
|
||||
|
||||
else if (rotate == 2)
|
||||
{
|
||||
return _get(w - x - 1, h - y - 1);
|
||||
}
|
||||
|
||||
else if (rotate == 3)
|
||||
{
|
||||
return _get(w - y - 1 , x);
|
||||
}
|
||||
|
||||
throw Drawable::InvalidRotateMode();
|
||||
}
|
||||
|
||||
/* 设置颜色 */
|
||||
void Drawable::set_color(Color &color) {
|
||||
this->clr = color;
|
||||
}
|
||||
|
||||
/* 设置字体大小 */
|
||||
void Drawable::FontSize(int fontsize) {
|
||||
font_w = fontsize;
|
||||
font_h = fontsize;
|
||||
}
|
||||
|
||||
/* 获取字体大小 */
|
||||
int Drawable::FontSize_get() {
|
||||
return font_w;
|
||||
}
|
||||
|
||||
/* 获取字体颜色 */
|
||||
Color &Drawable::getFontsColor() {
|
||||
return this->clr;
|
||||
}
|
||||
|
||||
|
||||
void Drawable::point(int x, int y, Color &clr) { /* 绘制点 */
|
||||
|
||||
if (rotate == 0)
|
||||
{
|
||||
_p(x, y, clr);
|
||||
}
|
||||
|
||||
else if (rotate == 1)
|
||||
{
|
||||
_p(y, h - x - 1, clr);
|
||||
}
|
||||
|
||||
else if (rotate == 2)
|
||||
{
|
||||
_p(w - x - 1, h - y - 1, clr);
|
||||
}
|
||||
|
||||
else if (rotate == 3) {
|
||||
_p(w - y - 1 , x, clr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Drawable::line(int x1, int y1, int x2, int y2, Color &clr) /* 绘制线 */
|
||||
{
|
||||
double t, dt;
|
||||
double i, j;
|
||||
i = x1;
|
||||
j = y1;
|
||||
if (std::fabs(x2 - x1) > std::fabs(y2 - y1))
|
||||
dt = 1 / (std::fabs(x2 - x1) * 2.0);
|
||||
else
|
||||
dt = 1 / (std::fabs(y2 - y1) * 2.0);
|
||||
for (t = 0; t <= 1.0; t += dt) {
|
||||
point((int)(i+0.5), (int)(j+0.5), clr);
|
||||
i += dt * (x2 - x1);
|
||||
j += dt * (y2 - y1);
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::rectangle(int x, int y, int w, int h, Color &clr) /* 绘制矩形 */
|
||||
{
|
||||
line(x, y, x+w, y, clr);
|
||||
line(x+w, y, x+w, y+h, clr);
|
||||
line(x+w, y+h, x, y+h, clr);
|
||||
line(x, y+h, x, y, clr);
|
||||
}
|
||||
|
||||
void Drawable::rectangle_fill(int x, int y, int w, int h, Color &clr) /* 填充矩形 */
|
||||
{
|
||||
for (int i = 0; i < h; ++i)
|
||||
{
|
||||
line(x, y+i, x+w, y+i, clr);
|
||||
}
|
||||
}
|
||||
|
||||
// void Drawable::up(int x, int y, int w, int h, int level) /* 画面上移 */
|
||||
// {
|
||||
// switch (rotate)
|
||||
// {
|
||||
// case 0: {
|
||||
// _up(x, y, w, h, level);
|
||||
// break;
|
||||
// }
|
||||
// case 1: {
|
||||
// _left(y, x, h, w, level);
|
||||
// break;
|
||||
// }
|
||||
// case 2: {
|
||||
// _down(this->w - x - w, this->h - y - h, w, h, level);
|
||||
// break;
|
||||
// }
|
||||
// case 3: {
|
||||
// _right(this->w - y - h, x, h, w, level);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void Drawable::down(int x, int y, int w, int h, int level) /* 画面下移 */
|
||||
// {
|
||||
// switch (rotate)
|
||||
// {
|
||||
// case 0: {
|
||||
// _down(x, y, w, h, level);
|
||||
// break;
|
||||
// }
|
||||
// case 1: {
|
||||
// _right(y, x, h, w, level);
|
||||
// break;
|
||||
// }
|
||||
// case 2: {
|
||||
// _up(this->w - x - w, this->h - y - h, w, h, level);
|
||||
// break;
|
||||
// }
|
||||
// case 3: {
|
||||
// _left(this->w - y - h, x, h, w, level);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void Drawable::left(int x, int y, int w, int h, int level) /* 画面左移 */
|
||||
// {
|
||||
// switch (rotate)
|
||||
// {
|
||||
// case 0: {
|
||||
// _left(x, y, w, h, level);
|
||||
// break;
|
||||
// }
|
||||
// case 1: {
|
||||
// _down(y, x, h, w, level);
|
||||
// break;
|
||||
// }
|
||||
// case 2: {
|
||||
// _right(this->w - x - w, this-> h - y - h, w, h, level);
|
||||
// break;
|
||||
// }
|
||||
// case 3: {
|
||||
// _up(this->w - y - h, x, h, w, level);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void Drawable::right(int x, int y, int w, int h, int level) /* 画面右移 */
|
||||
// {
|
||||
// switch (rotate)
|
||||
// {
|
||||
// case 0: {
|
||||
// _right(x, y, w, h, level);
|
||||
// break;
|
||||
// }
|
||||
// case 1: {
|
||||
// _up(y, x, h, w, level);
|
||||
// break;
|
||||
// }
|
||||
// case 2: {
|
||||
// _left(this->w - x - w, this->h - y - h, w, h, level);
|
||||
// break;
|
||||
// }
|
||||
// case 3: {
|
||||
// _down(this->w - y - h, x, h, w, level);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/* FIXME: 单独放在一个文件里 */
|
||||
void Drawable::test() { /* 绘制测试图案 */
|
||||
|
||||
Rectangle size = get_size();
|
||||
Color base;
|
||||
int k1 = -1;
|
||||
|
||||
for (int y=0; y < size.h; ++y) {
|
||||
Color cur = base;
|
||||
int k2 = -1;
|
||||
|
||||
for (int x=0; x < size.w; x++) {
|
||||
|
||||
if (cur.r == 0) k2 = 1;
|
||||
else if (cur.r == 255) k2 = -1;
|
||||
|
||||
cur.r += k2;
|
||||
cur.g += k2;
|
||||
cur.b += k2;
|
||||
|
||||
point(x, y, cur);
|
||||
|
||||
}
|
||||
|
||||
if (base.r == 0) k1 = 1;
|
||||
else if (base.r == 255) k1 = -1;
|
||||
|
||||
base.r += k1;
|
||||
base.g += k1;
|
||||
base.b += k1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动测试 */
|
||||
// void Drawable::test1()
|
||||
// {
|
||||
// Rectangle size = get_size();
|
||||
// up(0, 0, size.w, size.h, 10);
|
||||
// down(0, 0, size.w, size.h, 5);
|
||||
// left(0, 0, size.w, size.h, 10);
|
||||
// right(0, 0, size.w, size.h, 5);
|
||||
// }
|
||||
|
||||
unsigned char *Drawable::drawvect(int x, int y, unsigned char *vect, int width, int high, Color &clr)
|
||||
// x, y是左上角坐标,vect指向矢量图数据,width和high分别代表要绘制的图的宽和高(好像宽需要=高)
|
||||
{
|
||||
if (!vect) return nullptr;
|
||||
int i = 0, j = 1; // 指向第一对数字
|
||||
unsigned char ix, iy; // 从数组取出的第一对坐标放在这
|
||||
double x1, y1;//, x2, y2; // 绘制直线的实数的坐标
|
||||
if (high == 0) high = width; // 调节高宽
|
||||
int ii1, jj1, ii2, jj2; // 最后算出的绘制直线的坐标
|
||||
int isend = 0; //是否到达曲线尾
|
||||
int isallend = 0; // 是否结束整个图像
|
||||
do {
|
||||
ix = vect[i];
|
||||
iy = vect[j];
|
||||
isend = ix & 1; // 奇数代表曲线尾
|
||||
isallend = iy & 1; // 奇数代表整个图像结束
|
||||
if (isallend) {
|
||||
i += 2;
|
||||
break; // 整个图像结束返回
|
||||
}
|
||||
if (isend) { // 如果这里曲线结束什么也不画进到下一条曲线
|
||||
i += 2;
|
||||
j += 2;
|
||||
continue;
|
||||
}
|
||||
ix /= 2;
|
||||
iy /= 2;
|
||||
x1 = ix;
|
||||
y1 = iy;
|
||||
x1 = width * x1 / 128.0;
|
||||
y1 = high * y1 / 128.0;
|
||||
ii1 = (int)(x1 + x);
|
||||
jj1 = (int)(y1 + y);
|
||||
int k;
|
||||
for (k = 0; k < 100000; k++) {
|
||||
//x2 = x1;
|
||||
//y2 = y1;
|
||||
ii2 = ii1;
|
||||
jj2 = jj1;
|
||||
i += 2;
|
||||
j += 2;
|
||||
ix = vect[i];
|
||||
iy = vect[j];
|
||||
isend = ix & 1; //奇数代表曲线尾
|
||||
isallend = iy & 1; //奇数代表整个图像结束
|
||||
ix /= 2;
|
||||
iy /= 2;
|
||||
x1 = ix;
|
||||
y1 = iy;
|
||||
x1 = width * x1 / 128.0;
|
||||
y1 = high * y1 / 128.0;
|
||||
ii1 = (int)(x1 + x);
|
||||
jj1 = (int)(y1 + y);
|
||||
line(ii1, jj1, ii2, jj2, clr);
|
||||
if (isend || isallend)
|
||||
break;
|
||||
}
|
||||
i += 2; //指向下一条曲线
|
||||
j += 2;
|
||||
} while (!isallend);
|
||||
return vect + i;
|
||||
}
|
||||
|
||||
void Drawable::drawvfont(int x, int y, unsigned short z, Color &clr) {
|
||||
int i, j, k, ii;
|
||||
j = -1;
|
||||
for (i = 0; i < zn; i++) {
|
||||
if (chss[i] == z) {
|
||||
j = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < 0) return;
|
||||
k = 0;
|
||||
for (i = 0; i < vennum; i++) {
|
||||
if (k == j)
|
||||
break;
|
||||
for (ii =i; ii <vennum; ii++) {
|
||||
if (vec[2*ii + 1] & 1) {
|
||||
k++;
|
||||
i = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k < j) return;
|
||||
drawvect(x, y, vec + 2 * i, font_w, font_h, clr);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include "lci/FrameBuf.h"
|
||||
#include "lci/Area.h"
|
||||
namespace qing {
|
||||
|
||||
/* 这个构造函数太长,应该还可以再提炼一下 */
|
||||
FrameBuf::FrameBuf(const char *path, int rotate, int fontsize)
|
||||
: Drawable(0, 0, 0, 0, rotate, fontsize) {
|
||||
|
||||
//int open_dev(const char *path)
|
||||
{
|
||||
if (d != -1) {
|
||||
throw std::runtime_error("Device already opening.");
|
||||
}
|
||||
|
||||
if ((d = open(path, O_RDWR)) == -1) {
|
||||
throw std::runtime_error("Error openning framebuffer device");
|
||||
}
|
||||
}
|
||||
|
||||
//void init_info()
|
||||
{
|
||||
if (ioctl(d, FBIOGET_VSCREENINFO, &var_info) < 0) {
|
||||
close(d);
|
||||
d=-1;
|
||||
throw std::runtime_error("Error reading screen variable information");
|
||||
}
|
||||
|
||||
if (ioctl(d, FBIOGET_FSCREENINFO, &fix_info) < 0) {
|
||||
close(d);
|
||||
d=-1;
|
||||
throw std::runtime_error("Error reading fixed information");
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "res:\t\t" << var_info.xres << "," << var_info.yres << std::endl;
|
||||
std::cout << "res_virtual:\t" << var_info.xres_virtual << "," << var_info.yres_virtual << std::endl;
|
||||
std::cout << "bpp:\t\t" << var_info.bits_per_pixel << std::endl;
|
||||
|
||||
w = var_info.xres_virtual;
|
||||
h = var_info.yres_virtual;
|
||||
|
||||
|
||||
/*
|
||||
* 不知道用不用虚拟尺寸,目前是用,在虚拟机上的效果不清楚。我在虚拟机上见过虚拟比实际小的。
|
||||
*
|
||||
* 而我的机器人上则是虚拟尺寸比物理尺寸大。
|
||||
*/
|
||||
//long v_size = (long)var_info.xres * var_info.yres * var_info.bits_per_pixel / 8;
|
||||
long v_size = (long)var_info.xres_virtual * var_info.yres_virtual * var_info.bits_per_pixel / 8;
|
||||
|
||||
long logic_size = (long)fix_info.smem_len;
|
||||
int delta = logic_size - v_size;
|
||||
// 如果实长大于虚长,那么就证明这个缓冲区存在过量
|
||||
double delta_x = (double)delta / var_info.xres;
|
||||
double delta_y = (double)delta / var_info.yres;
|
||||
double delta_bpp = (double)delta / var_info.bits_per_pixel;
|
||||
|
||||
|
||||
std::cout << "virtual size:\t" << v_size << std::endl;
|
||||
std::cout << "logic size:\t" << logic_size<< std::endl;
|
||||
std::cout << "delta: \t" << delta<< std::endl;
|
||||
std::cout << "delta x: \t" << delta_x<< std::endl;
|
||||
std::cout << "delta y: \t" << delta_y<< std::endl;
|
||||
std::cout << "delta bpp: \t" << delta_bpp<< std::endl;
|
||||
if (delta == 0) {
|
||||
// 如果实长和虚长是相等的,那么就证明这个缓冲区无过量
|
||||
x_over = 0;
|
||||
}
|
||||
|
||||
else if (delta > 0) {
|
||||
|
||||
// 是否存在小数差距
|
||||
if (delta_y - (int)delta_y != 0) {
|
||||
//close(d);
|
||||
//d=-1;
|
||||
//throw std::runtime_error("y_over");
|
||||
x_over = delta_y * 8 / var_info.bits_per_pixel;
|
||||
}
|
||||
else {
|
||||
// 计算横轴过量
|
||||
x_over = delta_y * 8 / var_info.bits_per_pixel;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算行跳量
|
||||
line_jump = (var_info.xres_virtual + x_over) * var_info.bits_per_pixel / 8;
|
||||
|
||||
|
||||
//
|
||||
buf = (char*)mmap(buf, fix_info.smem_len, PROT_WRITE, MAP_SHARED, d, fix_info.smem_start);
|
||||
if (buf == MAP_FAILED) {
|
||||
close(d);
|
||||
d=-1;
|
||||
throw std::runtime_error("Failed mapping of the frame buffer to memory");
|
||||
}
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
FrameBuf::~FrameBuf() { /* 析构函数 */
|
||||
clear();
|
||||
munmap(buf, fix_info.smem_len);
|
||||
close(d);
|
||||
}
|
||||
|
||||
/* 获取像素比特 */
|
||||
int FrameBuf::get_bpp() { return var_info.bits_per_pixel; }
|
||||
|
||||
/* 32位像素比特的绘制点 */
|
||||
void FrameBuf::p32(__u32 ux, __u32 uy, Color &color)
|
||||
{
|
||||
if (ux < 0 || ux >= var_info.xres_virtual
|
||||
|| uy < 0 || uy >= var_info.yres_virtual)
|
||||
return;
|
||||
char *p = buf + (uy * line_jump + ux * 4);
|
||||
*p++ = color.b;
|
||||
*p++ = color.g;
|
||||
*p++ = color.r;
|
||||
}
|
||||
|
||||
/* 16位像素比特的绘制点 */
|
||||
void FrameBuf::p16(__u32 ux, __u32 uy, Color &color) {
|
||||
if (ux < 0 || ux >= var_info.xres_virtual
|
||||
|| uy < 0 || uy >= var_info.yres_virtual)
|
||||
return;
|
||||
char *p = buf + (uy * line_jump + ux * 2);
|
||||
uint16_t pixel_value = 0;
|
||||
pixel_value |= (color.r >> 3) << 11; //5
|
||||
pixel_value |= (color.g >> 2) << 5; //6
|
||||
pixel_value |= (color.b >> 3); //5
|
||||
*reinterpret_cast<uint16_t*>(p) = pixel_value;
|
||||
}
|
||||
|
||||
/* 获取16像素比特时的像素颜色 */
|
||||
Color FrameBuf::get16(__u32 ux, __u32 uy) {
|
||||
char *p = buf + uy * line_jump + ux * get_bpp() / 8;
|
||||
unsigned char r = (*(short*)p >> 11) & 0x1F;
|
||||
unsigned char g = (*(short*)p >> 5) & 0x3F;
|
||||
unsigned char b = (*(short*)p) & 0x1F;
|
||||
return Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2));
|
||||
}
|
||||
|
||||
/* 获取32位像素比特时的像素颜色 */
|
||||
Color FrameBuf::get32(__u32 ux, __u32 uy) {
|
||||
char *p = buf + uy * line_jump + ux * get_bpp() / 8;
|
||||
unsigned char r = *(p++);
|
||||
unsigned char g = *(p++);
|
||||
unsigned char b = *(p++);
|
||||
return Color(r, g, b);
|
||||
}
|
||||
|
||||
/* 覆盖获取点方法 */
|
||||
Color FrameBuf::_get(int x, int y) {
|
||||
switch (get_bpp()) {
|
||||
case 16:
|
||||
return get16((__u32)x, (__u32)y);
|
||||
case 32:
|
||||
return get32((__u32)x, (__u32)y);
|
||||
}
|
||||
throw FrameBuf::UnknowBPP();
|
||||
}
|
||||
|
||||
/* 覆盖绘制点方法 */
|
||||
void FrameBuf::_p(int x, int y, Color &clr) {
|
||||
|
||||
int bpp = get_bpp();
|
||||
if (bpp == 16) {
|
||||
p16((__u32)x, (__u32)y, clr);
|
||||
} else if (bpp == 32) {
|
||||
p32((__u32)x, (__u32)y, clr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 覆盖上移方法 */
|
||||
// void FrameBuf::_up(int x, int y, int w, int h, int level){
|
||||
// int l = get_bpp() / 8;
|
||||
// int oneline = w * l;
|
||||
// char *s = buf + y * line_jump + x * l;
|
||||
// char *d = s + level * line_jump;
|
||||
// for (int _ = 0; _ < h - level; ++_) {
|
||||
// memcpy(s, d, oneline);
|
||||
// s += line_jump;
|
||||
// d += line_jump;
|
||||
// }
|
||||
// for (int _ = 0; _ < level; ++_) {
|
||||
// memset(s, 0, oneline);
|
||||
// s += line_jump;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /* 覆盖下移方法 */
|
||||
// void FrameBuf::_down(int x, int y, int w, int h, int level) {
|
||||
// int l = get_bpp() / 8;
|
||||
// int oneline = w * l;
|
||||
// long d = level * line_jump;
|
||||
// char *start = buf + y * line_jump + x * l;
|
||||
// char *src = start + h * line_jump;
|
||||
// char *dist = src - d;
|
||||
//
|
||||
// do {
|
||||
//
|
||||
// src -= line_jump;
|
||||
// dist -= line_jump;
|
||||
// memcpy(src, dist, oneline);
|
||||
//
|
||||
// } while(dist != start);
|
||||
//
|
||||
// do {
|
||||
//
|
||||
// src -= line_jump;
|
||||
// memset(src, 0, oneline);
|
||||
//
|
||||
// } while (src != start);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// /* 覆盖左移方法 */
|
||||
// void FrameBuf::_left(int x, int y, int w, int h, int level) {
|
||||
//
|
||||
// int l = get_bpp() / 8;
|
||||
// int d = level * l;
|
||||
// int length = (w - level) * l;
|
||||
// char *start = buf + y * line_jump + x * l;
|
||||
// char *src = start + h * line_jump;
|
||||
// do {
|
||||
// src -= line_jump;
|
||||
// memcpy(src, src + d, length);
|
||||
// memset(src + length, 0, d);
|
||||
// } while (src != start);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// /* 覆盖右移方法 */
|
||||
// /*!*/
|
||||
// void FrameBuf::_right(int x, int y, int w, int h, int level) {
|
||||
// int l = get_bpp() / 8;
|
||||
// int d = level * l;
|
||||
// int length = (w - level) * l;
|
||||
// char *start = buf + y * line_jump + x * l;
|
||||
// char *p = start + h * line_jump;
|
||||
// char *tmp = new char[length];
|
||||
// do {
|
||||
// p -= line_jump;
|
||||
// char *src = p + d;
|
||||
// memcpy(tmp, p, length);
|
||||
// memcpy(src, tmp, length);
|
||||
// memset(p, 0, d);
|
||||
// } while (p != start);
|
||||
// delete[] tmp;
|
||||
// }
|
||||
|
||||
/* 清理方法 */
|
||||
void FrameBuf::clear() {
|
||||
memset(buf, 0, fix_info.smem_len);
|
||||
}
|
||||
|
||||
/* 将一个别的可绘制区域刷新到这个上面,也许可以绘制标头 */
|
||||
void FrameBuf::flush(Drawable *n, bool border) {
|
||||
Color &clr = n->getFontsColor();
|
||||
Rectangle size = n->get_size();
|
||||
Rectangle pos = n->get_pos();
|
||||
|
||||
if (border) {
|
||||
rectangle(pos.w-1, pos.h-1, size.w+2-1, size.h+2-1, clr);
|
||||
rectangle(pos.w-2, pos.h-2, size.w+4-1, size.h+4-1, clr);
|
||||
rectangle_fill(pos.w-2, pos.h-_HEAD_LEN-2+1, size.w+4-1, _HEAD_LEN, clr);
|
||||
}
|
||||
|
||||
Area *p = dynamic_cast<Area*>(n);
|
||||
if (p != nullptr) return;
|
||||
|
||||
// too slow 太慢
|
||||
for (int x = 0; x < size.w; ++x) {
|
||||
for (int y = 0; y < size.h; ++y) {
|
||||
Color color = n->get_color(x, y);
|
||||
point(pos.w + x, pos.h + y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "lci/utf8.h"
|
||||
#include "lci/ansi.h"
|
||||
#include "lci/InputBox.h"
|
||||
namespace qing {
|
||||
InputBox::InputBox(Drawable *d, int w, int h, int x, int y, int rotate = 0, int fontsize=18): Area(d, w, h, x, y, rotate, fontsize) {
|
||||
this->d = d;
|
||||
}
|
||||
|
||||
void InputBox::print(char *utf8, int _line) {
|
||||
|
||||
char *ansi = utf8::to_ansi(utf8);
|
||||
char *p = ansi;
|
||||
int fontsize = FontSize_get();
|
||||
int x_f = 0, y_f = _line * fontsize;
|
||||
Rectangle l = get_size();
|
||||
while (1) {
|
||||
|
||||
//int size = ansi::size_gb18030((unsigned char*)p, 4);
|
||||
|
||||
/* 这个汉字库应该是 GB2312 的 */
|
||||
int size = ansi::size_gb2312(*(unsigned char *)p);
|
||||
|
||||
if (*p == '\0') {
|
||||
|
||||
break;
|
||||
|
||||
} else { // 正常的字
|
||||
|
||||
char c;
|
||||
if (size == 1) {
|
||||
c = *(p + 1);
|
||||
*(p + 1) = '\0';
|
||||
}
|
||||
|
||||
drawvfont(x_f, y_f, *(unsigned short*)p, clr);
|
||||
|
||||
if (size == 1) {
|
||||
*(p + 1) = c;
|
||||
}
|
||||
x_f += (size == 1) ? fontsize / 2 : fontsize;
|
||||
|
||||
}
|
||||
|
||||
if (x_f >= l.w - fontsize) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
p += size;
|
||||
|
||||
}
|
||||
free(ansi);
|
||||
int y2 = y_f + fontsize-2;
|
||||
line(x_f + 2, y2, x_f + fontsize - 4, y2, clr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "lci/KeyboardEvent.h"
|
||||
namespace qing {
|
||||
/* FIXME: 划分代码块 */
|
||||
KeyboardEvent::KeyboardEvent() {
|
||||
const char *path = "/dev/input";
|
||||
struct dirent *dir;
|
||||
DIR *p = opendir(path);
|
||||
if(p == NULL) throw std::runtime_error("opendir");
|
||||
while ((dir = readdir(p)) != NULL) {
|
||||
if (!is_event_with_number(dir->d_name)) continue;
|
||||
char full_name[257] = {'\0'};
|
||||
snprintf(full_name, sizeof(full_name), "%s/%s", path, dir->d_name);
|
||||
struct stat buf;
|
||||
if (stat(full_name, &buf) == -1) {
|
||||
throw std::runtime_error("stat");
|
||||
}
|
||||
|
||||
if(S_ISDIR(buf.st_mode)) continue;
|
||||
|
||||
else if (pool.find(std::string(full_name)) == pool.end()){
|
||||
int d = open(full_name, O_RDONLY);
|
||||
if (d == -1) {
|
||||
;
|
||||
} else {
|
||||
set_noblocking(d);
|
||||
pool.insert(std::make_pair(std::string(full_name), d));
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(p);
|
||||
}
|
||||
KeyboardEvent::~KeyboardEvent() {
|
||||
std::map<std::string, int>::iterator it;
|
||||
for (it = pool.begin(); it != pool.end(); ++it) {
|
||||
close(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: 传入堵塞时间 */
|
||||
struct input_event KeyboardEvent::get() {
|
||||
fd_set set;
|
||||
FD_ZERO(&set);
|
||||
for (auto it = pool.begin(); it != pool.end(); ++it) {
|
||||
FD_SET(it->second, &set);
|
||||
}
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 60000;
|
||||
if (select(this->maxd()+1, &set, NULL, NULL, &timeout) == -1) {
|
||||
throw std::runtime_error("select");
|
||||
}
|
||||
|
||||
for (auto it = pool.begin(); it != pool.end(); ++it) {
|
||||
if (it->second != -1 && FD_ISSET(it->second, &set)) {
|
||||
struct input_event ev;
|
||||
if (read(it->second, &ev, sizeof(ev)) == sizeof(ev)) {
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NoEvent("");
|
||||
}
|
||||
bool KeyboardEvent::is_event_with_number(const char *filename) {
|
||||
size_t len = strlen(filename);
|
||||
if (len <= 5) return false;
|
||||
|
||||
if (strncmp(filename, "event", 5) != 0)
|
||||
return false;
|
||||
|
||||
for (size_t i=5; i<len; ++i)
|
||||
if (!isdigit(filename[i]))
|
||||
return false;
|
||||
|
||||
/* 所有条件都满足 */
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeyboardEvent::set_noblocking(int d) {
|
||||
int sig = fcntl(d, F_GETFL, 0);
|
||||
if (sig == -1) throw std::runtime_error("fcntl");
|
||||
if (fcntl(d, F_SETFL, sig|O_NONBLOCK) == -1)
|
||||
throw std::runtime_error("fcntl");
|
||||
}
|
||||
|
||||
int KeyboardEvent::maxd() {
|
||||
int maxValue = pool.begin()->second;
|
||||
std::map<std::string, int>::iterator it;
|
||||
for (it = pool.begin(); it != pool.end(); ++it)
|
||||
if (it->second > maxValue)
|
||||
maxValue = it->second;
|
||||
return maxValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "lci/ansi.h"
|
||||
namespace qing{
|
||||
/* 这个函数并不完全
|
||||
int ansi::size_gb18030(unsigned char *p, size_t maxLen) {
|
||||
if (*p < 0x80) {
|
||||
return 1;
|
||||
} else if ((*p > 0x81 && *p <= 0xFE) &&
|
||||
((*(p+1) >= 0x40 && *(p+1) <= 0x7E) || (*(p+1) >= 0x80 && *(p+1) <= 0xFE))) {
|
||||
return 2;
|
||||
} else if (maxLen >= 4 && (*p == 0x90 || *p == 0x91 || *p == 0xE8 || *p == 0xE9 ||
|
||||
(*p >= 0x8E && *p <= 0xEF) || (*p >= 0xF8 && *p <= 0xFB)) &&
|
||||
(*(p+1) & 0xC0) == 0x80 && (*(p+2) & 0xC0) == 0x80 && (*(p+3) & 0xC0) == 0x80) {
|
||||
return 4; //注意这里是简化判断
|
||||
} else {
|
||||
throw ansi::WrongLength();
|
||||
}
|
||||
}
|
||||
*/
|
||||
int ansi::size_gb2312(unsigned char first_byte) {
|
||||
return (first_byte >= 0xA1 && first_byte <= 0xFE) ? 2 : 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
#include"lci/fsm.h"
|
||||
namespace qing{
|
||||
namespace lci{
|
||||
Fsm::Fsm(){
|
||||
this->stat = SSTOP;
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
enum Stat Fsm::chk() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
return this->stat;
|
||||
}//check
|
||||
|
||||
void Fsm::suspend(){
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
cv.wait(lck);
|
||||
}//suspend
|
||||
|
||||
//-------------------------------------------------------
|
||||
void Fsm::wake() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
this->stat = SSTART;
|
||||
cv.notify_one();
|
||||
}//wake
|
||||
|
||||
void Fsm::stop() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
this->stat = SSTOP;
|
||||
cv.notify_one();
|
||||
}//stop
|
||||
|
||||
void Fsm::shut() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
this->stat = SSHUT;
|
||||
cv.notify_one();
|
||||
}//close
|
||||
|
||||
void Fsm::run() {
|
||||
std::unique_lock<std::mutex> lck(this->lock);
|
||||
this->stat = SRUNNING;
|
||||
cv.notify_one();
|
||||
}//run
|
||||
}//lci
|
||||
}//qing
|
||||
+20697
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,60 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <iconv.h>
|
||||
#include "lci/utf8.h"
|
||||
namespace qing {
|
||||
int utf8::size(const unsigned char c) {
|
||||
if ((c >> 7) == 0){
|
||||
// 0xxxxxxx
|
||||
return 1;
|
||||
} else if ((c >> 5) == 0b110) {
|
||||
// 110xxxxx 10xxxxxx
|
||||
return 2;
|
||||
} else if ((c >> 4) == 0b1110) {
|
||||
// 1110xxxx 10xxxxxx 10xxxxxx
|
||||
return 3;
|
||||
} else if ((c >> 3) == 0b11110) {
|
||||
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
return 4;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
char *utf8::to_ansi(char *utf8String) {
|
||||
//@20231120:楠舍科技刘仁
|
||||
//@20240528,qing:输出字符串的长度不确定
|
||||
iconv_t ic;
|
||||
ic = iconv_open("GB18030", "UTF-8");
|
||||
if (ic == (iconv_t)-1){
|
||||
perror("iconv_open");
|
||||
return NULL;
|
||||
}
|
||||
//设置输出字符串的大小为2倍输入
|
||||
size_t InputLength = strlen(utf8String);
|
||||
size_t OutputLength = InputLength * 2;
|
||||
|
||||
char *ansiString = (char*)malloc(OutputLength);
|
||||
if (ansiString == NULL) {
|
||||
perror("malloc");
|
||||
iconv_close(ic);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *InputBuffer = (char*)utf8String;
|
||||
char *OutputBuffer = ansiString;
|
||||
|
||||
size_t result = iconv(ic, &InputBuffer, &InputLength, &OutputBuffer, &OutputLength);
|
||||
if (result == (size_t)-1) {
|
||||
perror("iconv");
|
||||
free(ansiString);
|
||||
iconv_close(ic);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iconv_close(ic);
|
||||
|
||||
*OutputBuffer = '\0';
|
||||
return ansiString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "nn/ILayer.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
/* 获取函数对象 - 获取激活函数 */
|
||||
f_t ILayer::getf(ActivationFunc type) {
|
||||
switch (type) {
|
||||
case ActivationFunc::ReLU:
|
||||
return relu;
|
||||
case ActivationFunc::Leaky_ReLU:
|
||||
return leaky_relu;
|
||||
case ActivationFunc::Sigmoid:
|
||||
return sigmoid;
|
||||
case ActivationFunc::Tanh:
|
||||
return tanh;
|
||||
default:
|
||||
throw std::invalid_argument("ActivationFunc function does not support.");
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取函数对象 - 求激活函数导数 */
|
||||
f_t ILayer::getfdot(ActivationFunc type) {
|
||||
switch (type) {
|
||||
case ActivationFunc::ReLU:
|
||||
return relu_derivative;
|
||||
case ActivationFunc::Leaky_ReLU:
|
||||
return leaky_relu_derivative;
|
||||
case ActivationFunc::Sigmoid:
|
||||
return sigmoid_derivative;
|
||||
case ActivationFunc::Tanh:
|
||||
return tanh_derivative;
|
||||
default:
|
||||
throw std::invalid_argument("ActivationFunc Derivative does not support.");
|
||||
}
|
||||
}
|
||||
const char* ILayer::get_f_type(ActivationFunc& af) {
|
||||
switch (af) {
|
||||
case ActivationFunc::Sigmoid:
|
||||
return "Sigmoid";
|
||||
case ActivationFunc::ReLU:
|
||||
return "ReLU";
|
||||
case ActivationFunc::Leaky_ReLU:
|
||||
return "Leaky_ReLU";
|
||||
}
|
||||
throw std::runtime_error("What activation function");
|
||||
}
|
||||
|
||||
ILayer::ActivationFunc ILayer::parse_ff_type(const std::string& name) {
|
||||
if(name == "Sigmoid") {
|
||||
return ActivationFunc::Sigmoid;
|
||||
} else if (name == "ReLU") {
|
||||
return ActivationFunc::ReLU;
|
||||
} else if (name == "Leaky_ReLU") {
|
||||
return ActivationFunc::Leaky_ReLU;
|
||||
} else {
|
||||
throw std::runtime_error("What activation function");
|
||||
}
|
||||
}
|
||||
|
||||
/* ReLU 激活函数 */
|
||||
float ILayer::relu(float x) { return (x > 0.0) ? x : 0.0; }
|
||||
|
||||
/* ReLU 导数 */
|
||||
float ILayer::relu_derivative(float x) { return (x > 0) ? 1: 0; }
|
||||
|
||||
/*
|
||||
* leaky ReLU 激活函数
|
||||
*
|
||||
* NOTE: alpha太小或者选择使用ReLU函数的情况下,会导致梯度消失
|
||||
* NOTE: Leaky ReLU 不会使梯度完全消失,但也会使它变得很小
|
||||
*/
|
||||
float ILayer::leaky_relu(float x) { return (x > 0) ? x : LEAKY_RELU_ALPHA * x; }
|
||||
/* leaky ReLU 导数 */
|
||||
float ILayer::leaky_relu_derivative(float x) { return (x > 0) ? 1.0f : LEAKY_RELU_ALPHA; }
|
||||
/* Sigmoid 激活函数 */
|
||||
float ILayer::sigmoid(float x) { return 1.0 / (1.0 + std::exp(-x)); }
|
||||
/* Sigmoid 导数 */
|
||||
float ILayer::sigmoid_derivative(float x)
|
||||
{
|
||||
double s = sigmoid(x);
|
||||
return s * (1.0 - s);
|
||||
}
|
||||
/* Tanh 激活函数 */
|
||||
float ILayer::tanh(float x) { return std::tanh(x); }
|
||||
/* Tanh 导数 */
|
||||
float ILayer::tanh_derivative(float x) { return 1.0f - x * x; }
|
||||
@@ -0,0 +1,144 @@
|
||||
#include "nn/Matrix.hh"
|
||||
namespace qing {
|
||||
|
||||
template <typename T>
|
||||
Matrx<T>::Matrx(): row(0), col(0) {}
|
||||
|
||||
template <typename T>
|
||||
Matrx<T>::Matrx(const long row, const long col)
|
||||
: row(row), col(col) { /* 该构造函数用于从头开始构造 */
|
||||
arr = std::vector<T>(col * row);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Matrx<T>::Matrx(const long row, const long col, const std::vector<T>& src)
|
||||
: row(row), col(col) { /* 该构造函数用于通过已有的vector进行构造 */
|
||||
arr = src;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& Matrx<T>::get(long row, long col) const { /* 访问器 - 访问矩阵元素 */
|
||||
return arr[col * this->row + row];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& Matrx<T>::witch(long row, long col) { /* 访问器 - 访问矩阵元素 */
|
||||
return arr[col * this->row + row];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const std::vector<T>& Matrx<T>::get_data() const{ /* 访问器 - 用于获取原始数据 */
|
||||
return arr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const long Matrx<T>::get_col() const { /* 获取列数 */
|
||||
return col;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const long Matrx<T>::get_row() const { /* 获取行数 */
|
||||
return row;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Matrx<T>::has_same_shape(const Matrx<T>& other) { /* 两个矩阵是否具有相同形状 */
|
||||
return other.get_col() == this->get_col() || other.get_row() == this->get_row();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Matrx<T>::has_dot_shape(const Matrx<T>& other) { /* 具有矩阵乘法的形状 */
|
||||
return this->get_col() == other.get_row();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Matrx<T>::assert_same_shape(const Matrx<T>& other) { /* 断言两个矩阵具有相同形状 */
|
||||
if (!has_same_shape(other))
|
||||
throw std::invalid_argument("The shapes of matrix are not same.");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Matrx<T>::assert_dot_shape(const Matrx<T>& other) { /* 断言两个矩阵具有点积前置形状 */
|
||||
if (!has_dot_shape(other)) {
|
||||
throw std::invalid_argument("The shapes of matrix can not be dot.");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Matrx<T> Matrx<T>::operator+(const Matrx<T>& other) { /* 加法操作符重定义 - 矩阵加法 */
|
||||
assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
|
||||
std::vector<T> v0 = this->get_data();
|
||||
std::vector<T> vec = other.get_data();
|
||||
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or ) {
|
||||
*it += *it_or;
|
||||
}
|
||||
return Matrx<T>{this->get_row(), this->get_col(), v0};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Matrx<T> Matrx<T>::operator+(T& item) { /* 矩阵加法广播 */
|
||||
auto matrix = *this;
|
||||
for (auto it = arr.begin(); it != arr.end(); ++it) {
|
||||
*it += item;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Matrx<T> Matrx<T>::operator-(const Matrx<T>& other) { /* 操作符重载 - 矩阵减法 */
|
||||
assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
|
||||
std::vector<T> v0 = this->get_data();
|
||||
std::vector <T> vec = other.get_data();
|
||||
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or ) {
|
||||
*it -= *it_or;
|
||||
}
|
||||
return Matrx{this->get_row(), this->get_col(), v0};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Matrx<T> Matrx<T>::operator-(T& item) { /* 操作符重载 - 矩阵减法广播 */
|
||||
auto matrix = *this;
|
||||
for (auto it = arr.begin(); it != arr.end(); ++it) {
|
||||
*it -= item;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const Matrx<T> Matrx<T>::operator*(const Matrx<T>& other) { /* 操作符重载 - 矩阵乘法 */
|
||||
assert_dot_shape(other); /* 它们的形状是否能够点积 */
|
||||
auto matrix = Matrx<T>(this->get_row(), other.get_col());
|
||||
for (long i=0; i<this->get_row(); ++i) {/* 点积运算 */
|
||||
for (long j=0; j<other.get_col(); ++j) {
|
||||
T sum{0};
|
||||
for (long k=0; k<this->get_col(); ++k) {
|
||||
sum += this->get(i, k) * other.get(k, j);
|
||||
}
|
||||
matrix.witch(i, j) = sum;
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Matrx<T> Matrx<T>::operator*(T& item) { /* 操作符重载 - 矩阵乘法广播 */
|
||||
std::vector<T> vec = this->get_data();
|
||||
for (auto it = vec.begin(); it != vec.end(); ++it) {
|
||||
*it *= item;
|
||||
}
|
||||
return Matrx<T>{this->get_row(), this->get_col(), vec};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Matrx<T> Matrx<T>::operator/(T& item) { /* 操作符重载 - 矩阵除法广播 */
|
||||
std::vector<T> vec = this->get_data();
|
||||
for (auto it = vec.begin(); it != vec.end(); ++it) {
|
||||
*it /= item;
|
||||
}
|
||||
return Matrx<T>(this->get_row(), this->get_col(), vec);
|
||||
}
|
||||
|
||||
template class Matrx<float>; /* 显式实例化 */
|
||||
template class Matrx<double>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* MatrixCuda.cu - MatrixCuda类的CUDA实现文件
|
||||
*
|
||||
* 这个文件包含MatrixCuda类的具体实现和CUDA内核的显式实例化
|
||||
* 注意:这个文件需要使用nvcc编译器编译
|
||||
*/
|
||||
|
||||
#include "nn/MatrixCuda.cuh"
|
||||
#include "nn/Matrix.hh" // 用于与现有Matrx类交互
|
||||
|
||||
namespace qing {
|
||||
|
||||
// ============================================================================
|
||||
// 显式实例化CUDA内核函数
|
||||
// ============================================================================
|
||||
|
||||
namespace cuda_kernels {
|
||||
|
||||
// float类型内核显式实例化
|
||||
template __global__ void matrix_add_kernel<float>(
|
||||
const float* A, const float* B, float* C,
|
||||
long rows, long cols, long total_elements);
|
||||
|
||||
template __global__ void matrix_sub_kernel<float>(
|
||||
const float* A, const float* B, float* C,
|
||||
long rows, long cols, long total_elements);
|
||||
|
||||
template __global__ void matrix_scalar_mul_kernel<float>(
|
||||
const float* A, float scalar, float* C, long total_elements);
|
||||
|
||||
template __global__ void matrix_scalar_add_kernel<float>(
|
||||
const float* A, float scalar, float* C, long total_elements);
|
||||
|
||||
template __global__ void matrix_transpose_kernel<float>(
|
||||
const float* A, float* C, long rows, long cols);
|
||||
|
||||
template __global__ void matrix_hadamard_kernel<float>(
|
||||
const float* A, const float* B, float* C, long total_elements);
|
||||
|
||||
template __global__ void matrix_copy_kernel<float>(
|
||||
const float* src, float* dst, long total_elements);
|
||||
|
||||
// double类型内核显式实例化
|
||||
template __global__ void matrix_add_kernel<double>(
|
||||
const double* A, const double* B, double* C,
|
||||
long rows, long cols, long total_elements);
|
||||
|
||||
template __global__ void matrix_sub_kernel<double>(
|
||||
const double* A, const double* B, double* C,
|
||||
long rows, long cols, long total_elements);
|
||||
|
||||
template __global__ void matrix_scalar_mul_kernel<double>(
|
||||
const double* A, double scalar, double* C, long total_elements);
|
||||
|
||||
template __global__ void matrix_scalar_add_kernel<double>(
|
||||
const double* A, double scalar, double* C, long total_elements);
|
||||
|
||||
template __global__ void matrix_transpose_kernel<double>(
|
||||
const double* A, double* C, long rows, long cols);
|
||||
|
||||
template __global__ void matrix_hadamard_kernel<double>(
|
||||
const double* A, const double* B, double* C, long total_elements);
|
||||
|
||||
template __global__ void matrix_copy_kernel<double>(
|
||||
const double* src, double* dst, long total_elements);
|
||||
|
||||
} // namespace cuda_kernels
|
||||
|
||||
// ============================================================================
|
||||
// MatrixCuda类与现有Matrx类的交互实现
|
||||
// ============================================================================
|
||||
|
||||
// 从Matrx<float>转换到MatrixCuda<float>
|
||||
template <>
|
||||
CUDA_HOST MatrixCuda<float> MatrixCuda<float>::from_matrx(const Matrx<float>& mat) {
|
||||
long rows = mat.get_row();
|
||||
long cols = mat.get_col();
|
||||
const std::vector<float>& data = mat.get_data();
|
||||
|
||||
return MatrixCuda<float>(rows, cols, data);
|
||||
}
|
||||
|
||||
// 从MatrixCuda<float>转换到Matrx<float>
|
||||
template <>
|
||||
CUDA_HOST Matrx<float> MatrixCuda<float>::to_matrx() const {
|
||||
sync_device_to_host();
|
||||
return Matrx<float>(rows_, cols_, host_data_);
|
||||
}
|
||||
|
||||
// 从Matrx<double>转换到MatrixCuda<double>
|
||||
template <>
|
||||
CUDA_HOST MatrixCuda<double> MatrixCuda<double>::from_matrx(const Matrx<double>& mat) {
|
||||
long rows = mat.get_row();
|
||||
long cols = mat.get_col();
|
||||
const std::vector<double>& data = mat.get_data();
|
||||
|
||||
return MatrixCuda<double>(rows, cols, data);
|
||||
}
|
||||
|
||||
// 从MatrixCuda<double>转换到Matrx<double>
|
||||
template <>
|
||||
CUDA_HOST Matrx<double> MatrixCuda<double>::to_matrx() const {
|
||||
sync_device_to_host();
|
||||
return Matrx<double>(rows_, cols_, host_data_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 优化的矩阵乘法实现(使用共享内存)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 使用共享内存优化的矩阵乘法内核
|
||||
*
|
||||
* 这个内核使用平铺(tiling)技术来提高矩阵乘法的性能
|
||||
* 每个线程块计算输出矩阵的一个子块
|
||||
*/
|
||||
template <typename T>
|
||||
__global__ void matrix_mul_optimized_kernel(
|
||||
const T* A, const T* B, T* C,
|
||||
long A_rows, long A_cols, long B_cols,
|
||||
long tile_size) {
|
||||
|
||||
// 共享内存声明
|
||||
extern __shared__ T shared_mem[];
|
||||
T* tile_A = shared_mem;
|
||||
T* tile_B = &shared_mem[tile_size * tile_size];
|
||||
|
||||
// 线程块索引
|
||||
long block_row = blockIdx.y;
|
||||
long block_col = blockIdx.x;
|
||||
|
||||
// 线程索引
|
||||
long thread_row = threadIdx.y;
|
||||
long thread_col = threadIdx.x;
|
||||
|
||||
// 输出矩阵中的位置
|
||||
long row = block_row * tile_size + thread_row;
|
||||
long col = block_col * tile_size + thread_col;
|
||||
|
||||
T sum = T(0);
|
||||
|
||||
// 遍历所有平铺
|
||||
for (long tile_idx = 0; tile_idx < (A_cols + tile_size - 1) / tile_size; ++tile_idx) {
|
||||
// 从全局内存加载到共享内存
|
||||
long A_col = tile_idx * tile_size + thread_col;
|
||||
long B_row = tile_idx * tile_size + thread_row;
|
||||
|
||||
if (row < A_rows && A_col < A_cols) {
|
||||
tile_A[thread_row * tile_size + thread_col] = A[row * A_cols + A_col];
|
||||
} else {
|
||||
tile_A[thread_row * tile_size + thread_col] = T(0);
|
||||
}
|
||||
|
||||
if (B_row < A_cols && col < B_cols) {
|
||||
tile_B[thread_row * tile_size + thread_col] = B[B_row * B_cols + col];
|
||||
} else {
|
||||
tile_B[thread_row * tile_size + thread_col] = T(0);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// 计算平铺内的点积
|
||||
for (long k = 0; k < tile_size; ++k) {
|
||||
sum += tile_A[thread_row * tile_size + k] * tile_B[k * tile_size + thread_col];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// 将结果写回全局内存
|
||||
if (row < A_rows && col < B_cols) {
|
||||
C[row * B_cols + col] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 优化的矩阵乘法方法
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 使用优化CUDA内核的矩阵乘法
|
||||
*
|
||||
* 这个方法使用共享内存和平铺技术来加速矩阵乘法
|
||||
* 对于大矩阵,性能比朴素算法有显著提升
|
||||
*/
|
||||
template <typename T>
|
||||
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::matmul_optimized(const MatrixCuda<T>& other) const {
|
||||
assert_dot_shape(other);
|
||||
|
||||
MatrixCuda<T> result(rows_, other.cols_);
|
||||
|
||||
// 确保数据在设备上
|
||||
sync_host_to_device();
|
||||
other.sync_host_to_device();
|
||||
result.allocate_device_memory();
|
||||
|
||||
// 设置平铺大小(典型值为16或32)
|
||||
const long tile_size = 16;
|
||||
|
||||
// 计算线程块和网格大小
|
||||
dim3 block_size(tile_size, tile_size);
|
||||
dim3 grid_size((other.cols_ + tile_size - 1) / tile_size,
|
||||
(rows_ + tile_size - 1) / tile_size);
|
||||
|
||||
// 计算共享内存大小
|
||||
size_t shared_mem_size = 2 * tile_size * tile_size * sizeof(T);
|
||||
|
||||
// 启动优化内核
|
||||
matrix_mul_optimized_kernel<T><<<grid_size, block_size, shared_mem_size>>>(
|
||||
device_data_, other.device_data_, result.device_data_,
|
||||
rows_, cols_, other.cols_, tile_size
|
||||
);
|
||||
|
||||
CHECK_CUDA_ERROR(cudaGetLastError());
|
||||
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
|
||||
|
||||
result.device_modified_ = true;
|
||||
result.sync_device_to_host();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 显式实例化优化矩阵乘法
|
||||
template MatrixCuda<float> MatrixCuda<float>::matmul_optimized(const MatrixCuda<float>& other) const;
|
||||
template MatrixCuda<double> MatrixCuda<double>::matmul_optimized(const MatrixCuda<double>& other) const;
|
||||
|
||||
// ============================================================================
|
||||
// 使用cuBLAS的矩阵乘法(如果可用)
|
||||
// ============================================================================
|
||||
|
||||
#ifdef HAVE_CUBLAS
|
||||
#include <cublas_v2.h>
|
||||
|
||||
template <typename T>
|
||||
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::matmul_cublas(const MatrixCuda<T>& other) const {
|
||||
assert_dot_shape(other);
|
||||
|
||||
MatrixCuda<T> result(rows_, other.cols_);
|
||||
|
||||
// 确保数据在设备上
|
||||
sync_host_to_device();
|
||||
other.sync_host_to_device();
|
||||
result.allocate_device_memory();
|
||||
|
||||
// 创建cuBLAS句柄
|
||||
cublasHandle_t handle;
|
||||
cublasCreate(&handle);
|
||||
|
||||
// 设置cuBLAS参数
|
||||
T alpha = T(1);
|
||||
T beta = T(0);
|
||||
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
|
||||
other.cols_, rows_, cols_,
|
||||
&alpha,
|
||||
other.device_data_, other.cols_,
|
||||
device_data_, cols_,
|
||||
&beta,
|
||||
result.device_data_, other.cols_);
|
||||
} else if constexpr (std::is_same_v<T, double>) {
|
||||
cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
|
||||
other.cols_, rows_, cols_,
|
||||
&alpha,
|
||||
other.device_data_, other.cols_,
|
||||
device_data_, cols_,
|
||||
&beta,
|
||||
result.device_data_, other.cols_);
|
||||
}
|
||||
|
||||
// 销毁cuBLAS句柄
|
||||
cublasDestroy(handle);
|
||||
|
||||
CHECK_CUDA_ERROR(cudaGetLastError());
|
||||
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
|
||||
|
||||
result.device_modified_ = true;
|
||||
result.sync_device_to_host();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 显式实例化cuBLAS矩阵乘法
|
||||
template MatrixCuda<float> MatrixCuda<float>::matmul_cublas(const MatrixCuda<float>& other) const;
|
||||
template MatrixCuda<double> MatrixCuda<double>::matmul_cublas(const MatrixCuda<double>& other) const;
|
||||
|
||||
#endif // HAVE_CUBLAS
|
||||
|
||||
// ============================================================================
|
||||
// 测试函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 测试MatrixCuda类的功能
|
||||
*
|
||||
* 这个函数用于验证MatrixCuda类的正确性
|
||||
* 可以单独编译和运行进行测试
|
||||
*/
|
||||
template <typename T>
|
||||
CUDA_HOST void test_matrix_cuda() {
|
||||
std::cout << "Testing MatrixCuda<" << typeid(T).name() << ">..." << std::endl;
|
||||
|
||||
try {
|
||||
// 测试1:创建矩阵
|
||||
std::cout << "Test 1: Creating matrices..." << std::endl;
|
||||
MatrixCuda<T> A = MatrixCuda<T>::zeros(3, 3);
|
||||
MatrixCuda<T> B = MatrixCuda<T>::ones(3, 3);
|
||||
MatrixCuda<T> C = MatrixCuda<T>::identity(3);
|
||||
|
||||
A.print("A (zeros)");
|
||||
B.print("B (ones)");
|
||||
C.print("C (identity)");
|
||||
|
||||
// 测试2:矩阵加法
|
||||
std::cout << "\nTest 2: Matrix addition..." << std::endl;
|
||||
MatrixCuda<T> D = A + B;
|
||||
D.print("D = A + B");
|
||||
|
||||
// 测试3:标量运算
|
||||
std::cout << "\nTest 3: Scalar operations..." << std::endl;
|
||||
MatrixCuda<T> E = B * T(2.5);
|
||||
E.print("E = B * 2.5");
|
||||
|
||||
// 测试4:矩阵转置
|
||||
std::cout << "\nTest 4: Matrix transpose..." << std::endl;
|
||||
MatrixCuda<T> F(2, 3);
|
||||
F.fill(T(1));
|
||||
F.print("F (2x3)");
|
||||
MatrixCuda<T> FT = F.transpose();
|
||||
FT.print("F^T (3x2)");
|
||||
|
||||
// 测试5:元素对应乘积
|
||||
std::cout << "\nTest 5: Hadamard product..." << std::endl;
|
||||
MatrixCuda<T> G = MatrixCuda<T>::random(2, 2, T(0), T(1));
|
||||
MatrixCuda<T> H = MatrixCuda<T>::random(2, 2, T(0), T(1));
|
||||
G.print("G");
|
||||
H.print("H");
|
||||
MatrixCuda<T> I = G.hadamard_product(H);
|
||||
I.print("G ⊙ H");
|
||||
|
||||
// 测试6:矩阵乘法
|
||||
std::cout << "\nTest 6: Matrix multiplication..." << std::endl;
|
||||
MatrixCuda<T> J(2, 3);
|
||||
MatrixCuda<T> K(3, 2);
|
||||
J.fill(T(1));
|
||||
K.fill(T(2));
|
||||
J.print("J (2x3)");
|
||||
K.print("K (3x2)");
|
||||
MatrixCuda<T> L = J * K;
|
||||
L.print("J * K (2x2)");
|
||||
|
||||
std::cout << "All tests passed!" << std::endl;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Test failed: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 显式实例化测试函数
|
||||
template void test_matrix_cuda<float>();
|
||||
template void test_matrix_cuda<double>();
|
||||
|
||||
} // namespace qing
|
||||
|
||||
// ============================================================================
|
||||
// 主测试程序(可选)
|
||||
// ============================================================================
|
||||
|
||||
#ifdef TEST_MATRIX_CUDA
|
||||
|
||||
int main() {
|
||||
std::cout << "=== MatrixCuda Test Program ===" << std::endl;
|
||||
|
||||
// 初始化CUDA
|
||||
cudaError_t err = cudaSetDevice(0);
|
||||
if (err != cudaSuccess) {
|
||||
std::cerr << "Failed to set CUDA device: " << cudaGetErrorString(err) << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 测试float版本
|
||||
qing::test_matrix_cuda<float>();
|
||||
|
||||
std::cout << "\n";
|
||||
|
||||
// 测试double版本
|
||||
qing::test_matrix_cuda<double>();
|
||||
|
||||
// 重置CUDA设备
|
||||
cudaDeviceReset();
|
||||
|
||||
std::cout << "\n=== Test Program Finished ===" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // TEST_MATRIX_CUDA
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "nn/NNBuilder.hpp"
|
||||
namespace qing {
|
||||
|
||||
void NNBuilder::add(NeuralNetwork &layer) { nn.push_back(layer); }
|
||||
|
||||
std::vector<std::pair<long, long>> NNBuilder::get_shape() const {
|
||||
std::vector<std::pair<long, long>> shape;
|
||||
for (long j = 0; j < nn.size(); ++j) {
|
||||
shape.emplace_back(nn[j].get_inputno(), nn[j].get_outputno());
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
void NNBuilder::print_shape() const {
|
||||
auto shape = get_shape();
|
||||
for (const auto& p : shape) {
|
||||
std::cout << p.first << " -> " << p.second << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* 前向反馈 */
|
||||
std::vector<float> NNBuilder::forward(std::vector<float>& r) {
|
||||
auto x = r;
|
||||
for (long j=0; j< nn.size(); ++j) {
|
||||
x = nn[j].forward(x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/* 反向传播 */
|
||||
std::vector<float> NNBuilder::backward(std::vector<float>& errs, float discount) {
|
||||
auto e = errs;
|
||||
for (long j=nn.size()-1; j>=0;--j) {
|
||||
e = nn[j].backward(e);
|
||||
nn[j].update(discount);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/* 计算误差 */
|
||||
std::vector<float> NNBuilder::cal_err(std::vector<float>& r, std::vector<float>& t) {
|
||||
auto res = r;
|
||||
for (long j=0; j<r.size(); ++j) {
|
||||
res[j] = t[j] - r[j];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
#include "nn/NeuralNetwork.hpp"
|
||||
namespace qing{
|
||||
/* 构造函数 使用基本参数构造 */
|
||||
NeuralNetwork::NeuralNetwork(
|
||||
const long inputno, const long outputno,
|
||||
const std::vector<float> &weights,
|
||||
const std::vector<float> &bias,
|
||||
const float learning_rate,
|
||||
const ActivationFunc func_type
|
||||
): inputno(inputno), outputno(outputno),
|
||||
weights(inputno, outputno, weights), bias( 1, outputno, bias),
|
||||
learning_rate(learning_rate),
|
||||
f_type(func_type) {
|
||||
;/* 进行形状检查 */
|
||||
}
|
||||
|
||||
/* 获取输入层大小 */
|
||||
long NeuralNetwork::get_inputno() const {
|
||||
return this->inputno;
|
||||
}
|
||||
|
||||
/* 获取输出层大小 */
|
||||
long NeuralNetwork::get_outputno() const {
|
||||
return this->outputno;
|
||||
}
|
||||
|
||||
/* 获取权重 */
|
||||
Matrx<float>& NeuralNetwork::get_weights() {
|
||||
return this->weights;
|
||||
}
|
||||
|
||||
/* 获取偏置 */
|
||||
Matrx<float>& NeuralNetwork::get_bias() {
|
||||
return this->bias;
|
||||
}
|
||||
|
||||
/* 正态分布Xavier初始化 */
|
||||
void NeuralNetwork::xavierNormalInit(std::vector<float> &weights, long fan_in, long fan_out)
|
||||
{
|
||||
|
||||
/* 高质量的随机数生成器 */
|
||||
static std::mt19937 generator(std::random_device{}());
|
||||
|
||||
/* 计算标准差 */
|
||||
float sigma = std::sqrt(2.0f / (fan_in + fan_out));
|
||||
|
||||
/* 创建正态分布对象,均值为0,标准差为sigma */
|
||||
std::normal_distribution<float> distribution(0.0f, sigma);
|
||||
|
||||
/* 遍历权重矩阵,进行赋值 */
|
||||
long k=0;
|
||||
for (long i=0; i<fan_in; ++i)
|
||||
for (long j=0; j<fan_out; ++j)
|
||||
weights[k++] = distribution(generator);
|
||||
|
||||
}
|
||||
|
||||
/* 均匀分布Xavier初始化函数 */
|
||||
void NeuralNetwork::xavierUniformInit(std::vector<float> &weights, int fan_in, int fan_out)
|
||||
{
|
||||
/* 使用高质量的随机数生成器 */
|
||||
static std::mt19937 generator(std::random_device{}());
|
||||
|
||||
/* 计算区间范围:sqrt(2 / (fan_in + fan_out)) */
|
||||
float scale = std::sqrt(2.0f / (fan_in + fan_out));
|
||||
|
||||
/* 创建均匀分布对象,范围为[-scale, scale] */
|
||||
std::uniform_real_distribution<float> distribution(-scale, scale);
|
||||
|
||||
/* 遍历权重矩阵,进行赋值 */
|
||||
long k=0;
|
||||
for (long i=0; i<fan_in; ++i)
|
||||
for (long j=0; j<fan_out; ++j)
|
||||
weights[k++] = distribution(generator);
|
||||
}
|
||||
|
||||
/* 工厂模式 通过形状构建全连接层 */
|
||||
NeuralNetwork NeuralNetwork::Create_in_Factory(const long inputno, const long outputno, float learning_rate, ActivationFunc func_type)
|
||||
{
|
||||
std::vector<float> weights(inputno * outputno);
|
||||
xavierUniformInit(weights, inputno, outputno);
|
||||
std::vector<float> bias(outputno);
|
||||
NeuralNetwork layer(inputno, outputno, weights, bias, learning_rate, func_type);
|
||||
return layer; /* FIXME: 使用移动语义 */
|
||||
}
|
||||
|
||||
|
||||
/* 前向反馈 */
|
||||
std::vector<float> NeuralNetwork::forward(const std::vector<float>& x)
|
||||
|
||||
{
|
||||
|
||||
|
||||
/*
|
||||
* 矩阵乘法(点积)
|
||||
* wi => weights inputs(column)
|
||||
* wo => weights outputs(row)
|
||||
*
|
||||
* NOTE: 通常矩阵乘法是三层循环,这里我们用两层
|
||||
*/
|
||||
//std::vector<float> res(this->outputno); /* (1, outputs) */
|
||||
//for (long i=0; i<this->outputno; ++i) {
|
||||
// long wo = i;
|
||||
// float sum=0.0;
|
||||
// for (long j=0, wi=wo; j<this->inputno; ++j, wi+=this->outputno) {
|
||||
// sum += x[j] * this->weights[wi];
|
||||
// }
|
||||
// res[i]= sum;
|
||||
//}
|
||||
Matrx<float>mx{1, this->inputno, x};
|
||||
|
||||
|
||||
/*
|
||||
* 为输入进行备份
|
||||
*
|
||||
* FIXME: 使用移动语义传入参数,并且储存
|
||||
*/
|
||||
this->prev_inputs = mx; /* (1, inputs) */
|
||||
|
||||
|
||||
Matrx<float>r = mx * this->weights; /* (1, outputs) */
|
||||
|
||||
/* 偏置操作 */
|
||||
//for (long i=0; i<this->outputno; ++i) {
|
||||
// res[i]+=this->bias[i];
|
||||
//}
|
||||
r = r + this->bias;
|
||||
|
||||
/*
|
||||
* 为原始计算结果进行备份
|
||||
*/
|
||||
this->prev_raw_inputs = r;
|
||||
|
||||
/*
|
||||
* 使用ReLU激活函数
|
||||
*
|
||||
* FIXME: 建立一个算法类
|
||||
*/
|
||||
f_t f = getf(f_type);
|
||||
|
||||
std::vector<float> vec =r.get_data();
|
||||
for (long i=0; i<this->outputno; ++i) {
|
||||
float& v = vec[i];
|
||||
v = f(v);
|
||||
}
|
||||
|
||||
r = Matrx<float>(1, outputno, vec);
|
||||
|
||||
/*
|
||||
* 为输出进行备份
|
||||
*/
|
||||
this->prev_outputs = r;
|
||||
|
||||
|
||||
/* 返回前向反馈的结果。 (1, outputs) */
|
||||
return r.get_data();
|
||||
}
|
||||
|
||||
|
||||
/* 反向传播 */
|
||||
std::vector<float> NeuralNetwork::backward(const std::vector<float>& errors) /* errors => (outputs, 1) */
|
||||
|
||||
{
|
||||
|
||||
/* 匹配激活导数 */
|
||||
f_t fdot = getfdot(f_type);
|
||||
|
||||
std::vector<float> vec = this->prev_raw_inputs.get_data();
|
||||
/* 将原始输出转化为导数 */
|
||||
for (long i=0; i<this->outputno; ++i) {
|
||||
float& v = vec[i];
|
||||
v = errors[i] * fdot(v);
|
||||
}
|
||||
|
||||
/* 误差乘以导数 */
|
||||
//std::vector<float> first(this->outputno);
|
||||
//for (long i=0; i<this->outputno; ++i) {
|
||||
// first[i] = errors[i] * this->prev_raw_inputs[i];
|
||||
//}
|
||||
Matrx<float> first{1, this->outputno, vec};
|
||||
|
||||
/* 更新权重梯度 => (errors * 导数) @ this->inputs => (1, outputs ) @ (inputs, 1) = (outputs, inputs) */
|
||||
//this->weights_grad = std::vector<float>(this->outputno * this->inputno);
|
||||
//long k = 0;
|
||||
//for (long i=0; i<this->inputno; ++i) {
|
||||
// for (long j=0; j<this->outputno; ++j) {
|
||||
// this->weights_grad[k++] = first[j] * this->prev_inputs[i];
|
||||
// }
|
||||
//}
|
||||
Matrx<float> mx {this->inputno, 1, this->prev_inputs.get_data()}; /* (inputs, 1) */
|
||||
this->weights_grad = mx * first; /* (inputs, 1) @ (1, outputs) => (inputs, outputs) */
|
||||
|
||||
/*
|
||||
* 更新偏置梯度
|
||||
*
|
||||
* NOTE: 在多批次情况下,应该沿着每一样本纵向求平均数
|
||||
*/
|
||||
//this->bias_grad = std::vector<float>(this->outputno);
|
||||
//for (long i=0; i<this->outputno; ++i) {
|
||||
// this->bias_grad[i] = first[i];
|
||||
//}
|
||||
this->bias_grad = first;
|
||||
|
||||
|
||||
/*
|
||||
* 计算上一层的误差责任分担,然后返回
|
||||
*
|
||||
* this->weights @ first => (outputs, inputs) @ (1, outputs) = (inputs, 1)
|
||||
*/
|
||||
//std::vector<float> uplevel_errors(this->inputno);
|
||||
//for (long i=0; i<this->inputno; ++i) {
|
||||
// long wii = i * this->outputno; /* 每一列的起始点 */
|
||||
// float sum = 0.0f;
|
||||
// for (long j=0, wi=wii; j < this->outputno; ++j, ++wi) {
|
||||
// sum += this->weights[wi] * first[j];
|
||||
// }
|
||||
// uplevel_errors[i] = sum;
|
||||
//}
|
||||
Matrx<float> first1{this->outputno, 1, first.get_data()};
|
||||
Matrx<float> uplevel_errs = this->weights * first1; /* (inputs, outputs) @ (outputs, 1) */
|
||||
return uplevel_errs.get_data();
|
||||
|
||||
/* 使用移动语义返回上一层误差 */
|
||||
//return std::move(uplevel_errors);
|
||||
}
|
||||
|
||||
void NeuralNetwork::update(float discount) {
|
||||
this->weights = this->weights + (this->weights_grad * this->learning_rate * discount);
|
||||
this->bias = this->bias + (this->bias_grad * this->learning_rate * discount);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user