src/gui/text/qstatictext.cpp
changeset 33 3e2da88830cd
parent 30 5dc02b23752f
child 37 758a864f9613
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
   113     To force QStaticText to display its contents as either plain text or rich text, use the
   113     To force QStaticText to display its contents as either plain text or rich text, use the
   114     function QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and
   114     function QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and
   115     Qt::RichText.
   115     Qt::RichText.
   116 
   116 
   117     If it's the first time the static text is drawn, or if the static text, or the painter's font
   117     If it's the first time the static text is drawn, or if the static text, or the painter's font
   118     or matrix have been altered since the last time it was drawn, the text's layout has to be
   118     has been altered since the last time it was drawn, the text's layout has to be
   119     recalculated. This will impose an overhead on the QPainter::drawStaticText() call where the
   119     recalculated. On some paint engines, changing the matrix of the painter will also cause the
   120     relayout occurs. To avoid this overhead in the paint event, you can call prepare() ahead of
   120     layout to be recalculated. In particular, this will happen for any engine except for the
   121     time to ensure that the layout is calculated.
   121     OpenGL2 paint engine. Recalculating the layout will impose an overhead on the
       
   122     QPainter::drawStaticText() call where it occurs. To avoid this overhead in the paint event, you
       
   123     can call prepare() ahead of time to ensure that the layout is calculated.
   122 
   124 
   123     \sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument
   125     \sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument
   124 */
   126 */
   125 
   127 
   126 /*!
   128 /*!
   144     : data(new QStaticTextPrivate)
   146     : data(new QStaticTextPrivate)
   145 {
   147 {
   146 }
   148 }
   147 
   149 
   148 /*!
   150 /*!
   149     Constructs a QStaticText object with the given \a text and bounded by the given \a size.
   151     Constructs a QStaticText object with the given \a text.
   150 
       
   151     If an invalid size is passed for \a size the text will be unbounded.
       
   152 */
   152 */
   153 QStaticText::QStaticText(const QString &text)
   153 QStaticText::QStaticText(const QString &text)
   154     : data(new QStaticTextPrivate)
   154     : data(new QStaticTextPrivate)
   155 {    
   155 {    
   156     data->text = text;
   156     data->text = text;
   186   Prepares the QStaticText object for being painted with the given \a matrix and the given \a font
   186   Prepares the QStaticText object for being painted with the given \a matrix and the given \a font
   187   to avoid overhead when the actual drawStaticText() call is made.
   187   to avoid overhead when the actual drawStaticText() call is made.
   188 
   188 
   189   When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part
   189   When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part
   190   of the QStaticText object has changed since the last time it was drawn. It will also be
   190   of the QStaticText object has changed since the last time it was drawn. It will also be
   191   recalculated if the painter's font or matrix are not the same as when the QStaticText was last
   191   recalculated if the painter's font is not the same as when the QStaticText was last drawn, or,
   192   drawn.
   192   on any other paint engine than the OpenGL2 engine, if the painter's matrix has been altered
       
   193   since the static text was last drawn.
   193 
   194 
   194   To avoid the overhead of creating the layout the first time you draw the QStaticText after
   195   To avoid the overhead of creating the layout the first time you draw the QStaticText after
   195   making changes, you can use the prepare() function and pass in the \a matrix and \a font you
   196   making changes, you can use the prepare() function and pass in the \a matrix and \a font you
   196   expect to use when drawing the text.
   197   expect to use when drawing the text.
   197 
   198 
   319 {
   320 {
   320     return data->useBackendOptimizations ? AggressiveCaching : ModerateCaching;
   321     return data->useBackendOptimizations ? AggressiveCaching : ModerateCaching;
   321 }
   322 }
   322 
   323 
   323 /*!
   324 /*!
       
   325    Sets the text option structure that controls the layout process to the given \a textOption.
       
   326 
       
   327    \sa textOption()
       
   328 */
       
   329 void QStaticText::setTextOption(const QTextOption &textOption)
       
   330 {
       
   331     detach();
       
   332     data->textOption = textOption;
       
   333     data->invalidate();
       
   334 }
       
   335 
       
   336 /*!
       
   337     Returns the current text option used to control the layout process.
       
   338 */
       
   339 QTextOption QStaticText::textOption() const
       
   340 {
       
   341     return data->textOption;
       
   342 }
       
   343 
       
   344 /*!
   324     Sets the preferred width for this QStaticText. If the text is wider than the specified width,
   345     Sets the preferred width for this QStaticText. If the text is wider than the specified width,
   325     it will be broken into multiple lines and grow vertically. If the text cannot be split into
   346     it will be broken into multiple lines and grow vertically. If the text cannot be split into
   326     multiple lines, it will be larger than the specified \a textWidth.
   347     multiple lines, it will be larger than the specified \a textWidth.
   327 
   348 
   328     Setting the preferred text width to a negative number will cause the text to be unbounded.
   349     Setting the preferred text width to a negative number will cause the text to be unbounded.
   361         data->init();
   382         data->init();
   362     return data->actualSize;
   383     return data->actualSize;
   363 }
   384 }
   364 
   385 
   365 QStaticTextPrivate::QStaticTextPrivate()
   386 QStaticTextPrivate::QStaticTextPrivate()
   366         : textWidth(-1.0), items(0), itemCount(0), glyphPool(0), positionPool(0),
   387         : textWidth(-1.0), items(0), itemCount(0), glyphPool(0), positionPool(0), charPool(0),
   367           needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText)
   388           needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText),
       
   389           untransformedCoordinates(false)
   368 {
   390 {
   369 }
   391 }
   370 
   392 
   371 QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other)
   393 QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other)
   372     : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix),
   394     : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix),
   373       items(0), itemCount(0), glyphPool(0), positionPool(0), needsRelayout(true),
   395       items(0), itemCount(0), glyphPool(0), positionPool(0), charPool(0), needsRelayout(true),
   374       useBackendOptimizations(other.useBackendOptimizations), textFormat(other.textFormat)
   396       useBackendOptimizations(other.useBackendOptimizations), textFormat(other.textFormat),
       
   397       untransformedCoordinates(other.untransformedCoordinates)
   375 {
   398 {
   376 }
   399 }
   377 
   400 
   378 QStaticTextPrivate::~QStaticTextPrivate()
   401 QStaticTextPrivate::~QStaticTextPrivate()
   379 {
   402 {
   380     delete[] items;    
   403     delete[] items;
   381     delete[] glyphPool;
   404     delete[] glyphPool;
   382     delete[] positionPool;
   405     delete[] positionPool;
       
   406     delete[] charPool;
   383 }
   407 }
   384 
   408 
   385 QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q)
   409 QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q)
   386 {
   410 {
   387     return q->data.data();
   411     return q->data.data();
   393 namespace {
   417 namespace {
   394 
   418 
   395     class DrawTextItemRecorder: public QPaintEngine
   419     class DrawTextItemRecorder: public QPaintEngine
   396     {
   420     {
   397     public:
   421     public:
   398         DrawTextItemRecorder(int expectedItemCount, QStaticTextItem *items,
   422         DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations)
   399                              int expectedGlyphCount, QFixedPoint *positionPool, glyph_t *glyphPool)
   423                 : m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations),
   400                 : m_items(items),
   424                   m_untransformedCoordinates(untransformedCoordinates)
   401                   m_itemCount(0), m_glyphCount(0),
       
   402                   m_expectedItemCount(expectedItemCount),
       
   403                   m_expectedGlyphCount(expectedGlyphCount),
       
   404                   m_glyphPool(glyphPool),
       
   405                   m_positionPool(positionPool),
       
   406                   m_dirtyPen(false)
       
   407         {
   425         {
   408         }
   426         }
   409 
   427 
   410         virtual void updateState(const QPaintEngineState &newState)
   428         virtual void updateState(const QPaintEngineState &newState)
   411         {
   429         {
   413                 m_dirtyPen = true;
   431                 m_dirtyPen = true;
   414         }
   432         }
   415 
   433 
   416         virtual void drawTextItem(const QPointF &position, const QTextItem &textItem)
   434         virtual void drawTextItem(const QPointF &position, const QTextItem &textItem)
   417         {
   435         {
   418             const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);          
   436             const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
   419 
   437 
   420             m_itemCount++;
   438             QStaticTextItem currentItem;
   421             m_glyphCount += ti.glyphs.numGlyphs;
   439             currentItem.fontEngine = ti.fontEngine;
   422             if (m_items == 0)
   440             currentItem.font = ti.font();
   423                 return;
   441             currentItem.charOffset = m_chars.size();
   424 
   442             currentItem.numChars = ti.num_chars;
   425             Q_ASSERT(m_itemCount <= m_expectedItemCount);
   443             currentItem.numGlyphs = ti.glyphs.numGlyphs;
   426             Q_ASSERT(m_glyphCount <= m_expectedGlyphCount);
   444             currentItem.glyphOffset = m_glyphs.size(); // Store offset into glyph pool
   427 
   445             currentItem.positionOffset = m_glyphs.size(); // Offset into position pool
   428             QStaticTextItem *currentItem = (m_items + (m_itemCount - 1));
   446             currentItem.useBackendOptimizations = m_useBackendOptimizations;
   429             currentItem->fontEngine = ti.fontEngine;
       
   430             currentItem->font = ti.font();
       
   431             currentItem->chars = ti.chars;
       
   432             currentItem->numChars = ti.num_chars;
       
   433             currentItem->numGlyphs = ti.glyphs.numGlyphs;
       
   434             currentItem->glyphs = m_glyphPool;
       
   435             currentItem->glyphPositions = m_positionPool;
       
   436             if (m_dirtyPen)
   447             if (m_dirtyPen)
   437                 currentItem->color = state->pen().color();
   448                 currentItem.color = state->pen().color();
   438 
   449 
   439             QTransform matrix = state->transform();
   450             QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform();
   440             matrix.translate(position.x(), position.y());
   451             matrix.translate(position.x(), position.y());
   441 
   452 
   442             QVarLengthArray<glyph_t> glyphs;
   453             QVarLengthArray<glyph_t> glyphs;
   443             QVarLengthArray<QFixedPoint> positions;
   454             QVarLengthArray<QFixedPoint> positions;
   444             ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
   455             ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
   445 
   456 
   446             int size = glyphs.size();
   457             int size = glyphs.size();
   447             Q_ASSERT(size == ti.glyphs.numGlyphs);
   458             Q_ASSERT(size == ti.glyphs.numGlyphs);
   448             Q_ASSERT(size == positions.size());
   459             Q_ASSERT(size == positions.size());
   449 
   460 
   450             memmove(currentItem->glyphs, glyphs.constData(), sizeof(glyph_t) * size);
   461             m_glyphs.resize(m_glyphs.size() + size);
   451             memmove(currentItem->glyphPositions, positions.constData(), sizeof(QFixedPoint) * size);
   462             m_positions.resize(m_glyphs.size());
   452 
   463             m_chars.resize(m_chars.size() + ti.num_chars);
   453             m_glyphPool += size;
   464 
   454             m_positionPool += size;
   465             glyph_t *glyphsDestination = m_glyphs.data() + currentItem.glyphOffset;
       
   466             memcpy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * currentItem.numGlyphs);
       
   467 
       
   468             QFixedPoint *positionsDestination = m_positions.data() + currentItem.positionOffset;
       
   469             memcpy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * currentItem.numGlyphs);
       
   470 
       
   471             QChar *charsDestination = m_chars.data() + currentItem.charOffset;
       
   472             memcpy(charsDestination, ti.chars, sizeof(QChar) * currentItem.numChars);
       
   473 
       
   474             m_items.append(currentItem);
   455         }                
   475         }                
   456 
       
   457 
   476 
   458         virtual bool begin(QPaintDevice *)  { return true; }
   477         virtual bool begin(QPaintDevice *)  { return true; }
   459         virtual bool end() { return true; }
   478         virtual bool end() { return true; }
   460         virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {}
   479         virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {}
   461         virtual Type type() const
   480         virtual Type type() const
   462         {
   481         {
   463             return User;
   482             return User;
   464         }
   483         }
   465 
   484 
   466         int itemCount() const
   485         QVector<QStaticTextItem> items() const
   467         {
   486         {
   468             return m_itemCount;
   487             return m_items;
   469         }
   488         }
   470 
   489 
   471         int glyphCount() const
   490         QVector<QFixedPoint> positions() const
   472         {
   491         {
   473             return m_glyphCount;
   492             return m_positions;
       
   493         }
       
   494 
       
   495         QVector<glyph_t> glyphs() const
       
   496         {
       
   497             return m_glyphs;
       
   498         }
       
   499 
       
   500         QVector<QChar> chars() const
       
   501         {
       
   502             return m_chars;
   474         }
   503         }
   475 
   504 
   476     private:
   505     private:
   477         QStaticTextItem *m_items;
   506         QVector<QStaticTextItem> m_items;
   478         int m_itemCount;
   507         QVector<QFixedPoint> m_positions;
   479         int m_glyphCount;
   508         QVector<glyph_t> m_glyphs;
   480         int m_expectedItemCount;
   509         QVector<QChar> m_chars;
   481         int m_expectedGlyphCount;
       
   482 
       
   483         glyph_t *m_glyphPool;
       
   484         QFixedPoint *m_positionPool;
       
   485 
   510 
   486         bool m_dirtyPen;
   511         bool m_dirtyPen;
       
   512         bool m_useBackendOptimizations;
       
   513         bool m_untransformedCoordinates;
   487     };
   514     };
   488 
   515 
   489     class DrawTextItemDevice: public QPaintDevice
   516     class DrawTextItemDevice: public QPaintDevice
   490     {
   517     {
   491     public:
   518     public:
   492         DrawTextItemDevice(int expectedItemCount = -1,  QStaticTextItem *items = 0,
   519         DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations)
   493                            int expectedGlyphCount = -1, QFixedPoint *positionPool = 0,
   520         {
   494                            glyph_t *glyphPool = 0)
   521             m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates,
   495         {
   522                                                      useBackendOptimizations);
   496             m_paintEngine = new DrawTextItemRecorder(expectedItemCount, items,
       
   497                                                      expectedGlyphCount, positionPool, glyphPool);
       
   498         }
   523         }
   499 
   524 
   500         ~DrawTextItemDevice()
   525         ~DrawTextItemDevice()
   501         {
   526         {
   502             delete m_paintEngine;
   527             delete m_paintEngine;
   536         virtual QPaintEngine *paintEngine() const
   561         virtual QPaintEngine *paintEngine() const
   537         {
   562         {
   538             return m_paintEngine;
   563             return m_paintEngine;
   539         }
   564         }
   540 
   565 
   541         int itemCount() const
   566         QVector<glyph_t> glyphs() const
   542         {
   567         {
   543             return m_paintEngine->itemCount();
   568             return m_paintEngine->glyphs();
   544         }
   569         }
   545 
   570 
   546         int glyphCount() const
   571         QVector<QFixedPoint> positions() const
   547         {
   572         {
   548             return m_paintEngine->glyphCount();
   573             return m_paintEngine->positions();
       
   574         }
       
   575 
       
   576         QVector<QStaticTextItem> items() const
       
   577         {
       
   578             return m_paintEngine->items();
       
   579         }
       
   580 
       
   581         QVector<QChar> chars() const
       
   582         {
       
   583             return m_paintEngine->chars();
   549         }
   584         }
   550 
   585 
   551     private:
   586     private:
   552         DrawTextItemRecorder *m_paintEngine;
   587         DrawTextItemRecorder *m_paintEngine;
   553     };
   588     };
   560 
   595 
   561     if (!preferRichText) {
   596     if (!preferRichText) {
   562         QTextLayout textLayout;
   597         QTextLayout textLayout;
   563         textLayout.setText(text);
   598         textLayout.setText(text);
   564         textLayout.setFont(font);
   599         textLayout.setFont(font);
       
   600         textLayout.setTextOption(textOption);
   565 
   601 
   566         qreal leading = QFontMetricsF(font).leading();
   602         qreal leading = QFontMetricsF(font).leading();
   567         qreal height = -leading;
   603         qreal height = -leading;
   568 
   604 
   569         textLayout.beginLayout();
   605         textLayout.beginLayout();
   590                                       .arg(QString::number(color.red(), 16), 2, QLatin1Char('0'))
   626                                       .arg(QString::number(color.red(), 16), 2, QLatin1Char('0'))
   591                                       .arg(QString::number(color.green(), 16), 2, QLatin1Char('0'))
   627                                       .arg(QString::number(color.green(), 16), 2, QLatin1Char('0'))
   592                                       .arg(QString::number(color.blue(), 16), 2, QLatin1Char('0')));
   628                                       .arg(QString::number(color.blue(), 16), 2, QLatin1Char('0')));
   593 #endif
   629 #endif
   594         document.setDefaultFont(font);
   630         document.setDefaultFont(font);
   595         document.setDocumentMargin(0.0);
   631         document.setDocumentMargin(0.0);        
   596         if (textWidth >= 0.0)
       
   597             document.setTextWidth(textWidth);
       
   598 #ifndef QT_NO_TEXTHTMLPARSER
   632 #ifndef QT_NO_TEXTHTMLPARSER
   599         document.setHtml(text);
   633         document.setHtml(text);
   600 #else
   634 #else
   601         document.setPlainText(text);
   635         document.setPlainText(text);
   602 #endif
   636 #endif
   603 
   637         if (textWidth >= 0.0)
   604         document.adjustSize();
   638             document.setTextWidth(textWidth);
       
   639         else
       
   640             document.adjustSize();
       
   641         document.setDefaultTextOption(textOption);
       
   642 
   605         p->save();
   643         p->save();
   606         p->translate(topLeftPosition);
   644         p->translate(topLeftPosition);
   607         document.drawContents(p);
   645         document.drawContents(p);
   608         p->restore();
   646         p->restore();
   609 
   647 
       
   648         if (textWidth >= 0.0)
       
   649             document.adjustSize(); // Find optimal size
       
   650 
   610         actualSize = document.size();
   651         actualSize = document.size();
   611     }
   652     }
   612 }
   653 }
   613 
   654 
   614 void QStaticTextPrivate::init()
   655 void QStaticTextPrivate::init()
   615 {
   656 {
   616     delete[] items;
   657     delete[] items;
   617     delete[] glyphPool;
   658     delete[] glyphPool;
   618     delete[] positionPool;
   659     delete[] positionPool;
       
   660     delete[] charPool;
   619 
   661 
   620     position = QPointF(0, 0);
   662     position = QPointF(0, 0);
   621 
   663 
   622     // Draw once to count number of items and glyphs, so that we can use as little memory
   664     DrawTextItemDevice device(untransformedCoordinates, useBackendOptimizations);
   623     // as possible to store the data
       
   624     DrawTextItemDevice counterDevice;
       
   625     {
   665     {
   626         QPainter painter(&counterDevice);
   666         QPainter painter(&device);
   627         painter.setFont(font);
   667         painter.setFont(font);
   628         painter.setTransform(matrix);
   668         painter.setTransform(matrix);
   629 
   669 
   630         paintText(QPointF(0, 0), &painter);
   670         paintText(QPointF(0, 0), &painter);
   631 
       
   632     }
   671     }
   633 
   672 
   634     itemCount = counterDevice.itemCount();    
   673     QVector<QStaticTextItem> deviceItems = device.items();
       
   674     QVector<QFixedPoint> positions = device.positions();
       
   675     QVector<glyph_t> glyphs = device.glyphs();
       
   676     QVector<QChar> chars = device.chars();
       
   677 
       
   678     itemCount = deviceItems.size();
   635     items = new QStaticTextItem[itemCount];
   679     items = new QStaticTextItem[itemCount];
   636 
   680 
   637     if (useBackendOptimizations) {
   681     glyphPool = new glyph_t[glyphs.size()];
   638         for (int i=0; i<itemCount; ++i)
   682     memcpy(glyphPool, glyphs.constData(), glyphs.size() * sizeof(glyph_t));
   639             items[i].useBackendOptimizations = true;
   683 
       
   684     positionPool = new QFixedPoint[positions.size()];
       
   685     memcpy(positionPool, positions.constData(), positions.size() * sizeof(QFixedPoint));
       
   686 
       
   687     charPool = new QChar[chars.size()];
       
   688     memcpy(charPool, chars.constData(), chars.size() * sizeof(QChar));
       
   689 
       
   690     for (int i=0; i<itemCount; ++i) {
       
   691         items[i] = deviceItems.at(i);
       
   692 
       
   693         items[i].glyphs = glyphPool + items[i].glyphOffset;
       
   694         items[i].glyphPositions = positionPool + items[i].positionOffset;
       
   695         items[i].chars = charPool + items[i].charOffset;
   640     }
   696     }
   641 
   697 
   642 
       
   643     int glyphCount = counterDevice.glyphCount();
       
   644     glyphPool = new glyph_t[glyphCount];
       
   645     positionPool = new QFixedPoint[glyphCount];
       
   646 
       
   647     // Draw again to actually record the items and glyphs
       
   648     DrawTextItemDevice recorderDevice(itemCount, items, glyphCount, positionPool, glyphPool);
       
   649     {
       
   650         QPainter painter(&recorderDevice);
       
   651         painter.setFont(font);
       
   652         painter.setTransform(matrix);
       
   653 
       
   654         paintText(QPointF(0, 0), &painter);
       
   655     }
       
   656 
       
   657     needsRelayout = false;
   698     needsRelayout = false;
   658 }
   699 }
   659 
   700 
   660 QT_END_NAMESPACE
   701 QT_END_NAMESPACE