Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to make QImage aware of updated memory buffer?
QtWS25 Last Chance

How to make QImage aware of updated memory buffer?

Scheduled Pinned Locked Moved Unsolved General and Desktop
6 Posts 2 Posters 412 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    davecotter
    wrote on last edited by
    #1

    I manually twiddle the memory returned by bits(), and i want the cacheKey to be invaliated, so it knows the image cached on the video card is stale. I want to mix QPainter calls with my manual bit-twiddles. There doesn't seem to be a way to do this.

    I have way too much legacy code that does manual bit twiddling and buffer iteration for graphics ops, so it's not an option to just rewrite everything to "use QPainter calls only" on my buffers.

    I saw this post:
    https://stackoverflow.com/questions/55869926/qt-how-to-make-qimage-aware-of-updated-memory-buffer

    but the solution offered seems like a hack, and could be a performance problem too since i think it will copy the bits from the mem buffer back to the video card every time?

    Is there or will there be a sanctioned method for this?

    C 1 Reply Last reply
    0
    • D davecotter

      I manually twiddle the memory returned by bits(), and i want the cacheKey to be invaliated, so it knows the image cached on the video card is stale. I want to mix QPainter calls with my manual bit-twiddles. There doesn't seem to be a way to do this.

      I have way too much legacy code that does manual bit twiddling and buffer iteration for graphics ops, so it's not an option to just rewrite everything to "use QPainter calls only" on my buffers.

      I saw this post:
      https://stackoverflow.com/questions/55869926/qt-how-to-make-qimage-aware-of-updated-memory-buffer

      but the solution offered seems like a hack, and could be a performance problem too since i think it will copy the bits from the mem buffer back to the video card every time?

      Is there or will there be a sanctioned method for this?

      C Offline
      C Offline
      ChrisW67
      wrote on last edited by
      #2

      @davecotter QImage is an in-memory representation of an image. The underlying buffer is not directly connected to any video memory. The software (yours and the display manager's) cannot avoid copying data from the QImage in the process of displaying it: scaling, cropping, colour depth conversion, bit ordering, composition, all sorts... Exactly what is happening depends on how it gets to a screen. Since you do not divulge how this QImage gets onto a screen there's no specifics we can help with.

      The offered solution creates a new QImage object without copying the buffer, e.g.:

      QImage image(m_buffer.data(), _width, _height, QImage::Format_Mono);
      

      This buffer copy appears to be what you are concerned about, but needn't be.

      1 Reply Last reply
      3
      • D Offline
        D Offline
        davecotter
        wrote on last edited by
        #3

        Okay, the QImage is sent to the screen via QGraphicsPixmapItem to a QGraphicsScene.
        When i call pix(), the bits change, as if they get copied back from the graphics card?

        1 Reply Last reply
        0
        • D Offline
          D Offline
          davecotter
          wrote on last edited by
          #4

          i tried calling bits() after every API call, but that slowed things down considerably, to like 1fps. without calling bits i get > 30fps.
          I just wish there was a call to invalidate the cache WITHOUT changing my bits, my bits should stay the same, but they CHANGE, why is that? i want to twiddle my bits, and invalidate the cache, and have the bits that I PUT THERE get displayed, then i want to chnage my bits again, and have them display again. but if i call bits(), the buffer i get back is now a DIFFERENT buffer, which confuses me.

          C 1 Reply Last reply
          0
          • D davecotter

            i tried calling bits() after every API call, but that slowed things down considerably, to like 1fps. without calling bits i get > 30fps.
            I just wish there was a call to invalidate the cache WITHOUT changing my bits, my bits should stay the same, but they CHANGE, why is that? i want to twiddle my bits, and invalidate the cache, and have the bits that I PUT THERE get displayed, then i want to chnage my bits again, and have them display again. but if i call bits(), the buffer i get back is now a DIFFERENT buffer, which confuses me.

            C Offline
            C Offline
            ChrisW67
            wrote on last edited by
            #5

            @davecotter As documented, calling the non-const QImage::bits() performs a deep copy i.e. a different buffer. Image uses implicit sharing so:

            QImage image1(256, 256, QImage::Format_RGB32);
            QImage image2 = image1;
            

            results in the two QImage objects sharing the same buffer. If you modify either object using QImage methods then a deep-copy will occur and each copy will become independent. QImage::bits() gives you something you can modify so, if the QImage is sharing a buffer a deep-copy will happen.

            Something like this, where there is only one persistent QImage should avoid the issue:

            // member vars
            QVector<uchar> m_buffer;  // you can bit twiddle in here behind QImage's back
            QImage m_image;
            
            // Constructor
            m_buffer.resize(NUMBER_OF_BYTES_IN_IMAGE);
            m_image = QImage(m_buffer.data(), _width, _height_, QImage::Format_RGB32);
            
            // Update time
            // twiddle bits directly in m_buffer.data().  Do not call QImage::bits()
            
            QPainter p(&m_image);
            // paint in m_image
            
            // update the QGraphicsPixmapItem 
            // Create a QPixmap from a const reference to the image.
            item->setPixmap(QPixmap::fromImage(m_image));
            
            C 1 Reply Last reply
            1
            • C ChrisW67

              @davecotter As documented, calling the non-const QImage::bits() performs a deep copy i.e. a different buffer. Image uses implicit sharing so:

              QImage image1(256, 256, QImage::Format_RGB32);
              QImage image2 = image1;
              

              results in the two QImage objects sharing the same buffer. If you modify either object using QImage methods then a deep-copy will occur and each copy will become independent. QImage::bits() gives you something you can modify so, if the QImage is sharing a buffer a deep-copy will happen.

              Something like this, where there is only one persistent QImage should avoid the issue:

              // member vars
              QVector<uchar> m_buffer;  // you can bit twiddle in here behind QImage's back
              QImage m_image;
              
              // Constructor
              m_buffer.resize(NUMBER_OF_BYTES_IN_IMAGE);
              m_image = QImage(m_buffer.data(), _width, _height_, QImage::Format_RGB32);
              
              // Update time
              // twiddle bits directly in m_buffer.data().  Do not call QImage::bits()
              
              QPainter p(&m_image);
              // paint in m_image
              
              // update the QGraphicsPixmapItem 
              // Create a QPixmap from a const reference to the image.
              item->setPixmap(QPixmap::fromImage(m_image));
              
              C Offline
              C Offline
              ChrisW67
              wrote on last edited by
              #6

              A more complete example:

              #ifndef WIDGET_H
              #define WIDGET_H
              
              #include <QWidget>
              #include <QLabel>
              #include <QImage>
              
              class Widget : public QWidget
              {
                  Q_OBJECT
              
              public:
                  Widget(QWidget *parent = nullptr);
                  ~Widget();
              
              private slots:
                  void updateDisplay();
              
              private:
                  QLabel *m_label;
                  QImage m_image;
                  QVector<uchar> m_buffer;
              };
              #endif // WIDGET_H
              
              #include "widget.h"
              
              #include <QVBoxLayout>
              #include <QLabel>
              #include <QImage>
              #include <QDebug>
              #include <QTimer>
              #include <QRandomGenerator>
              #include <QPainter>
              
              namespace {
                  // 640x480 mono image: 80 bytes per scan line, 480 scan lines
                  int _width(640);
                  int _height(480);
                  int _bufferSize(_width * _height / 8);
              }
              
              Widget::Widget(QWidget *parent)
                  : QWidget(parent)
              {
                  // set up the initial empty image
                  m_buffer.resize(_bufferSize);
                  m_image = QImage(m_buffer.data(), _width, _height, QImage::Format_Mono);
                  m_image.fill(Qt::color0);
                  qDebug() << Q_FUNC_INFO << m_image << m_image.cacheKey();
              
                  // somewhere to display it
                  QVBoxLayout *layout = new QVBoxLayout(this);
                  m_label = new QLabel(this);
                  layout->addWidget(m_label);
                  m_label->setPixmap(QPixmap::fromImage(m_image));
              
                  // generate some demo output
                  QTimer *timer = new QTimer(this);
                  connect(timer, &QTimer::timeout, this, &Widget::updateDisplay);
                  timer->start(500);
              }
              
              Widget::~Widget() {
              }
              
              void Widget::updateDisplay()
              {
                  // modify some bytes in the buffer directly
                  for (qsizetype i = 0; i < 10; ++i) {
                      int index = QRandomGenerator::global()->bounded(m_buffer.size());
                      m_buffer[index] ^= 0xff;
                  }
              
                  // Paint on the same image
                  QPainter p;
                  if (p.begin(&m_image)) {
                      p.setPen(Qt::color1);
                      QPoint a(QRandomGenerator::global()->bounded(_width),
                               QRandomGenerator::global()->bounded(_height));
                      QPoint b(QRandomGenerator::global()->bounded(_width),
                               QRandomGenerator::global()->bounded(_height));
                      p.drawLine(a, b);
                      p.end();
                  }
              
                  qDebug() << Q_FUNC_INFO << "Out" << m_image << m_image.cacheKey();
                  m_label->setPixmap(QPixmap::fromImage(m_image));
              }
              

              c5612693-fd39-469d-ab0f-36e0a5ead8c6-image.png

              1 Reply Last reply
              1
              • C ChrisW67 referenced this topic on

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved