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 /*! |
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 |
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(); |
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; |
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 |