Qt: Как сделать QImage осведомленным об обновленном буфере памяти? - PullRequest
3 голосов
/ 26 апреля 2019

Мне нужно нарисовать данные пикселей, которые хранятся в библиотеке как 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)));
}

, который просто рисует обновленную область QImage через paint():

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 об обновлениях его буфера памяти?

(Справочная информация: я пишу пользовательский тип QML с бэкэндом C ++, который должен отображать содержимое сеанса VNC. IДля этого использую LibVNC / libvncclient .)

Ответы [ 2 ]

0 голосов
/ 26 апреля 2019

AFAICT класс QImage уже работает так, как вы думаете - в частности, простая запись во внешний буфер кадра фактически обновляет содержимое QImage. Я предполагаю, что в вашей программе какой-то другой фрагмент кода копирует данные QImage во внутреннюю область QPixmap (поскольку QPixmap всегда будет хранить свой внутренний буфер в собственном формате аппаратного обеспечения и, следовательно, будет более эффективно рисовать на экране несколько раз), и это QPixmap, который не изменяется при обновлении frameBuffer.

В качестве доказательства того, что 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();
}
0 голосов
/ 26 апреля 2019

Я бы предположил, что какой-то механизм кэширования мешает вашим ожиданиям. QImage имеет cacheKey, который изменяется, если QImage изменяется. Это, конечно, может произойти, только если вы измените изображение с помощью QImage функций. Насколько я вижу, вы напрямую меняете базовый буфер, поэтому QImage cacheKey останется прежним. Кэш растрового изображения Qt затем имеет этот ключ в своем кеше и использует кэшированное растровое изображение из соображений производительности.

К сожалению, нет прямого способа обновить cacheKey или иначе "сделать недействительным" QImage. У вас есть два варианта:

  1. Воссоздайте QImage, когда вам это нужно. Нет необходимости new, так что вы можете сохранить выделение кучи. Создание буферизованной QImage кажется «дешевой» операцией, поэтому я сомневаюсь, что это узкое место.
  2. Выполните тривиальную операцию с QImage (т. Е. setPixel на одном пикселе до черного, а затем до старого значения). Это несколько «хакерский», но, вероятно, самый эффективный способ обойти этот недостаток API (насколько я могу судить, это приведет к обновлению до cacheKey).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...