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 | \memberswap{static text instance} |
199 | */ |
200 | |
201 | /*! |
202 | Compares \a other to this QStaticText. Returns \c true if the texts, fonts and text widths |
203 | are equal. |
204 | */ |
205 | bool QStaticText::operator==(const QStaticText &other) const |
206 | { |
207 | return (data == other.data |
208 | || (data->text == other.data->text |
209 | && data->font == other.data->font |
210 | && data->textWidth == other.data->textWidth)); |
211 | } |
212 | |
213 | /*! |
214 | Compares \a other to this QStaticText. Returns \c true if the texts, fonts or maximum sizes |
215 | are different. |
216 | */ |
217 | bool QStaticText::operator!=(const QStaticText &other) const |
218 | { |
219 | return !(*this == other); |
220 | } |
221 | |
222 | /*! |
223 | Sets the text of the QStaticText to \a text. |
224 | |
225 | \note This function will cause the layout of the text to require recalculation. |
226 | |
227 | \sa text() |
228 | */ |
229 | void QStaticText::setText(const QString &text) |
230 | { |
231 | detach(); |
232 | data->text = text; |
233 | data->invalidate(); |
234 | } |
235 | |
236 | /*! |
237 | Sets the text format of the QStaticText to \a textFormat. If \a textFormat is set to |
238 | Qt::AutoText (the default), the format of the text will try to be determined using the |
239 | function Qt::mightBeRichText(). If the text format is Qt::PlainText, then the text will be |
240 | displayed as is, whereas it will be interpreted as HTML if the format is Qt::RichText. HTML tags |
241 | that alter the font of the text, its color, or its layout are supported by QStaticText. |
242 | |
243 | \note This function will cause the layout of the text to require recalculation. |
244 | |
245 | \sa textFormat(), setText(), text() |
246 | */ |
247 | void QStaticText::setTextFormat(Qt::TextFormat textFormat) |
248 | { |
249 | detach(); |
250 | data->textFormat = textFormat; |
251 | data->invalidate(); |
252 | } |
253 | |
254 | /*! |
255 | Returns the text format of the QStaticText. |
256 | |
257 | \sa setTextFormat(), setText(), text() |
258 | */ |
259 | Qt::TextFormat QStaticText::textFormat() const |
260 | { |
261 | return Qt::TextFormat(data->textFormat); |
262 | } |
263 | |
264 | /*! |
265 | Returns the text of the QStaticText. |
266 | |
267 | \sa setText() |
268 | */ |
269 | QString QStaticText::text() const |
270 | { |
271 | return data->text; |
272 | } |
273 | |
274 | /*! |
275 | Sets the performance hint of the QStaticText according to the \a |
276 | performanceHint provided. The \a performanceHint is used to |
277 | customize how much caching is done internally to improve |
278 | performance. |
279 | |
280 | The default is QStaticText::ModerateCaching. |
281 | |
282 | \note This function will cause the layout of the text to require recalculation. |
283 | |
284 | \sa performanceHint() |
285 | */ |
286 | void QStaticText::setPerformanceHint(PerformanceHint performanceHint) |
287 | { |
288 | if ((performanceHint == ModerateCaching && !data->useBackendOptimizations) |
289 | || (performanceHint == AggressiveCaching && data->useBackendOptimizations)) { |
290 | return; |
291 | } |
292 | detach(); |
293 | data->useBackendOptimizations = (performanceHint == AggressiveCaching); |
294 | data->invalidate(); |
295 | } |
296 | |
297 | /*! |
298 | Returns which performance hint is set for the QStaticText. |
299 | |
300 | \sa setPerformanceHint() |
301 | */ |
302 | QStaticText::PerformanceHint QStaticText::performanceHint() const |
303 | { |
304 | return data->useBackendOptimizations ? AggressiveCaching : ModerateCaching; |
305 | } |
306 | |
307 | /*! |
308 | Sets the text option structure that controls the layout process to the given \a textOption. |
309 | |
310 | \sa textOption() |
311 | */ |
312 | void QStaticText::setTextOption(const QTextOption &textOption) |
313 | { |
314 | detach(); |
315 | data->textOption = textOption; |
316 | data->invalidate(); |
317 | } |
318 | |
319 | /*! |
320 | Returns the current text option used to control the layout process. |
321 | */ |
322 | QTextOption QStaticText::textOption() const |
323 | { |
324 | return data->textOption; |
325 | } |
326 | |
327 | /*! |
328 | Sets the preferred width for this QStaticText. If the text is wider than the specified width, |
329 | it will be broken into multiple lines and grow vertically. If the text cannot be split into |
330 | multiple lines, it will be larger than the specified \a textWidth. |
331 | |
332 | Setting the preferred text width to a negative number will cause the text to be unbounded. |
333 | |
334 | Use size() to get the actual size of the text. |
335 | |
336 | \note This function will cause the layout of the text to require recalculation. |
337 | |
338 | \sa textWidth(), size() |
339 | */ |
340 | void QStaticText::setTextWidth(qreal textWidth) |
341 | { |
342 | detach(); |
343 | data->textWidth = textWidth; |
344 | data->invalidate(); |
345 | } |
346 | |
347 | /*! |
348 | Returns the preferred width for this QStaticText. |
349 | |
350 | \sa setTextWidth() |
351 | */ |
352 | qreal QStaticText::textWidth() const |
353 | { |
354 | return data->textWidth; |
355 | } |
356 | |
357 | /*! |
358 | Returns the size of the bounding rect for this QStaticText. |
359 | |
360 | \sa textWidth() |
361 | */ |
362 | QSizeF QStaticText::size() const |
363 | { |
364 | if (data->needsRelayout) |
365 | data->init(); |
366 | return data->actualSize; |
367 | } |
368 | |
369 | QStaticTextPrivate::QStaticTextPrivate() |
370 | : textWidth(-1.0), items(nullptr), itemCount(0), glyphPool(nullptr), positionPool(nullptr), |
371 | needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText), |
372 | untransformedCoordinates(false) |
373 | { |
374 | } |
375 | |
376 | QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other) |
377 | : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix), |
378 | items(nullptr), itemCount(0), glyphPool(nullptr), positionPool(nullptr), textOption(other.textOption), |
379 | needsRelayout(true), useBackendOptimizations(other.useBackendOptimizations), |
380 | textFormat(other.textFormat), untransformedCoordinates(other.untransformedCoordinates) |
381 | { |
382 | } |
383 | |
384 | QStaticTextPrivate::~QStaticTextPrivate() |
385 | { |
386 | delete[] items; |
387 | delete[] glyphPool; |
388 | delete[] positionPool; |
389 | } |
390 | |
391 | QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q) |
392 | { |
393 | return q->data.data(); |
394 | } |
395 | |
396 | namespace { |
397 | |
398 | class DrawTextItemRecorder: public QPaintEngine |
399 | { |
400 | public: |
401 | DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations) |
402 | : m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations), |
403 | m_untransformedCoordinates(untransformedCoordinates), m_currentColor(0, 0, 0, 0) |
404 | { |
405 | } |
406 | |
407 | virtual void updateState(const QPaintEngineState &newState) override |
408 | { |
409 | if (newState.state() & QPaintEngine::DirtyPen |
410 | && newState.pen().color() != m_currentColor) { |
411 | m_dirtyPen = true; |
412 | m_currentColor = newState.pen().color(); |
413 | } |
414 | } |
415 | |
416 | virtual void drawTextItem(const QPointF &position, const QTextItem &textItem) override |
417 | { |
418 | const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); |
419 | |
420 | QStaticTextItem currentItem; |
421 | currentItem.setFontEngine(ti.fontEngine); |
422 | currentItem.font = ti.font(); |
423 | currentItem.glyphOffset = m_glyphs.size(); // Store offset into glyph pool |
424 | currentItem.positionOffset = m_glyphs.size(); // Offset into position pool |
425 | currentItem.useBackendOptimizations = m_useBackendOptimizations; |
426 | if (m_dirtyPen) |
427 | currentItem.color = m_currentColor; |
428 | |
429 | QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform(); |
430 | matrix.translate(dx: position.x(), dy: position.y()); |
431 | |
432 | QVarLengthArray<glyph_t> glyphs; |
433 | QVarLengthArray<QFixedPoint> positions; |
434 | ti.fontEngine->getGlyphPositions(glyphs: ti.glyphs, matrix, flags: ti.flags, glyphs_out&: glyphs, positions); |
435 | |
436 | int size = glyphs.size(); |
437 | Q_ASSERT(size == positions.size()); |
438 | currentItem.numGlyphs = size; |
439 | |
440 | m_glyphs.resize(size: m_glyphs.size() + size); |
441 | m_positions.resize(size: m_glyphs.size()); |
442 | |
443 | glyph_t *glyphsDestination = m_glyphs.data() + currentItem.glyphOffset; |
444 | memcpy(dest: glyphsDestination, src: glyphs.constData(), n: sizeof(glyph_t) * currentItem.numGlyphs); |
445 | |
446 | QFixedPoint *positionsDestination = m_positions.data() + currentItem.positionOffset; |
447 | memcpy(dest: positionsDestination, src: positions.constData(), n: sizeof(QFixedPoint) * currentItem.numGlyphs); |
448 | |
449 | m_items.append(t: currentItem); |
450 | } |
451 | |
452 | virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ) override |
453 | { |
454 | /* intentionally empty */ |
455 | } |
456 | |
457 | virtual bool begin(QPaintDevice *) override { return true; } |
458 | virtual bool end() override { return true; } |
459 | virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) override {} |
460 | virtual Type type() const override |
461 | { |
462 | return User; |
463 | } |
464 | |
465 | QList<QStaticTextItem> items() const |
466 | { |
467 | return m_items; |
468 | } |
469 | |
470 | QList<QFixedPoint> positions() const |
471 | { |
472 | return m_positions; |
473 | } |
474 | |
475 | QList<glyph_t> glyphs() const |
476 | { |
477 | return m_glyphs; |
478 | } |
479 | |
480 | private: |
481 | QList<QStaticTextItem> m_items; |
482 | QList<QFixedPoint> m_positions; |
483 | QList<glyph_t> m_glyphs; |
484 | |
485 | bool m_dirtyPen; |
486 | bool m_useBackendOptimizations; |
487 | bool m_untransformedCoordinates; |
488 | QColor m_currentColor; |
489 | }; |
490 | |
491 | class DrawTextItemDevice: public QPaintDevice |
492 | { |
493 | public: |
494 | DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations) |
495 | { |
496 | m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates, |
497 | useBackendOptimizations); |
498 | } |
499 | |
500 | ~DrawTextItemDevice() |
501 | { |
502 | delete m_paintEngine; |
503 | } |
504 | |
505 | int metric(PaintDeviceMetric m) const override |
506 | { |
507 | int val; |
508 | switch (m) { |
509 | case PdmWidth: |
510 | case PdmHeight: |
511 | case PdmWidthMM: |
512 | case PdmHeightMM: |
513 | val = 0; |
514 | break; |
515 | case PdmDpiX: |
516 | case PdmPhysicalDpiX: |
517 | val = qt_defaultDpiX(); |
518 | break; |
519 | case PdmDpiY: |
520 | case PdmPhysicalDpiY: |
521 | val = qt_defaultDpiY(); |
522 | break; |
523 | case PdmNumColors: |
524 | val = 16777216; |
525 | break; |
526 | case PdmDepth: |
527 | val = 24; |
528 | break; |
529 | case PdmDevicePixelRatio: |
530 | val = 1; |
531 | break; |
532 | case PdmDevicePixelRatioScaled: |
533 | val = devicePixelRatioFScale(); |
534 | break; |
535 | default: |
536 | val = 0; |
537 | qWarning(msg: "DrawTextItemDevice::metric: Invalid metric command" ); |
538 | } |
539 | return val; |
540 | } |
541 | |
542 | virtual QPaintEngine *paintEngine() const override |
543 | { |
544 | return m_paintEngine; |
545 | } |
546 | |
547 | QList<glyph_t> glyphs() const |
548 | { |
549 | return m_paintEngine->glyphs(); |
550 | } |
551 | |
552 | QList<QFixedPoint> positions() const |
553 | { |
554 | return m_paintEngine->positions(); |
555 | } |
556 | |
557 | QList<QStaticTextItem> items() const |
558 | { |
559 | return m_paintEngine->items(); |
560 | } |
561 | |
562 | private: |
563 | DrawTextItemRecorder *m_paintEngine; |
564 | }; |
565 | } |
566 | |
567 | void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p, const QColor &pen) |
568 | { |
569 | bool preferRichText = textFormat == Qt::RichText |
570 | || (textFormat == Qt::AutoText && Qt::mightBeRichText(text)); |
571 | |
572 | if (!preferRichText) { |
573 | QTextLayout textLayout; |
574 | textLayout.setText(text); |
575 | textLayout.setFont(font); |
576 | textLayout.setTextOption(textOption); |
577 | textLayout.setCacheEnabled(true); |
578 | |
579 | qreal height = 0; |
580 | textLayout.beginLayout(); |
581 | while (1) { |
582 | QTextLine line = textLayout.createLine(); |
583 | if (!line.isValid()) |
584 | break; |
585 | line.setLeadingIncluded(true); |
586 | |
587 | if (textWidth >= 0.0) |
588 | line.setLineWidth(textWidth); |
589 | else |
590 | line.setLineWidth(QFIXED_MAX); |
591 | line.setPosition(QPointF(0.0, height)); |
592 | height += line.height(); |
593 | if (line.leading() < 0) |
594 | height += qCeil(v: line.leading()); |
595 | } |
596 | textLayout.endLayout(); |
597 | |
598 | actualSize = textLayout.boundingRect().size(); |
599 | p->setPen(pen); |
600 | textLayout.draw(p, pos: topLeftPosition); |
601 | } else { |
602 | QTextDocument document; |
603 | #ifndef QT_NO_CSSPARSER |
604 | document.setDefaultStyleSheet(QString::fromLatin1(ba: "body { color: rgba(%1, %2, %3, %4%) }" ) |
605 | .arg(a: QString::number(pen.red())) |
606 | .arg(a: QString::number(pen.green())) |
607 | .arg(a: QString::number(pen.blue())) |
608 | .arg(a: QString::number(pen.alpha()))); |
609 | #endif |
610 | document.setDefaultFont(font); |
611 | document.setDocumentMargin(0.0); |
612 | #ifndef QT_NO_TEXTHTMLPARSER |
613 | document.setHtml(text); |
614 | #else |
615 | document.setPlainText(text); |
616 | #endif |
617 | if (textWidth >= 0.0) |
618 | document.setTextWidth(textWidth); |
619 | else |
620 | document.adjustSize(); |
621 | document.setDefaultTextOption(textOption); |
622 | |
623 | p->save(); |
624 | p->translate(offset: topLeftPosition); |
625 | QAbstractTextDocumentLayout::PaintContext ctx; |
626 | ctx.palette.setColor(acr: QPalette::Text, acolor: pen); |
627 | document.documentLayout()->draw(painter: p, context: ctx); |
628 | p->restore(); |
629 | |
630 | actualSize = document.size(); |
631 | } |
632 | } |
633 | |
634 | void QStaticTextPrivate::init() |
635 | { |
636 | delete[] items; |
637 | delete[] glyphPool; |
638 | delete[] positionPool; |
639 | |
640 | position = QPointF(0, 0); |
641 | |
642 | DrawTextItemDevice device(untransformedCoordinates, useBackendOptimizations); |
643 | { |
644 | QPainter painter(&device); |
645 | painter.setFont(font); |
646 | painter.setTransform(transform: matrix); |
647 | |
648 | paintText(topLeftPosition: QPointF(0, 0), p: &painter, pen: QColor(0, 0, 0, 0)); |
649 | } |
650 | |
651 | QList<QStaticTextItem> deviceItems = device.items(); |
652 | QList<QFixedPoint> positions = device.positions(); |
653 | QList<glyph_t> glyphs = device.glyphs(); |
654 | |
655 | itemCount = deviceItems.size(); |
656 | items = new QStaticTextItem[itemCount]; |
657 | |
658 | glyphPool = new glyph_t[glyphs.size()]; |
659 | memcpy(dest: glyphPool, src: glyphs.constData(), n: glyphs.size() * sizeof(glyph_t)); |
660 | |
661 | positionPool = new QFixedPoint[positions.size()]; |
662 | memcpy(dest: positionPool, src: positions.constData(), n: positions.size() * sizeof(QFixedPoint)); |
663 | |
664 | for (int i=0; i<itemCount; ++i) { |
665 | items[i] = deviceItems.at(i); |
666 | |
667 | items[i].glyphs = glyphPool + items[i].glyphOffset; |
668 | items[i].glyphPositions = positionPool + items[i].positionOffset; |
669 | } |
670 | |
671 | needsRelayout = false; |
672 | } |
673 | |
674 | QT_END_NAMESPACE |
675 | |