簡體   English   中英

Qt:如何讓 QImage 知道更新的內存緩沖區?

[英]Qt: How to make QImage aware of updated memory buffer?

我需要繪制由庫保存的像素數據作為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)));
}

它只是通過paint() QImage的更新區域:

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 。)

如果調用 QImage::bits(),則 QImage 會更新。

它不會分配新的緩沖區,您可以丟棄結果,但它會神奇地觸發圖像刷新。 每次您想要刷新時都需要它。

我不知道這是否是有保證的行為,也不知道它是否可以通過重新創建來節省任何東西。

我猜想某種緩存機制正在干擾您的期望。 QImage 有一個cacheKey ,如果QImage被改變,它就會改變。 這當然只有在您通過QImage函數更改圖像時才會發生。 據我所知,您正在直接更改底層緩沖區,因此QImagecacheKey將保持不變。 Qt 的像素圖緩存然后在其緩存中具有該鍵,並出於性能原因使用緩存的像素圖。

不幸的是,似乎沒有直接的方法來更新這個cacheKey或以其他方式“無效” QImage 您有兩個選擇:

  1. 需要時重新創建QImage 無需new它,因此您可以節省堆分配。 創建一個后緩沖QImage似乎是一個“便宜”的操作,所以我懷疑這是一個瓶頸。
  2. QImage做一個簡單的操作(即將單個像素上的setPixel設置為黑色,然后設置為舊值)。 這有點“hackish”,但可能是解決此 API 缺陷的最有效方法(據我cacheKey ,它將觸發對cacheKey的更新)。

AFAICT QImage類已經按照您認為的方式工作 - 特別是,簡單地寫入外部幀緩沖區實際上會更新QImage的內容。 我的猜測是,在您的程序中,其他一些代碼正在內部某處將QImage數據復制到QPixmap (因為QPixmap將始終以硬件的本機格式存儲其內部緩沖區,因此在屏幕上繪制會更有效重復),並且在更新 frameBuffer 時不會修改QPixmap

作為QImage實際上始終包含來自 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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM