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. qt optimize drawing of outlined text
Forum Updated to NodeBB v4.3 + New Features

qt optimize drawing of outlined text

Scheduled Pinned Locked Moved Unsolved General and Desktop
6 Posts 3 Posters 2.9k Views 2 Watching
  • 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
    DaveMilter
    wrote on last edited by
    #1

    I use such code to draw outlined text:

    painter.setPen(Qt::black);
    painter.setBrush(Qt::white);
    QPainterPath ppath;
    ppath.addText({200., 200.}, font(), line);
    painter.drawPath(ppath);
    

    it works, but so slow... For 100 captions it takes 30-50 milleseconds on 4GHz CPU, it is too long, because I want at least 10FPS, and I need much more stuff in addition to drawing of captions.

    Any hint how speedup code bellow? I thought that Qt has font cache, but for some reason 50% of program spend inside libfreetype in TT_RunIns code, while I draw only digits, so I thought that it should cache all glyphs after several iterations. May be I need call some function to turn on font cache? Note for simplicity I draw text on the same positon, in real program of course I do it in the right way.

    #include <QApplication>
    #include <QElapsedTimer>
    #include <QPainter>
    #include <QTimer>
    #include <QWidget>
    #include <algorithm>
    
    struct MyW : public QWidget {
      void paintEvent(QPaintEvent *) override {
        QPainter painter{this};
        painter.fillRect(rect(), Qt::red);
    
        painter.setPen(Qt::black);
        painter.setBrush(Qt::white);
    
        QVector<QString> lines;
        lines.resize(100);
        std::generate(std::begin(lines), std::end(lines),
                      [] { return QString::number(qrand()); });
    
        QElapsedTimer timer;
        timer.start();
        for (const auto &line : lines) {
          QPainterPath ppath;
          ppath.addText({200., 200.}, font(), line);
          painter.drawPath(ppath);
        }
        qInfo("paint text takes %d ms", static_cast<int>(timer.elapsed()));
      }
    };
    
    int main(int argc, char *argv[]) {
      QApplication app(argc, argv);
      MyW w;
      w.resize(600, 800);
      w.show();
      QTimer timer;
      QObject::connect(&timer, &QTimer::timeout, &app, [&w] { w.update(); });
      timer.start(1000);
      return app.exec();
    }
    
    
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      You should profile your code however I think that the generation of your random list of text is one of the part that takes most of the time.

      Do you really expect your application to generate that kind of data every time you paint.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      D 1 Reply Last reply
      1
      • SGaistS SGaist

        Hi,

        You should profile your code however I think that the generation of your random list of text is one of the part that takes most of the time.

        Do you really expect your application to generate that kind of data every time you paint.

        D Offline
        D Offline
        DaveMilter
        wrote on last edited by DaveMilter
        #3

        @SGaist

        You should profile your code

        If you reread my post you see that I already did this.

        but for some reason 50% of program spend inside libfreetype in TT_RunIns code

        however I think that the generation of your random list of text is one of the part that takes most of the time.
        Do you really expect your application to generate that kind of data every time you paint.

        You joking right? As you can see the time of generation was NOT counted, QElapsedTimer is started after all generation done.

        1 Reply Last reply
        0
        • mrjjM Offline
          mrjjM Offline
          mrjj
          Lifetime Qt Champion
          wrote on last edited by mrjj
          #4

          Hi
          since you kindly provided ready to run code i played around with it a bit.
          It seems that
          ppath.addText({200., 200.}, font(), line);
          is pretty heavy.
          I tried with ppath.addRect instead and it much, much faster.
          So im guessing on converting the font info to QPainterPath data is the heavy part and
          if you google TT_RunIns, there is also mention of that being slow outside of Qt.

          So if you only draw numbers 0-9
          you could pre create paths for them and use that ?

          just as a proof of concept. Warning. ugly code!

          
          struct MyW : public QWidget {
              QPainterPath *cache[10] = {nullptr};
              MyW(QWidget *par = nullptr) : QWidget(par)
              {
                  for (int var = 0; var < 10; ++var) {
                      cache[var] = new QPainterPath; // this leaks!
                      cache[var]->addText({0,0}, font(), QString::number(var)); 
                  }
              }
              void paintEvent(QPaintEvent *) override
              {
                  QPainter painter{this};
                  painter.fillRect(rect(), Qt::red);
          
                  painter.setPen(Qt::black);
                  painter.setBrush(Qt::white);
          
                  QVector<QString> lines;
                  lines.resize(100);
                  std::generate(std::begin(lines), std::end(lines),
                                [] { return QString::number(qrand()); });
          
                  QElapsedTimer timer;
                  timer.start();
                  for (const auto &line : lines) {
                      for (int ch = 0; ch < line.length(); ++ch) {
                          const QPainterPath *path = cache[ line.at(ch).digitValue()];
                          painter.drawPath( path->translated(200+(ch*10),200) );
                      }
                  }
                  qInfo("paint text takes %d ms", static_cast<int>(timer.elapsed()));
              }
          };
          

          note it leaks the paths! and the digit placement is off.
          Just for speed test. this is on my pc
          3 ms where the original code is 30-35.

          However, it might not be feasible for your real app if its not just numbers.

          D 1 Reply Last reply
          2
          • mrjjM mrjj

            Hi
            since you kindly provided ready to run code i played around with it a bit.
            It seems that
            ppath.addText({200., 200.}, font(), line);
            is pretty heavy.
            I tried with ppath.addRect instead and it much, much faster.
            So im guessing on converting the font info to QPainterPath data is the heavy part and
            if you google TT_RunIns, there is also mention of that being slow outside of Qt.

            So if you only draw numbers 0-9
            you could pre create paths for them and use that ?

            just as a proof of concept. Warning. ugly code!

            
            struct MyW : public QWidget {
                QPainterPath *cache[10] = {nullptr};
                MyW(QWidget *par = nullptr) : QWidget(par)
                {
                    for (int var = 0; var < 10; ++var) {
                        cache[var] = new QPainterPath; // this leaks!
                        cache[var]->addText({0,0}, font(), QString::number(var)); 
                    }
                }
                void paintEvent(QPaintEvent *) override
                {
                    QPainter painter{this};
                    painter.fillRect(rect(), Qt::red);
            
                    painter.setPen(Qt::black);
                    painter.setBrush(Qt::white);
            
                    QVector<QString> lines;
                    lines.resize(100);
                    std::generate(std::begin(lines), std::end(lines),
                                  [] { return QString::number(qrand()); });
            
                    QElapsedTimer timer;
                    timer.start();
                    for (const auto &line : lines) {
                        for (int ch = 0; ch < line.length(); ++ch) {
                            const QPainterPath *path = cache[ line.at(ch).digitValue()];
                            painter.drawPath( path->translated(200+(ch*10),200) );
                        }
                    }
                    qInfo("paint text takes %d ms", static_cast<int>(timer.elapsed()));
                }
            };
            

            note it leaks the paths! and the digit placement is off.
            Just for speed test. this is on my pc
            3 ms where the original code is 30-35.

            However, it might not be feasible for your real app if its not just numbers.

            D Offline
            D Offline
            DaveMilter
            wrote on last edited by DaveMilter
            #5

            @mrjj

            3 ms where the original code is 30-35.

            I don't see so huge difference on my machine,
            I enable permanently turbo-boost for one core, to make time measurements more fair,
            and now original code takes 20ms (still too slow),
            with your modification it takes 9ms.

            So 2x time speedup, great, but it is still 1/10 of frame (1 seconds / 10 / 10)
            spend on just 100 captions, it looks like too slow for me.

            But if I emulate the glyph cache all works 0ms,
            but for some reason QPainterPath don't use internal Qt's glyphs cache :(

              void paintEvent(QPaintEvent *) override {
                QPainter painter{this};
                painter.fillRect(rect(), Qt::red);
            
                QVector<QString> lines;
                lines.resize(100);
                std::generate(std::begin(lines), std::end(lines),
                              [] { return QString::number(qrand()); });
            
                QFontMetrics fm{font()};
            
                std::vector<QPixmap> ppaths;
                ppaths.reserve(lines.size());
                for (const auto &line : lines) {
                  const auto r = fm.boundingRect(line);
                  QPixmap pix{r.size()};
                  QPainter pix_painter{&pix};
                  pix_painter.setPen(Qt::black);
                  pix_painter.setBrush(Qt::white);
            
                  QPainterPath ppath;
                  ppath.addText({0., float(r.height()) / 2}, font(), line);
                  pix_painter.drawPath(ppath);
                  ppaths.push_back(pix);
                }
                QElapsedTimer timer;
                timer.start();
                for (const auto &ppath : ppaths) {
                  painter.drawPixmap(QPoint{200, 200}, ppath);
                }
                qInfo("paint text takes %d ms", static_cast<int>(timer.elapsed()));
              }
            
            1 Reply Last reply
            0
            • mrjjM Offline
              mrjjM Offline
              mrjj
              Lifetime Qt Champion
              wrote on last edited by
              #6

              Hi
              Hmm, yes. Drawing as pixmap really is much faster than drawPath (tried your new code also)
              so i guess that will be the way to go.
              However, if you plan on much more drawing,
              QPainter might end up being to slow anyway to reach 10 FPS.

              What else are you planning to paint ?

              1 Reply Last reply
              0

              • Login

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