3

我需要绘制由库保存的像素数据,这些数据uint8_t *经常和部分更新。每次更新完成时,我都会从库中收到回调,如下所示:

void gotFrameBufferUpdate(int x, int y, int w, int h);

我尝试使用像素数据指针创建QImage

QImage bufferImage(frameBuffer, width, height, QImage::Format_RGBX8888);

update()并让我的小部件的回调触发

void gotFrameBufferUpdate(int x, int y, int w, int h)
{
    update(QRect(QPoint(x, y), QSize(w, h)));
}

它只是通过以下方式绘制QImagepaint()的更新区域:

void MyWidget::paint(QPainter *painter)
{
    QRect rect = painter->clipBoundingRect().toRect();
    painter->drawImage(rect, bufferImage, rect);
}

这种方法的问题是QImage似乎没有反映对像素缓冲区的任何更新。它不断显示其初始内容。

我当前的解决方法是每次更新缓冲区时重新创建一个QImage实例:

void gotFrameBufferUpdate(int x, int y, int w, int h)
{
    if (bufferImage)
        delete bufferImage;
    bufferImage = new QImage(frameBuffer, width, height,
                             QImage::Format_RGBX8888);

    update(QRect(QPoint(x, y), QSize(w, h)));
}

这可行,但对我来说似乎效率很低。有没有更好的方法来处理 Qt 中外部更新的像素数据?我可以让我的QImage知道其内存缓冲区的更新吗?

(背景:我正在使用 C++ 后端编写一个自定义 QML 类型,该类型应显示 VNC 会话的内容。我为此使用LibVNC/libvncclient。)

4

3 回答 3

1

如果调用 QImage::bits(),QImage 就会更新。

它不会分配新的缓冲区,您可以丢弃结果,但它会神奇地触发图像的刷新。每次您想要刷新时都需要它。

我不知道这是否是有保证的行为,也不知道它是否在重新创建它时节省了任何东西。

于 2021-01-14T15:45:44.070 回答
0

我猜想某种缓存机制会干扰您的期望。QImage 有一个cacheKey,如果改变了它QImage就会改变。当然,这只有在您通过QImage函数更改图像时才会发生。据我所知,您正在直接更改底层缓冲区,因此QImage'scacheKey将保持不变。Qt 的像素图缓存在其缓存中拥有该键,并出于性能原因使用缓存的像素图。

不幸的是,似乎没有直接的方法来更新这个cacheKey或以其他方式“无效” a QImage。你有两个选择:

  1. QImage需要时重新创建。不需要new它,因此您可以节省堆分配。创建一个后缓冲QImage似乎是一个“便宜”的操作,所以我怀疑这是一个瓶颈。
  2. QImage对(即setPixel在单个像素上变为黑色,然后变为旧值)做一个简单的操作。这有点“骇人听闻”,但可能是解决此 API 缺陷的最有效方法(cacheKey据我所知,它将触发更新)。
于 2019-04-26T14:56:09.047 回答
0

AFAICT 这个QImage类已经按照你认为应该的方式工作了——特别是,简单地写入外部帧缓冲区实际上会更新QImage. 我的猜测是,在您的程序中,其他一些代码正在将QImage数据复制到QPixmap内部某处(因为 aQPixmap将始终以硬件的本机格式存储其内部缓冲区,因此重复绘制到屏幕上会更有效率)和就是QPixmap当 frameBuffer 更新时,它没有被修改。

作为 aQImage实际上总是包含来自 frameBuffer 的数据的证据,这里有一个程序,每次单击窗口时都会将新颜色写入其帧缓冲区,然后调用update()以强制小部件重新绘制自身。我看到小部件在每次单击鼠标时都会改变颜色:

#include <stdio.h>
#include <stdint.h>
#include <QPixmap>
#include <QWidget>
#include <QApplication>
#include <QPainter>

const int width = 500;
const int height = 500;
const int frameBufferSizeBytes = width*height*sizeof(uint32_t);
unsigned char * frameBuffer = NULL;

class MyWidget : public QWidget
{
public:
   MyWidget(QImage * img) : _image(img) {/* empty */}
   virtual ~MyWidget() {delete _image;}

   virtual void paintEvent(QPaintEvent * e)
   {
      QPainter p(this);
      p.drawImage(rect(), *_image);
   }

   virtual void mousePressEvent(QMouseEvent * e)
   {
      const uint32_t someColor = rand();
      const size_t frameBufferSizeWords = frameBufferSizeBytes/sizeof(uint32_t);
      uint32_t * fb32 = (uint32_t *) frameBuffer;
      for (size_t i=0; i<frameBufferSizeWords; i++) fb32[i] = someColor;

      update();
   }

private:
   QImage * _image;
};

int main(int argc, char ** argv)
{
   QApplication app(argc, argv);

   frameBuffer = new unsigned char[frameBufferSizeBytes];
   memset(frameBuffer, 0xFF, frameBufferSizeBytes);

   QImage * img = new QImage(frameBuffer, width, height, QImage::Format_RGBX8888);
   MyWidget * w = new MyWidget(img);
   w->resize(width, height);
   w->show();

   return app.exec();
}
于 2019-04-26T15:06:43.810 回答