1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qstatictext.h" |
5 | #include "qstatictext_p.h" |
6 | #include <qmath.h> |
7 | #include <private/qtextengine_p.h> |
8 | #include <private/qfontengine_p.h> |
9 | #include <qabstracttextdocumentlayout.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | QT_IMPL_METATYPE_EXTERN(QStaticText) |
14 | |
15 | QStaticTextUserData::~QStaticTextUserData() |
16 | { |
17 | } |
18 | |
19 | /*! |
20 | \class QStaticText |
21 | \brief The QStaticText class enables optimized drawing of text when the text and its layout |
22 | is updated rarely. |
23 | \since 4.7 |
24 | \inmodule QtGui |
25 | |
26 | \ingroup multimedia |
27 | \ingroup text |
28 | \ingroup shared |
29 | |
30 | QStaticText provides a way to cache layout data for a block of text so that it can be drawn |
31 | more efficiently than by using QPainter::drawText() in which the layout information is |
32 | recalculated with every call. |
33 | |
34 | The class primarily provides an optimization for cases where the text, its font and the |
35 | transformations on the painter are static over several paint events. If the text or its layout |
36 | is changed for every iteration, QPainter::drawText() is the more efficient alternative, since |
37 | the static text's layout would have to be recalculated to take the new state into consideration. |
38 | |
39 | Translating the painter will not cause the layout of the text to be recalculated, but will cause |
40 | a very small performance impact on drawStaticText(). Altering any other parts of the painter's |
41 | transformation or the painter's font will cause the layout of the static text to be |
42 | recalculated. This should be avoided as often as possible to maximize the performance |
43 | benefit of using QStaticText. |
44 | |
45 | In addition, only affine transformations are supported by drawStaticText(). Calling |
46 | drawStaticText() on a projected painter will perform slightly worse than using the regular |
47 | drawText() call, so this should be avoided. |
48 | |
49 | \code |
50 | class MyWidget: public QWidget |
51 | { |
52 | public: |
53 | MyWidget(QWidget *parent = nullptr) : QWidget(parent), m_staticText("This is static text") |
54 | |
55 | protected: |
56 | void paintEvent(QPaintEvent *) |
57 | { |
58 | QPainter painter(this); |
59 | painter.drawStaticText(0, 0, m_staticText); |
60 | } |
61 | |
62 | private: |
63 | QStaticText m_staticText; |
64 | }; |
65 | \endcode |
66 | |
67 | The QStaticText class can be used to mimic the behavior of QPainter::drawText() to a specific |
68 | point with no boundaries, and also when QPainter::drawText() is called with a bounding |
69 | rectangle. |
70 | |
71 | If a bounding rectangle is not required, create a QStaticText object without setting a preferred |
72 | text width. The text will then occupy a single line. |
73 | |
74 | If you set a text width on the QStaticText object, this will bound the text. The text will |
75 | be formatted so that no line exceeds the given width. The text width set for QStaticText will |
76 | not automatically be used for clipping. To achieve clipping in addition to line breaks, use |
77 | QPainter::setClipRect(). The position of the text is decided by the argument passed to |
78 | QPainter::drawStaticText() and can change from call to call with a minimal impact on |
79 | performance. |
80 | |
81 | For extra convenience, it is possible to apply formatting to the text using the HTML subset |
82 | supported by QTextDocument. QStaticText will attempt to guess the format of the input text using |
83 | Qt::mightBeRichText(), and interpret it as rich text if this function returns \c true. To force |
84 | QStaticText to display its contents as either plain text or rich text, use the function |
85 | QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and Qt::RichText. |
86 | |
87 | QStaticText can only represent text, so only HTML tags which alter the layout or appearance of |
88 | the text will be respected. Adding an image to the input HTML, for instance, will cause the |
89 | image to be included as part of the layout, affecting the positions of the text glyphs, but it |
90 | will not be displayed. The result will be an empty area the size of the image in the output. |
91 | Similarly, using tables will cause the text to be laid out in table format, but the borders |
92 | will not be drawn. |
93 | |
94 | If it's the first time the static text is drawn, or if the static text, or the painter's font |
95 | has been altered since the last time it was drawn, the text's layout has to be |
96 | recalculated. On some paint engines, changing the matrix of the painter will also cause the |
97 | layout to be recalculated. In particular, this will happen for any engine except for the |
98 | OpenGL2 paint engine. Recalculating the layout will impose an overhead on the |
99 | QPainter::drawStaticText() call where it occurs. To avoid this overhead in the paint event, you |
100 | can call prepare() ahead of time to ensure that the layout is calculated. |
101 | |
102 | \sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument |
103 | */ |
104 | |
105 | /*! |
106 | \enum QStaticText::PerformanceHint |
107 | |
108 | This enum the different performance hints that can be set on the QStaticText. These hints |
109 | can be used to indicate that the QStaticText should use additional caches, if possible, |
110 | to improve performance at the expense of memory. In particular, setting the performance hint |
111 | AggressiveCaching on the QStaticText will improve performance when using the OpenGL graphics |
112 | system or when drawing to a QOpenGLWidget. |
113 | |
114 | \value ModerateCaching Do basic caching for high performance at a low memory cost. |
115 | \value AggressiveCaching Use additional caching when available. This may improve performance |
116 | at a higher memory cost. |
117 | */ |
118 | |
119 | /*! |
120 | Constructs an empty QStaticText |
121 | */ |
122 | QStaticText::QStaticText() |
123 | : data(new QStaticTextPrivate) |
124 | { |
125 | } |
126 | |
127 | /*! |
128 | Constructs a QStaticText object with the given \a text. |
129 | */ |
130 | QStaticText::QStaticText(const QString &text) |
131 | : data(new QStaticTextPrivate) |
132 | { |
133 | data->text = text; |
134 | data->invalidate(); |
135 | } |
136 | |
137 | /*! |
138 | Constructs a QStaticText object which is a copy of \a other. |
139 | */ |
140 | QStaticText::QStaticText(const QStaticText &other) |
141 | { |
142 | data = other.data; |
143 | } |
144 | |
145 | /*! |
146 | Destroys the QStaticText. |
147 | */ |
148 | QStaticText::~QStaticText() |
149 | { |
150 | Q_ASSERT(!data || data->ref.loadRelaxed() >= 1); |
151 | } |
152 | |
153 | /*! |
154 | \internal |
155 | */ |
156 | void QStaticText::detach() |
157 | { |
158 | if (data->ref.loadRelaxed() != 1) |
159 | data.detach(); |
160 | } |
161 | |
162 | /*! |
163 | Prepares the QStaticText object for being painted with the given \a matrix and the given \a font |
164 | to avoid overhead when the actual drawStaticText() call is made. |
165 | |
166 | When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part |
167 | of the QStaticText object has changed since the last time it was drawn. It will also be |
168 | recalculated if the painter's font is not the same as when the QStaticText was last drawn, or, |
169 | on any other paint engine than the OpenGL2 engine, if the painter's matrix has been altered |
170 | since the static text was last drawn. |
171 | |
172 | To avoid the overhead of creating the layout the first time you draw the QStaticText after |
173 | making changes, you can use the prepare() function and pass in the \a matrix and \a font you |
174 | expect to use when drawing the text. |
175 | |
176 | \sa QPainter::setFont(), QPainter::setWorldTransform() |
177 | */ |
178 | void QStaticText::prepare(const QTransform &matrix, const QFont &font) |
179 | { |
180 | data->matrix = matrix; |
181 | data->font = font; |
182 | data->init(); |
183 | } |
184 | |
185 | |
186 | /*! |
187 | Assigns \a other to this QStaticText. |
188 | */ |
189 | QStaticText &QStaticText::operator=(const QStaticText &other) |
190 | { |
191 | data = other.data; |
192 | return *this; |
193 | } |
194 | |
195 | /*! |
196 | \fn void QStaticText::swap(QStaticText &other) |
197 | \since 5.0 |
198 | |
199 | Swaps this static text instance with \a other. This function is |
200 | very fast and never fails. |
201 | */ |
202 | |
203 | /*! |
204 | Compares \a other to this QStaticText. Returns \c true if the texts, fonts and text widths |
205 | are equal. |
206 | */ |
207 | bool QStaticText::operator==(const QStaticText &other) const |
208 | { |
209 | return (data == other.data |
210 | || (data->text == other.data->text |
211 | && data->font == other.data->font |
212 | && data->textWidth == other.data->textWidth)); |
213 | } |
214 | |
215 | /*! |
216 | Compares \a other to this QStaticText. Returns \c true if the texts, fonts or maximum sizes |
217 | are different. |
218 | */ |
219 | bool QStaticText::operator!=(const QStaticText &other) const |
220 | { |
221 | return !(*this == other); |
222 | } |
223 | |
224 | /*! |
225 | Sets the text of the QStaticText to \a text. |
226 | |
227 | \note This function will cause the layout of the text to require recalculation. |
228 | |
229 | \sa text() |
230 | */ |
231 | void QStaticText::setText(const QString &text) |
232 | { |
233 | detach(); |
234 | data->text = text; |
235 | data->invalidate(); |
236 | } |
237 | |
238 | /*! |
239 | Sets the text format of the QStaticText to \a textFormat. If \a textFormat is set to |
240 | Qt::AutoText (the default), the format of the text will try to be determined using the |
241 | function Qt::mightBeRichText(). If the text format is Qt::PlainText, then the text will be |
242 | displayed as is, whereas it will be interpreted as HTML if the format is Qt::RichText. HTML tags |
243 | that alter the font of the text, its color, or its layout are supported by QStaticText. |
244 | |
245 | \note This function will cause the layout of the text to require recalculation. |
246 | |
247 | \sa textFormat(), setText(), text() |
248 | */ |
249 | void QStaticText::setTextFormat(Qt::TextFormat textFormat) |
250 | { |
251 | detach(); |
252 | data->textFormat = textFormat; |
253 | data->invalidate(); |
254 | } |
255 | |
256 | /*! |
257 | Returns the text format of the QStaticText. |
258 | |
259 | \sa setTextFormat(), setText(), text() |
260 | */ |
261 | Qt::TextFormat QStaticText::textFormat() const |
262 | { |
263 | return Qt::TextFormat(data->textFormat); |
264 | } |
265 | |
266 | /*! |
267 | Returns the text of the QStaticText. |
268 | |
269 | \sa setText() |
270 | */ |
271 | QString QStaticText::text() const |
272 | { |
273 | return data->text; |
274 | } |
275 | |
276 | /*! |
277 | Sets the performance hint of the QStaticText according to the \a |
278 | performanceHint provided. The \a performanceHint is used to |
279 | customize how much caching is done internally to improve |
280 | performance. |
281 | |
282 | The default is QStaticText::ModerateCaching. |
283 | |
284 | \note This function will cause the layout of the text to require recalculation. |
285 | |
286 | \sa performanceHint() |
287 | */ |
288 | void QStaticText::setPerformanceHint(PerformanceHint performanceHint) |
289 | { |
290 | if ((performanceHint == ModerateCaching && !data->useBackendOptimizations) |
291 | || (performanceHint == AggressiveCaching && data->useBackendOptimizations)) { |
292 | return; |
293 | } |
294 | detach(); |
295 | data->useBackendOptimizations = (performanceHint == AggressiveCaching); |
296 | data->invalidate(); |
297 | } |
298 | |
299 | /*! |
300 | Returns which performance hint is set for the QStaticText. |
301 | |
302 | \sa setPerformanceHint() |
303 | */ |
304 | QStaticText::PerformanceHint QStaticText::performanceHint() const |
305 | { |
306 | return data->useBackendOptimizations ? AggressiveCaching : ModerateCaching; |
307 | } |
308 | |
309 | /*! |
310 | Sets the text option structure that controls the layout process to the given \a textOption. |
311 | |
312 | \sa textOption() |
313 | */ |
314 | void QStaticText::setTextOption(const QTextOption &textOption) |
315 | { |
316 | detach(); |
317 | data->textOption = textOption; |
318 | data->invalidate(); |
319 | } |
320 | |
321 | /*! |
322 | Returns the current text option used to control the layout process. |
323 | */ |
324 | QTextOption QStaticText::textOption() const |
325 | { |
326 | return data->textOption; |
327 | } |
328 | |
329 | /*! |
330 | Sets the preferred width for this QStaticText. If the text is wider than the specified width, |
331 | it will be broken into multiple lines and grow vertically. If the text cannot be split into |
332 | multiple lines, it will be larger than the specified \a textWidth. |
333 | |
334 | Setting the preferred text width to a negative number will cause the text to be unbounded. |
335 | |
336 | Use size() to get the actual size of the text. |
337 | |
338 | \note This function will cause the layout of the text to require recalculation. |
339 | |
340 | \sa textWidth(), size() |
341 | */ |
342 | void QStaticText::setTextWidth(qreal textWidth) |
343 | { |
344 | detach(); |
345 | data->textWidth = textWidth; |
346 | data->invalidate(); |
347 | } |
348 | |
349 | /*! |
350 | Returns the preferred width for this QStaticText. |
351 | |
352 | \sa setTextWidth() |
353 | */ |
354 | qreal QStaticText::textWidth() const |
355 | { |
356 | return data->textWidth; |
357 | } |
358 | |
359 | /*! |
360 | Returns the size of the bounding rect for this QStaticText. |
361 | |
362 | \sa textWidth() |
363 | */ |
364 | QSizeF QStaticText::size() const |
365 | { |
366 | if (data->needsRelayout) |
367 | data->init(); |
368 | return data->actualSize; |
369 | } |
370 | |
371 | QStaticTextPrivate::QStaticTextPrivate() |
372 | : textWidth(-1.0), items(nullptr), itemCount(0), glyphPool(nullptr), positionPool(nullptr), |
373 | needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText), |
374 | untransformedCoordinates(false) |
375 | { |
376 | } |
377 | |
378 | QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other) |
379 | : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix), |
380 | items(nullptr), itemCount(0), glyphPool(nullptr), positionPool(nullptr), textOption(other.textOption), |
381 | needsRelayout(true), useBackendOptimizations(other.useBackendOptimizations), |
382 | textFormat(other.textFormat), untransformedCoordinates(other.untransformedCoordinates) |
383 | { |
384 | } |
385 | |
386 | QStaticTextPrivate::~QStaticTextPrivate() |
387 | { |
388 | delete[] items; |
389 | delete[] glyphPool; |
390 | delete[] positionPool; |
391 | } |
392 | |
393 | QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q) |
394 | { |
395 | return q->data.data(); |
396 | } |
397 | |
398 | namespace { |
399 | |
400 | class DrawTextItemRecorder: public QPaintEngine |
401 | { |
402 | public: |
403 | DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations) |
404 | : m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations), |
405 | m_untransformedCoordinates(untransformedCoordinates), m_currentColor(0, 0, 0, 0) |
406 | { |
407 | } |
408 | |
409 | virtual void updateState(const QPaintEngineState &newState) override |
410 | { |
411 | if (newState.state() & QPaintEngine::DirtyPen |
412 | && newState.pen().color() != m_currentColor) { |
413 | m_dirtyPen = true; |
414 | m_currentColor = newState.pen().color(); |
415 | } |
416 | } |
417 | |
418 | virtual void drawTextItem(const QPointF &position, const QTextItem &textItem) override |
419 | { |
420 | const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); |
421 | |
422 | QStaticTextItem currentItem; |
423 | currentItem.setFontEngine(ti.fontEngine); |
424 | currentItem.font = ti.font(); |
425 | currentItem.glyphOffset = m_glyphs.size(); // Store offset into glyph pool |
426 | currentItem.positionOffset = m_glyphs.size(); // Offset into position pool |
427 | currentItem.useBackendOptimizations = m_useBackendOptimizations; |
428 | if (m_dirtyPen) |
429 | currentItem.color = m_currentColor; |
430 | |
431 | QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform(); |
432 | matrix.translate(dx: position.x(), dy: position.y()); |
433 | |
434 | QVarLengthArray<glyph_t> glyphs; |
435 | QVarLengthArray<QFixedPoint> positions; |
436 | ti.fontEngine->getGlyphPositions(glyphs: ti.glyphs, matrix, flags: ti.flags, glyphs_out&: glyphs, positions); |
437 | |
438 | int size = glyphs.size(); |
439 | Q_ASSERT(size == positions.size()); |
440 | currentItem.numGlyphs = size; |
441 | |
442 | m_glyphs.resize(size: m_glyphs.size() + size); |
443 | m_positions.resize(size: m_glyphs.size()); |
444 | |
445 | glyph_t *glyphsDestination = m_glyphs.data() + currentItem.glyphOffset; |
446 | memcpy(dest: glyphsDestination, src: glyphs.constData(), n: sizeof(glyph_t) * currentItem.numGlyphs); |
447 | |
448 | QFixedPoint *positionsDestination = m_positions.data() + currentItem.positionOffset; |
449 | memcpy(dest: positionsDestination, src: positions.constData(), n: sizeof(QFixedPoint) * currentItem.numGlyphs); |
450 | |
451 | m_items.append(t: currentItem); |
452 | } |
453 | |
454 | virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ) override |
455 | { |
456 | /* intentionally empty */ |
457 | } |
458 | |
459 | virtual bool begin(QPaintDevice *) override { return true; } |
460 | virtual bool end() override { return true; } |
461 | virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) override {} |
462 | virtual Type type() const override |
463 | { |
464 | return User; |
465 | } |
466 | |
467 | QList<QStaticTextItem> items() const |
468 | { |
469 | return m_items; |
470 | } |
471 | |
472 | QList<QFixedPoint> positions() const |
473 | { |
474 | return m_positions; |
475 | } |
476 | |
477 | QList<glyph_t> glyphs() const |
478 | { |
479 | return m_glyphs; |
480 | } |
481 | |
482 | private: |
483 | QList<QStaticTextItem> m_items; |
484 | QList<QFixedPoint> m_positions; |
485 | QList<glyph_t> m_glyphs; |
486 | |
487 | bool m_dirtyPen; |
488 | bool m_useBackendOptimizations; |
489 | bool m_untransformedCoordinates; |
490 | QColor m_currentColor; |
491 | }; |
492 | |
493 | class DrawTextItemDevice: public QPaintDevice |
494 | { |
495 | public: |
496 | DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations) |
497 | { |
498 | m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates, |
499 | useBackendOptimizations); |
500 | } |
501 | |
502 | ~DrawTextItemDevice() |
503 | { |
504 | delete m_paintEngine; |
505 | } |
506 | |
507 | int metric(PaintDeviceMetric m) const override |
508 | { |
509 | int val; |
510 | switch (m) { |
511 | case PdmWidth: |
512 | case PdmHeight: |
513 | case PdmWidthMM: |
514 | case PdmHeightMM: |
515 | val = 0; |
516 | break; |
517 | case PdmDpiX: |
518 | case PdmPhysicalDpiX: |
519 | val = qt_defaultDpiX(); |
520 | break; |
521 | case PdmDpiY: |
522 | case PdmPhysicalDpiY: |
523 | val = qt_defaultDpiY(); |
524 | break; |
525 | case PdmNumColors: |
526 | val = 16777216; |
527 | break; |
528 | case PdmDepth: |
529 | val = 24; |
530 | break; |
531 | case PdmDevicePixelRatio: |
532 | val = 1; |
533 | break; |
534 | case PdmDevicePixelRatioScaled: |
535 | val = devicePixelRatioFScale(); |
536 | break; |
537 | default: |
538 | val = 0; |
539 | qWarning(msg: "DrawTextItemDevice::metric: Invalid metric command" ); |
540 | } |
541 | return val; |
542 | } |
543 | |
544 | virtual QPaintEngine *paintEngine() const override |
545 | { |
546 | return m_paintEngine; |
547 | } |
548 | |
549 | QList<glyph_t> glyphs() const |
550 | { |
551 | return m_paintEngine->glyphs(); |
552 | } |
553 | |
554 | QList<QFixedPoint> positions() const |
555 | { |
556 | return m_paintEngine->positions(); |
557 | } |
558 | |
559 | QList<QStaticTextItem> items() const |
560 | { |
561 | return m_paintEngine->items(); |
562 | } |
563 | |
564 | private: |
565 | DrawTextItemRecorder *m_paintEngine; |
566 | }; |
567 | } |
568 | |
569 | void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p, const QColor &pen) |
570 | { |
571 | bool preferRichText = textFormat == Qt::RichText |
572 | || (textFormat == Qt::AutoText && Qt::mightBeRichText(text)); |
573 | |
574 | if (!preferRichText) { |
575 | QTextLayout textLayout; |
576 | textLayout.setText(text); |
577 | textLayout.setFont(font); |
578 | textLayout.setTextOption(textOption); |
579 | textLayout.setCacheEnabled(true); |
580 | |
581 | qreal height = 0; |
582 | textLayout.beginLayout(); |
583 | while (1) { |
584 | QTextLine line = textLayout.createLine(); |
585 | if (!line.isValid()) |
586 | break; |
587 | line.setLeadingIncluded(true); |
588 | |
589 | if (textWidth >= 0.0) |
590 | line.setLineWidth(textWidth); |
591 | else |
592 | line.setLineWidth(QFIXED_MAX); |
593 | line.setPosition(QPointF(0.0, height)); |
594 | height += line.height(); |
595 | if (line.leading() < 0) |
596 | height += qCeil(v: line.leading()); |
597 | } |
598 | textLayout.endLayout(); |
599 | |
600 | actualSize = textLayout.boundingRect().size(); |
601 | p->setPen(pen); |
602 | textLayout.draw(p, pos: topLeftPosition); |
603 | } else { |
604 | QTextDocument document; |
605 | #ifndef QT_NO_CSSPARSER |
606 | document.setDefaultStyleSheet(QString::fromLatin1(ba: "body { color: rgba(%1, %2, %3, %4%) }" ) |
607 | .arg(a: QString::number(pen.red())) |
608 | .arg(a: QString::number(pen.green())) |
609 | .arg(a: QString::number(pen.blue())) |
610 | .arg(a: QString::number(pen.alpha()))); |
611 | #endif |
612 | document.setDefaultFont(font); |
613 | document.setDocumentMargin(0.0); |
614 | #ifndef QT_NO_TEXTHTMLPARSER |
615 | document.setHtml(text); |
616 | #else |
617 | document.setPlainText(text); |
618 | #endif |
619 | if (textWidth >= 0.0) |
620 | document.setTextWidth(textWidth); |
621 | else |
622 | document.adjustSize(); |
623 | document.setDefaultTextOption(textOption); |
624 | |
625 | p->save(); |
626 | p->translate(offset: topLeftPosition); |
627 | QAbstractTextDocumentLayout::PaintContext ctx; |
628 | ctx.palette.setColor(acr: QPalette::Text, acolor: pen); |
629 | document.documentLayout()->draw(painter: p, context: ctx); |
630 | p->restore(); |
631 | |
632 | actualSize = document.size(); |
633 | } |
634 | } |
635 | |
636 | void QStaticTextPrivate::init() |
637 | { |
638 | delete[] items; |
639 | delete[] glyphPool; |
640 | delete[] positionPool; |
641 | |
642 | position = QPointF(0, 0); |
643 | |
644 | DrawTextItemDevice device(untransformedCoordinates, useBackendOptimizations); |
645 | { |
646 | QPainter painter(&device); |
647 | painter.setFont(font); |
648 | painter.setTransform(transform: matrix); |
649 | |
650 | paintText(topLeftPosition: QPointF(0, 0), p: &painter, pen: QColor(0, 0, 0, 0)); |
651 | } |
652 | |
653 | QList<QStaticTextItem> deviceItems = device.items(); |
654 | QList<QFixedPoint> positions = device.positions(); |
655 | QList<glyph_t> glyphs = device.glyphs(); |
656 | |
657 | itemCount = deviceItems.size(); |
658 | items = new QStaticTextItem[itemCount]; |
659 | |
660 | glyphPool = new glyph_t[glyphs.size()]; |
661 | memcpy(dest: glyphPool, src: glyphs.constData(), n: glyphs.size() * sizeof(glyph_t)); |
662 | |
663 | positionPool = new QFixedPoint[positions.size()]; |
664 | memcpy(dest: positionPool, src: positions.constData(), n: positions.size() * sizeof(QFixedPoint)); |
665 | |
666 | for (int i=0; i<itemCount; ++i) { |
667 | items[i] = deviceItems.at(i); |
668 | |
669 | items[i].glyphs = glyphPool + items[i].glyphOffset; |
670 | items[i].glyphPositions = positionPool + items[i].positionOffset; |
671 | } |
672 | |
673 | needsRelayout = false; |
674 | } |
675 | |
676 | QT_END_NAMESPACE |
677 | |