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