工作中遇到一个问题,就是使用QT作为界面库,但是由于底层编解码在叠加OSD的时候,如果使用argb32(QImage::Format_ARGB32)的时候,4K的图像图片太大,编解码叠加导致性能不足等问题,所以需要图片压缩成argb4444的格式来规避性能问题。不想搞插件,只说说我的做方法,愿意用插件的自己搞。
由于framebuffer映射上来的格式是argb4444,所以上层需要修改QT源代码 qt-everywhere-src-5.14.2/qtbase/src/plugins/platforms/linuxfb下qlinuxfbscreen.cpp文件和qlinuxfbscreen.h文件,
1、使映射到QT上层的framebuffer的格式为QImage::Format_ARGB32,主要修改函数: bool QLinuxFbScreen::initialize()
bool QLinuxFbScreen::initialize()
{
QRegularExpression ttyRx(QLatin1String("tty=(.*)"));
QRegularExpression fbRx(QLatin1String("fb=(.*)"));
QRegularExpression mmSizeRx(QLatin1String("mmsize=(\\d+)x(\\d+)"));
QRegularExpression sizeRx(QLatin1String("size=(\\d+)x(\\d+)"));
QRegularExpression offsetRx(QLatin1String("offset=(\\d+)x(\\d+)"));
QString fbDevice, ttyDevice;
QSize userMmSize;
QRect userGeometry;
bool doSwitchToGraphicsMode = true;
// Parse arguments
for (const QString &arg : qAsConst(mArgs)) {
QRegularExpressionMatch match;
if (arg == QLatin1String("nographicsmodeswitch"))
doSwitchToGraphicsMode = false;
else if (arg.contains(mmSizeRx, &match))
userMmSize = QSize(match.captured(1).toInt(), match.captured(2).toInt());
else if (arg.contains(sizeRx, &match))
userGeometry.setSize(QSize(match.captured(1).toInt(), match.captured(2).toInt()));
else if (arg.contains(offsetRx, &match))
userGeometry.setTopLeft(QPoint(match.captured(1).toInt(), match.captured(2).toInt()));
else if (arg.contains(ttyRx, &match))
ttyDevice = match.captured(1);
else if (arg.contains(fbRx, &match))
fbDevice = match.captured(1);
}
if (fbDevice.isEmpty()) {
fbDevice = QLatin1String("/dev/fb0");
if (!QFile::exists(fbDevice))
fbDevice = QLatin1String("/dev/graphics/fb0");
if (!QFile::exists(fbDevice)) {
qWarning("Unable to figure out framebuffer device. Specify it manually.");
return false;
}
}
// Open the device
mFbFd = openFramebufferDevice(fbDevice);
if (mFbFd == -1) {
qErrnoWarning(errno, "Failed to open framebuffer %s", qPrintable(fbDevice));
return false;
}
// Read the fixed and variable screen information
fb_fix_screeninfo finfo;
fb_var_screeninfo vinfo;
memset(&vinfo, 0, sizeof(vinfo));
memset(&finfo, 0, sizeof(finfo));
//bkspidex
fb_fix_screeninfo finfo32;
fb_var_screeninfo vinfo32;
memset(&vinfo32, 0, sizeof(vinfo32));
memset(&finfo32, 0, sizeof(finfo32));
//bkspidex
if (ioctl(mFbFd, FBIOGET_FSCREENINFO, &finfo) != 0) {
qErrnoWarning(errno, "Error reading fixed information");
return false;
}
if (ioctl(mFbFd, FBIOGET_VSCREENINFO, &vinfo)) {
qErrnoWarning(errno, "Error reading variable information");
return false;
}
mDepth = 32;//determineDepth(vinfo);
mBytesPerLine = finfo.line_length * 2;
QRect geometry = determineGeometry(vinfo, userGeometry);
mGeometry = QRect(QPoint(0, 0), geometry.size());
mFormat = QImage::Format_ARGB32;//determineFormat(vinfo, mDepth);
mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, geometry.size());
// mmap the framebuffer
mMmap.size = finfo.smem_len;
uchar *data = (unsigned char *)mmap(0, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0);
if ((long)data == -1) {
qErrnoWarning(errno, "Failed to mmap framebuffer");
return false;
}
//bkspidex
mMmap.offset = geometry.y() * mBytesPerLine / 2 + geometry.x() * mDepth / 8 / 2;
mMmap.data = data + mMmap.offset;
//bkspidex
data32 = (unsigned char *)malloc(mMmap.size * 2);
if ((long)data32 == 0) {
qErrnoWarning(errno, "Failed to malloc data32");
return false;
}
memset(data32, 0, mMmap.size * 2);
datatemp = (unsigned char *)malloc(mMmap.size);
if ((long)datatemp == 0) {
qErrnoWarning(errno, "Failed to malloc datatemp");
return false;
}
memset(datatemp, 0, mMmap.size);
qDebug() << " mDepth: " << determineDepth(vinfo);
qDebug() << " finfo.line_length: " << finfo.line_length;
qDebug() << " finfo.smem_len: " << finfo.smem_len;
qDebug() << " mMmap.size: " << mMmap.size;
qDebug() << " mMmap.offset: " << mMmap.offset;
qDebug() << " mMmap.data: " << mMmap.data;
qDebug() << " geometry.x(): " << geometry.x();
qDebug() << " geometry.y(): " << geometry.y();
qDebug() << " geometry.size(): " << geometry.size();
qDebug() << " mPhysicalSize: " << mPhysicalSize;
//bkspidex
QFbScreen::initializeCompositor();
//mFbScreenImage = QImage(mMmap.data, geometry.width(), geometry.height(), mBytesPerLine, mFormat);
mFbScreenImage = QImage(data32, geometry.width(), geometry.height(), mBytesPerLine, QImage::Format_ARGB32);
mCursor = new QFbCursor(this);
mTtyFd = openTtyDevice(ttyDevice);
if (mTtyFd == -1)
qErrnoWarning(errno, "Failed to open tty");
switchToGraphicsMode(mTtyFd, doSwitchToGraphicsMode, &mOldTtyMode);
blankScreen(mFbFd, false);
return true;
}
2、将QT写入到framebufferd 数据转化成argb4444,主要修改函数:
QRegion QLinuxFbScreen::doRedraw()
QRegion QLinuxFbScreen::doRedraw()
{
//qDebug() << " doRedraw: " << 1;
QRegion touched = QFbScreen::doRedraw();
//qDebug() << " doRedraw: " << 2;
if (touched.isEmpty())
return touched;
//qDebug() << " doRedraw: " << 3;
if (!mBlitter)
mBlitter = new QPainter(&mFbScreenImage);
//qDebug() << " doRedraw: " << 4;
mBlitter->setCompositionMode(QPainter::CompositionMode_Source);
//qDebug() << " doRedraw: " << 5;
for (const QRect &rect : touched)
mBlitter->drawImage(rect, mScreenImage, rect);
//qDebug() << " doRedraw: " << 6;
//bkspidex
int i = 0;
memset(datatemp, 0, mMmap.size);
uchar* data4444 = datatemp;
uchar* data8888 = data32;
//qDebug() << " doRedraw: " << 7;
for(i = 0; i < (mMmap.size * 2); i += 4)
{
//b g r a
uchar b = *data8888;
data8888++;
uchar g = *data8888;
data8888++;
uchar r = *data8888;
data8888++;
uchar a = *data8888;
data8888++;
*data4444 = ((b >> 4) | ((g >> 4) << 4));
data4444++;
*data4444 = ((r >> 4) | ((a >> 4) << 4));
data4444++;
}
//qDebug() << " doRedraw: " << 8;
memcpy(mMmap.data, datatemp, mMmap.size);
//qDebug() << " doRedraw: " << 9;
return touched;
}
3、完成后需要重启编译QT源代码就行。