需求描述
- 在HDMI扩展屏上连续播放32位深度png图片
- png分辨率1080x800
- 播放间隔毫秒级(1~100毫秒)
解决思路
【方案1】每次播放图片时从磁盘现加载当前图片,绘制播放
- 缺点:加载动作耗时,导致播放卡顿,难以达到毫秒级间隔。
【方案2】提前把所有png图片加载到内存,并且绘制成QPixmap
- 缺点:单张png图片对应QPixmap内存为1080x800x4 byte(3.375M),若总共1000张图片就需要3G内存,且预加载时间长
【方案3】采用环形队列缓冲区,先预加载队列深度(50)张图片,然后再播放图片的同时,另开一个线程从磁盘加载图片填充缓冲区
环形队列
用vector实现环形队列,定义一个QPixmap类型的QVector容器,用于存储位图图像。
QVector<QPixmap> SliceBuffer;
设置队列深度,预先从磁盘加载少量位图图像。
#define SliceDataBufferLength 50
设置图片序号变量,记录当前需要从磁盘加载的图片序号
int wSliceIndexToRead = 0;
预先从磁盘加载位图,填满缓冲区
SliceBuffer.clear();
for (int i = 0; i < SliceDataBufferLength; i++)
{
QPixmap pixmap = QPixmap(tr("%1/%2.png").arg(ui->lineEdit_PicPath->text()).arg(i, 4, 10, QChar('0')));
SliceBuffer.push_back(pixmap);
}
wSliceIndexToRead = SliceDataBufferLength;
填充线程
【初始化线程】(重点关注红色加粗内容)
if (m_writeBufferThread)
{
return;
}
m_writeBufferThread = new QThread();
m_writeBufferObj->moveToThread(m_writeBufferThread);
connect(m_writeBufferThread, &QThread::finished, m_writeBufferThread, &QObject::deleteLater);
connect(m_writeBufferThread, &QThread::finished, m_writeBufferObj, &QObject::deleteLater);
connect(this, &MainWindow::startWriteBuffer, m_writeBufferObj, &WriteSliceBufferThreadObject::writeBuffer);
connect(m_writeBufferObj, &WriteSliceBufferThreadObject::message, this, &MainWindow::receiveMessage);
m_writeBufferThread->start();
【填充操作】
void WriteSliceBufferThreadObject::writeBuffer()
{
while (1)
{
int tempWReadPoint = wReadPoint;
if ((wWritePoint + 1) % SliceDataBufferLength != tempWReadPoint)//缓冲区未满
{
SliceBuffer[wWritePoint % SliceDataBufferLength] = QPixmap(tr("%1/%2.png").arg(m_slicePicPath).arg(wSliceIndexToRead++, 4, 10, QChar('0')));
wWritePoint = (wWritePoint + 1) % SliceDataBufferLength;
}
}
}
播放线程
【初始化线程】(重点关注红色加粗内容)
void MainWindow::startBuildThread()
{
if (m_buildThread)
{
return;
}
m_buildThread = new QThread();
m_buildObj->moveToThread(m_buildThread);
connect(m_buildThread, &QThread::finished, m_buildThread, &QObject::deleteLater);
connect(m_buildThread, &QThread::finished, m_buildObj, &QObject::deleteLater);
connect(this, &MainWindow::startPrint, m_buildObj, &PrintBuildThreadObject::buildRun);
/*connect(m_buildObj, &PrintBuildThreadObject::message, this, &MainWindow::receiveMessage);
connect(m_buildObj, &PrintBuildThreadObject::progress, this, &MainWindow::updateProgress);
connect(m_buildObj, &PrintBuildThreadObject::complete, this, &MainWindow::completeBuild);*/
m_buildThread->start();
}
//打印线程设置
if (m_buildObj)
{
//m_buildObj->resetSerialPort(ui->comboBox_SerialPort->currentText());
m_buildObj->setGcodePath(ui->lineEdit_GCode->text());
connect(m_buildObj, &PrintBuildThreadObject::message, this, &MainWindow::receiveMessage);
connect(m_buildObj, &PrintBuildThreadObject::progress, this, &MainWindow::updateProgress);
connect(m_buildObj, &PrintBuildThreadObject::complete, this, &MainWindow::completeBuild);
}
【播放操作】(重点关注红色加粗内容)
void PrintBuildThreadObject::buildRun()
{
{
QMutexLocker locker(&m_stopMutex);
m_isStop = false;
}
QFile file(m_gcodefile);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::warning(NULL,tr("Error"), tr("read gcode file error:%1").arg(file.errorString()));
}
QTextStream in(&file);
LARGE_INTEGER large_interger;
double dff;
__int64 c0,c1, c2, c3 = 0,c4;
QueryPerformanceFrequency(&large_interger);
dff = large_interger.QuadPart;
QueryPerformanceCounter(&large_interger);
c0 = large_interger.QuadPart;
Singleton::getInstance()->m_canContinue = true;
while (!in.atEnd())
{
{
QMutexLocker locker(&m_stopMutex);
if (m_isStop)
return;
}
QString line;
if (Singleton::getInstance()->m_canContinue)
line = in.readLine();
else
line = "Idle";
if (line.startsWith(";<Slice>"))
{
//qDebug() << tr("show pic:%1").arg(line.right(line.length() - QString(";<Slice>").length()).trimmed());
emit progress(line.right(line.length() - 8).toInt());
emit message(tr("show pic:%1").arg(line.right(line.length() - QString(";<Slice>").length()).trimmed()));
}
else if (line.startsWith(";<Delay>"))
{
int delay = line.right(line.length() - QString(";<Delay>").length()).trimmed().toInt();
QueryPerformanceCounter(&large_interger);
c1 = large_interger.QuadPart;
QueryPerformanceCounter(&large_interger);
c2 = large_interger.QuadPart;
while (((c2 - c1) * 1000 / dff) < delay)
{
QueryPerformanceCounter(&large_interger);
c2 = large_interger.QuadPart;
}
}
else if (line.startsWith("G1"))
{
/*QByteArray ba = line.trimmed().toLatin1();
char* chmm = ba.data();
Singleton::getInstance()->m_canContinue = false;
Singleton::getInstance()->m_extSerialPort->write(chmm);*/
char *sendBuffer = (char*)malloc(FrameLengthWithoutData + 3 + line.trimmed().length()+2);
SerialPortProtocol::CodingSerialport(sendBuffer, MotorControl, G1Motion, Request, line.trimmed().length(), line.trimmed().toLatin1().data());
if (Singleton::getInstance()->m_extSerialPort != NULL)
{
emit message(QString::fromLocal8Bit("%1:发送%2命令").arg(Singleton::getCurrentTime()).arg(line.trimmed()));
if(Singleton::getInstance()->m_noneedAnswer==false)
Singleton::getInstance()->m_canContinue = false;
sendBuffer[FrameLengthWithoutData + 3 + line.trimmed().length()] = 0x0d;
sendBuffer[FrameLengthWithoutData + 3 + line.trimmed().length() + 1] = 0x0a;
Singleton::getInstance()->m_extSerialPort->write(sendBuffer, FrameLengthWithoutData + 3 + line.trimmed().length()+2);
}
delete sendBuffer;
//emit message(QString::fromLocal8Bit("%1:已发送G1命令").arg(Singleton::getCurrentTime()));
QueryPerformanceCounter(&large_interger);
c2 = large_interger.QuadPart;
if (c3 != 0)
//qDebug() << tr("time intevl:%1").arg((c2 - c3) * 1000 / dff);
emit message(tr("time intevl:%1").arg((c2 - c3) * 1000 / dff));
c3 = c2;
}
}
QueryPerformanceCounter(&large_interger);
c4 = large_interger.QuadPart;
int secs = (c4 - c0) * 1000 / dff / 1000;
QString slaspetime = tr("total time:%1 min %2 sec").arg(secs / 60).arg(secs % 60);
emit message(slaspetime);
emit complete();
file.close();
}
void MainWindow::updateProgress(int value)
{
ui->progressBar->setValue(value);
//m_pic->setPixmap(SliceBuffer.at(value % SliceDataBufferLength));
int itempWritePoint = wWritePoint;
if (wReadPoint % SliceDataBufferLength != itempWritePoint)
{
//m_pic->setPixmap(SliceBuffer.at(wReadPoint));
Singleton::getInstance()->m_projector->showPixmap(SliceBuffer.at(wReadPoint));
wReadPoint = (wReadPoint + 1) % SliceDataBufferLength;
}
//m_pic->showMaximized();
}