Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / qtquick1 / graphicsitems / qdeclarativetextlayout.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativetextlayout_p.h"
43 #include <private/qstatictext_p.h>
44 #include <private/qfontengine_p.h>
45 #include <private/qtextengine_p.h>
46 #include <private/qpainter_p.h>
47 #include <private/qpaintengineex_p.h>
48
49 QT_BEGIN_NAMESPACE
50
51 // Defined in qpainter.cpp
52 extern Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray,
53                                                        const QFixedPoint *positions, int glyphCount,
54                                                        QFontEngine *fontEngine, const QFont &font,
55                                                        const QTextCharFormat &charFormat);
56
57
58
59 class QDeclarative1TextLayoutPrivate
60 {
61 public:
62     QDeclarative1TextLayoutPrivate() 
63     : cached(false) {}
64
65     QPointF position;
66
67     bool cached;
68     QVector<QStaticTextItem> items;
69     QVector<QFixedPoint> positions;
70     QVector<glyph_t> glyphs;
71     QVector<QChar> chars;
72 };
73
74 namespace {
75 class DrawTextItemRecorder: public QPaintEngine
76 {
77     public:
78         DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations)
79             : m_inertText(0), m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations),
80               m_untransformedCoordinates(untransformedCoordinates), m_currentColor(Qt::black)
81             {
82             }
83
84         virtual void updateState(const QPaintEngineState &newState)
85         {
86             if (newState.state() & QPaintEngine::DirtyPen
87                 && newState.pen().color() != m_currentColor) {
88                 m_dirtyPen = true;
89                 m_currentColor = newState.pen().color();
90             }
91         }
92
93         virtual void drawTextItem(const QPointF &position, const QTextItem &textItem)
94         {
95             int glyphOffset = m_inertText->glyphs.size(); // Store offset into glyph pool
96             int positionOffset = m_inertText->glyphs.size(); // Offset into position pool
97             int charOffset = m_inertText->chars.size();
98
99             const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
100
101             bool needFreshCurrentItem = true;
102             if (!m_inertText->items.isEmpty()) {
103                 QStaticTextItem &last = m_inertText->items[m_inertText->items.count() - 1];
104
105                 if (last.fontEngine() == ti.fontEngine && last.font == ti.font() &&
106                     (!m_dirtyPen || last.color == state->pen().color())) {
107                     needFreshCurrentItem = false;
108
109                     last.numChars += ti.num_chars;
110
111                 }
112             } 
113
114             if (needFreshCurrentItem) {
115                 QStaticTextItem currentItem;
116
117                 currentItem.setFontEngine(ti.fontEngine);
118                 currentItem.font = ti.font();
119                 currentItem.charOffset = charOffset;
120                 currentItem.numChars = ti.num_chars;
121                 currentItem.numGlyphs = 0;
122                 currentItem.glyphOffset = glyphOffset;
123                 currentItem.positionOffset = positionOffset;
124                 currentItem.useBackendOptimizations = m_useBackendOptimizations;
125                 if (m_dirtyPen)
126                     currentItem.color = m_currentColor;
127
128                 m_inertText->items.append(currentItem);
129             }
130
131             QStaticTextItem &currentItem = m_inertText->items.last();
132
133             QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform();
134             matrix.translate(position.x(), position.y());
135
136             QVarLengthArray<glyph_t> glyphs;
137             QVarLengthArray<QFixedPoint> positions;
138             ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
139
140             int size = glyphs.size();
141             Q_ASSERT(size == positions.size());
142             currentItem.numGlyphs += size;
143
144             m_inertText->glyphs.resize(m_inertText->glyphs.size() + size);
145             m_inertText->positions.resize(m_inertText->glyphs.size());
146             m_inertText->chars.resize(m_inertText->chars.size() + ti.num_chars);
147
148             glyph_t *glyphsDestination = m_inertText->glyphs.data() + glyphOffset;
149             qMemCopy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * size);
150
151             QFixedPoint *positionsDestination = m_inertText->positions.data() + positionOffset;
152             qMemCopy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * size);
153
154             QChar *charsDestination = m_inertText->chars.data() + charOffset;
155             qMemCopy(charsDestination, ti.chars, sizeof(QChar) * ti.num_chars);
156
157         }
158
159         virtual void drawPolygon(const QPointF *, int , PolygonDrawMode )
160         {
161             /* intentionally empty */
162         }
163
164         virtual bool begin(QPaintDevice *)  { return true; }
165         virtual bool end() { return true; }
166         virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {}
167         virtual Type type() const
168         {
169             return User;
170         }
171
172         void begin(QDeclarative1TextLayoutPrivate *t) {
173             m_inertText = t;
174             m_dirtyPen = false;
175         }
176
177     private:
178         QDeclarative1TextLayoutPrivate *m_inertText;
179
180         bool m_dirtyPen;
181         bool m_useBackendOptimizations;
182         bool m_untransformedCoordinates;
183         QColor m_currentColor;
184 };
185
186 class DrawTextItemDevice: public QPaintDevice
187 {
188     public:
189         DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations)
190         {
191             m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates,
192                     useBackendOptimizations);
193         }
194
195         ~DrawTextItemDevice()
196         {
197             delete m_paintEngine;
198         }
199
200         void begin(QDeclarative1TextLayoutPrivate *t) {
201             m_paintEngine->begin(t);
202         }
203
204         int metric(PaintDeviceMetric m) const
205         {
206             int val;
207             switch (m) {
208             case PdmWidth:
209             case PdmHeight:
210             case PdmWidthMM:
211             case PdmHeightMM:
212                 val = 0;
213                 break;
214             case PdmDpiX:
215             case PdmPhysicalDpiX:
216                 val = qt_defaultDpiX();
217                 break;
218             case PdmDpiY:
219             case PdmPhysicalDpiY:
220                 val = qt_defaultDpiY();
221                 break;
222             case PdmNumColors:
223                 val = 16777216;
224                 break;
225             case PdmDepth:
226                 val = 24;
227                 break;
228             default:
229                 val = 0;
230                 qWarning("DrawTextItemDevice::metric: Invalid metric command");
231             }
232             return val;
233         }
234
235         virtual QPaintEngine *paintEngine() const
236         {
237             return m_paintEngine;
238         }
239
240     private:
241         DrawTextItemRecorder *m_paintEngine;
242 };
243
244 struct InertTextPainter {
245     InertTextPainter()
246     : device(true, true), painter(&device) {}
247
248     DrawTextItemDevice device;
249     QPainter painter;
250 };
251 }
252
253 Q_GLOBAL_STATIC(InertTextPainter, inertTextPainter);
254
255 /*!
256 \class QDeclarative1TextLayout
257 \brief The QDeclarative1TextLayout class is a version of QStaticText that works with QTextLayouts.
258 \internal
259
260 This class is basically a copy of the QStaticText code, but it is adapted to source its text from
261 QTextLayout.
262
263 It is also considerably faster to create a QDeclarative1TextLayout than a QStaticText because it uses 
264 a single, shared QPainter instance.  QStaticText by comparison creates a new QPainter per instance.
265 As a consequence this means that QDeclarative1TextLayout is not re-enterant.  Adding a lock around 
266 the shared painter solves this, and only introduces a minor performance penalty, but is unnecessary 
267 for QDeclarative1TextLayout's current use (QDeclarative1Text is already tied to the GUI thread).
268 */
269
270 QDeclarative1TextLayout::QDeclarative1TextLayout()
271 : d(0)
272 {
273 }
274
275 QDeclarative1TextLayout::QDeclarative1TextLayout(const QString &text)
276 : QTextLayout(text), d(0)
277 {
278 }
279
280 QDeclarative1TextLayout::~QDeclarative1TextLayout()
281 {
282     if (d) delete d;
283 }
284
285 void QDeclarative1TextLayout::beginLayout()
286 {
287     if (d && d->cached) {
288         d->cached = false;
289         d->items.clear();
290         d->positions.clear();
291         d->glyphs.clear();
292         d->chars.clear();
293         d->position = QPointF();
294     }
295     QTextLayout::beginLayout();
296 }
297
298 void QDeclarative1TextLayout::clearLayout()
299 {
300     if (d && d->cached) {
301         d->cached = false;
302         d->items.clear();
303         d->positions.clear();
304         d->glyphs.clear();
305         d->chars.clear();
306         d->position = QPointF();
307     }
308     QTextLayout::clearLayout();
309 }
310
311 void QDeclarative1TextLayout::prepare()
312 {
313     if (!d || !d->cached) {
314
315         if (!d)  
316             d = new QDeclarative1TextLayoutPrivate;
317
318         InertTextPainter *itp = inertTextPainter();
319         itp->device.begin(d);
320         QTextLayout::draw(&itp->painter, QPointF(0, 0));
321
322         glyph_t *glyphPool = d->glyphs.data();
323         QFixedPoint *positionPool = d->positions.data();
324         QChar *charPool = d->chars.data();
325
326         int itemCount = d->items.count();
327         for (int ii = 0; ii < itemCount; ++ii) {
328             QStaticTextItem &item = d->items[ii];
329             item.glyphs = glyphPool + item.glyphOffset;
330             item.glyphPositions = positionPool + item.positionOffset;
331             item.chars = charPool + item.charOffset;
332         }
333
334         d->cached = true;
335     }
336 }
337
338 void QDeclarative1TextLayout::draw(QPainter *painter, const QPointF &p)
339 {
340     QPainterPrivate *priv = QPainterPrivate::get(painter);
341
342     bool paintEngineSupportsTransformations = priv->extended &&
343                                               (priv->extended->type() == QPaintEngine::OpenGL2 ||
344                                                priv->extended->type() == QPaintEngine::OpenVG ||
345                                                priv->extended->type() == QPaintEngine::OpenGL);
346
347     if (!paintEngineSupportsTransformations || !priv->state->matrix.isAffine()) {
348         QTextLayout::draw(painter, p);
349         return;
350     }
351
352     prepare();
353
354     int itemCount = d->items.count();
355
356     if (p != d->position) {
357         QFixed fx = QFixed::fromReal(p.x());
358         QFixed fy = QFixed::fromReal(p.y());
359         QFixed oldX = QFixed::fromReal(d->position.x());
360         QFixed oldY = QFixed::fromReal(d->position.y());
361         for (int item = 0; item < itemCount; ++item) {
362             QStaticTextItem &textItem = d->items[item];
363
364             for (int ii = 0; ii < textItem.numGlyphs; ++ii) {
365                 textItem.glyphPositions[ii].x += fx - oldX;
366                 textItem.glyphPositions[ii].y += fy - oldY;
367             }
368             textItem.userDataNeedsUpdate = true;
369         }
370
371         d->position = p;
372     }
373
374     QPen oldPen = priv->state->pen;
375     QColor currentColor = oldPen.color();
376     for (int ii = 0; ii < itemCount; ++ii) {
377         QStaticTextItem &item = d->items[ii];
378         if (item.color.isValid() && currentColor != item.color) {
379             painter->setPen(item.color);
380             currentColor = item.color;
381         }
382         priv->extended->drawStaticTextItem(&item);
383
384         qt_draw_decoration_for_glyphs(painter, item.glyphs, item.glyphPositions,
385                                       item.numGlyphs, item.fontEngine(), painter->font(),
386                                       QTextCharFormat());
387     }
388     if (currentColor != oldPen.color())
389         painter->setPen(oldPen);
390 }
391
392
393
394 QT_END_NAMESPACE
395