Make QRegion not need to be friends with QVector
[profile/ivi/qtbase.git] / src / gui / painting / qoutlinemapper.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 QtGui 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 "qoutlinemapper_p.h"
43 #include <private/qpainterpath_p.h>
44 #include "qmath.h"
45 #include <private/qbezier_p.h>
46
47 #include <stdlib.h>
48
49 QT_BEGIN_NAMESPACE
50
51 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
52
53 #define qreal_to_fixed_26_6(f) (int(f * 64))
54
55
56
57
58 static const QRectF boundingRect(const QPointF *points, int pointCount)
59 {
60     const QPointF *e = points;
61     const QPointF *last = points + pointCount;
62     qreal minx, maxx, miny, maxy;
63     minx = maxx = e->x();
64     miny = maxy = e->y();
65     while (++e < last) {
66         if (e->x() < minx)
67             minx = e->x();
68         else if (e->x() > maxx)
69             maxx = e->x();
70         if (e->y() < miny)
71             miny = e->y();
72         else if (e->y() > maxy)
73             maxy = e->y();
74     }
75     return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
76 }
77
78 void QOutlineMapper::curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) {
79 #ifdef QT_DEBUG_CONVERT
80     printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y());
81 #endif
82
83     QBezier bezier = QBezier::fromPoints(m_elements.last(), cp1, cp2, ep);
84     bezier.addToPolygon(m_elements, m_curve_threshold);
85     m_element_types.reserve(m_elements.size());
86     for (int i = m_elements.size() - m_element_types.size(); i; --i)
87         m_element_types << QPainterPath::LineToElement;
88     Q_ASSERT(m_elements.size() == m_element_types.size());
89 }
90
91
92 QT_FT_Outline *QOutlineMapper::convertPath(const QPainterPath &path)
93 {
94     Q_ASSERT(!path.isEmpty());
95     int elmCount = path.elementCount();
96 #ifdef QT_DEBUG_CONVERT
97     printf("QOutlineMapper::convertPath(), size=%d\n", elmCount);
98 #endif
99     beginOutline(path.fillRule());
100
101     for (int index=0; index<elmCount; ++index) {
102         const QPainterPath::Element &elm = path.elementAt(index);
103
104         switch (elm.type) {
105
106         case QPainterPath::MoveToElement:
107             if (index == elmCount - 1)
108                 continue;
109             moveTo(elm);
110             break;
111
112         case QPainterPath::LineToElement:
113             lineTo(elm);
114             break;
115
116         case QPainterPath::CurveToElement:
117             curveTo(elm, path.elementAt(index + 1), path.elementAt(index + 2));
118             index += 2;
119             break;
120
121         default:
122             break; // This will never hit..
123         }
124     }
125
126     endOutline();
127     return outline();
128 }
129
130 QT_FT_Outline *QOutlineMapper::convertPath(const QVectorPath &path)
131 {
132     int count = path.elementCount();
133
134 #ifdef QT_DEBUG_CONVERT
135     printf("QOutlineMapper::convertPath(VP), size=%d\n", count);
136 #endif
137     beginOutline(path.hasWindingFill() ? Qt::WindingFill : Qt::OddEvenFill);
138
139     if (path.elements()) {
140         // TODO: if we do closing of subpaths in convertElements instead we
141         // could avoid this loop
142         const QPainterPath::ElementType *elements = path.elements();
143         const QPointF *points = reinterpret_cast<const QPointF *>(path.points());
144
145         for (int index = 0; index < count; ++index) {
146             switch (elements[index]) {
147                 case QPainterPath::MoveToElement:
148                     if (index == count - 1)
149                         continue;
150                     moveTo(points[index]);
151                     break;
152
153                 case QPainterPath::LineToElement:
154                     lineTo(points[index]);
155                     break;
156
157                 case QPainterPath::CurveToElement:
158                     curveTo(points[index], points[index+1], points[index+2]);
159                     index += 2;
160                     break;
161
162                 default:
163                     break; // This will never hit..
164             }
165         }
166
167     } else {
168         // ### We can kill this copying and just use the buffer straight...
169
170         m_elements.resize(count);
171         if (count)
172             memcpy(m_elements.data(), path.points(), count* sizeof(QPointF));
173
174         m_element_types.resize(0);
175     }
176
177     endOutline();
178     return outline();
179 }
180
181
182 void QOutlineMapper::endOutline()
183 {
184     closeSubpath();
185
186     if (m_elements.isEmpty()) {
187         memset(&m_outline, 0, sizeof(m_outline));
188         return;
189     }
190
191     QPointF *elements = m_elements.data();
192
193     // Transform the outline
194     if (m_txop == QTransform::TxNone) {
195         // Nothing to do.
196     } else if (m_txop == QTransform::TxTranslate) {
197         for (int i = 0; i < m_elements.size(); ++i) {
198             QPointF &e = elements[i];
199             e = QPointF(e.x() + m_dx, e.y() + m_dy);
200         }
201     } else if (m_txop == QTransform::TxScale) {
202         for (int i = 0; i < m_elements.size(); ++i) {
203             QPointF &e = elements[i];
204             e = QPointF(m_m11 * e.x() + m_dx, m_m22 * e.y() + m_dy);
205         }
206     } else if (m_txop < QTransform::TxProject) {
207         for (int i = 0; i < m_elements.size(); ++i) {
208             QPointF &e = elements[i];
209             e = QPointF(m_m11 * e.x() + m_m21 * e.y() + m_dx,
210                         m_m22 * e.y() + m_m12 * e.x() + m_dy);
211         }
212     } else {
213         const QVectorPath vp((qreal *)elements, m_elements.size(),
214                              m_element_types.size() ? m_element_types.data() : 0);
215         QPainterPath path = vp.convertToPainterPath();
216         path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path);
217         if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
218             path.setFillRule(Qt::WindingFill);
219         uint old_txop = m_txop;
220         m_txop = QTransform::TxNone;
221         if (path.isEmpty())
222             m_valid = false;
223         else
224             convertPath(path);
225         m_txop = old_txop;
226         return;
227     }
228
229     if (m_round_coords) {
230         // round coordinates to match outlines drawn with drawLine_midpoint_i
231         for (int i = 0; i < m_elements.size(); ++i)
232             elements[i] = QPointF(qFloor(elements[i].x() + aliasedCoordinateDelta),
233                                   qFloor(elements[i].y() + aliasedCoordinateDelta));
234     }
235
236     controlPointRect = boundingRect(elements, m_elements.size());
237
238 #ifdef QT_DEBUG_CONVERT
239     printf(" - control point rect (%.2f, %.2f) %.2f x %.2f, clip=(%d,%d, %dx%d)\n",
240            controlPointRect.x(), controlPointRect.y(),
241            controlPointRect.width(), controlPointRect.height(),
242            m_clip_rect.x(), m_clip_rect.y(), m_clip_rect.width(), m_clip_rect.height());
243 #endif
244
245
246     // Check for out of dev bounds...
247     const bool do_clip = !m_in_clip_elements && ((controlPointRect.left() < -QT_RASTER_COORD_LIMIT
248                           || controlPointRect.right() > QT_RASTER_COORD_LIMIT
249                           || controlPointRect.top() < -QT_RASTER_COORD_LIMIT
250                           || controlPointRect.bottom() > QT_RASTER_COORD_LIMIT
251                           || controlPointRect.width() > QT_RASTER_COORD_LIMIT
252                           || controlPointRect.height() > QT_RASTER_COORD_LIMIT));
253
254     if (do_clip) {
255         clipElements(elements, elementTypes(), m_elements.size());
256     } else {
257         convertElements(elements, elementTypes(), m_elements.size());
258     }
259 }
260
261 void QOutlineMapper::convertElements(const QPointF *elements,
262                                        const QPainterPath::ElementType *types,
263                                        int element_count)
264 {
265
266     if (types) {
267         // Translate into FT coords
268         const QPointF *e = elements;
269         for (int i=0; i<element_count; ++i) {
270             switch (*types) {
271             case QPainterPath::MoveToElement:
272                 {
273                     QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
274                                               qreal_to_fixed_26_6(e->y()) };
275                     if (i != 0)
276                         m_contours << m_points.size() - 1;
277                     m_points << pt_fixed;
278                     m_tags <<  QT_FT_CURVE_TAG_ON;
279                 }
280                 break;
281
282             case QPainterPath::LineToElement:
283                 {
284                     QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
285                                               qreal_to_fixed_26_6(e->y()) };
286                     m_points << pt_fixed;
287                     m_tags << QT_FT_CURVE_TAG_ON;
288                 }
289                 break;
290
291             case QPainterPath::CurveToElement:
292                 {
293                     QT_FT_Vector cp1_fixed = { qreal_to_fixed_26_6(e->x()),
294                                                qreal_to_fixed_26_6(e->y()) };
295                     ++e;
296                     QT_FT_Vector cp2_fixed = { qreal_to_fixed_26_6((e)->x()),
297                                                qreal_to_fixed_26_6((e)->y()) };
298                     ++e;
299                     QT_FT_Vector ep_fixed = { qreal_to_fixed_26_6((e)->x()),
300                                               qreal_to_fixed_26_6((e)->y()) };
301
302                     m_points << cp1_fixed << cp2_fixed << ep_fixed;
303                     m_tags << QT_FT_CURVE_TAG_CUBIC
304                            << QT_FT_CURVE_TAG_CUBIC
305                            << QT_FT_CURVE_TAG_ON;
306
307                     types += 2;
308                     i += 2;
309                 }
310                 break;
311             default:
312                 break;
313             }
314             ++types;
315             ++e;
316         }
317     } else {
318         // Plain polygon...
319         const QPointF *last = elements + element_count;
320         const QPointF *e = elements;
321         while (e < last) {
322             QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
323                                       qreal_to_fixed_26_6(e->y()) };
324             m_points << pt_fixed;
325             m_tags << QT_FT_CURVE_TAG_ON;
326             ++e;
327         }
328     }
329
330     // close the very last subpath
331     m_contours << m_points.size() - 1;
332
333     m_outline.n_contours = m_contours.size();
334     m_outline.n_points = m_points.size();
335
336     m_outline.points = m_points.data();
337     m_outline.tags = m_tags.data();
338     m_outline.contours = m_contours.data();
339
340 #ifdef QT_DEBUG_CONVERT
341     printf("QOutlineMapper::endOutline\n");
342
343     printf(" - contours: %d\n", m_outline.n_contours);
344     for (int i=0; i<m_outline.n_contours; ++i) {
345         printf("   - %d\n", m_outline.contours[i]);
346     }
347
348     printf(" - points: %d\n", m_outline.n_points);
349     for (int i=0; i<m_outline.n_points; ++i) {
350         printf("   - %d -- %.2f, %.2f, (%d, %d)\n", i,
351                (double) (m_outline.points[i].x / 64.0),
352                (double) (m_outline.points[i].y / 64.0),
353                (int) m_outline.points[i].x, (int) m_outline.points[i].y);
354     }
355 #endif
356 }
357
358 void QOutlineMapper::clipElements(const QPointF *elements,
359                                     const QPainterPath::ElementType *types,
360                                     int element_count)
361 {
362     // We could save a bit of time by actually implementing them fully
363     // instead of going through convenience functionallity, but since
364     // this part of code hardly every used, it shouldn't matter.
365
366     m_in_clip_elements = true;
367
368     QPainterPath path;
369
370     if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
371         path.setFillRule(Qt::WindingFill);
372
373     if (types) {
374         for (int i=0; i<element_count; ++i) {
375             switch (types[i]) {
376             case QPainterPath::MoveToElement:
377                 path.moveTo(elements[i]);
378                 break;
379
380             case QPainterPath::LineToElement:
381                 path.lineTo(elements[i]);
382                 break;
383
384             case QPainterPath::CurveToElement:
385                 path.cubicTo(elements[i], elements[i+1], elements[i+2]);
386                 i += 2;
387                 break;
388             default:
389                 break;
390             }
391         }
392     } else {
393         path.moveTo(elements[0]);
394         for (int i=1; i<element_count; ++i)
395             path.lineTo(elements[i]);
396     }
397
398     QPainterPath clipPath;
399     clipPath.addRect(m_clip_rect);
400     QPainterPath clippedPath = path.intersected(clipPath);
401     uint old_txop = m_txop;
402     m_txop = QTransform::TxNone;
403     if (clippedPath.isEmpty())
404         m_valid = false;
405     else
406         convertPath(clippedPath);
407     m_txop = old_txop;
408
409     m_in_clip_elements = false;
410 }
411
412 QT_END_NAMESPACE