1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qoutlinemapper_p.h"
43 #include <private/qpainterpath_p.h>
44 #include "qmath.h"
45 
46 #include <stdlib.h>
47 
48 QT_BEGIN_NAMESPACE
49 
50 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
51 
52 #define qreal_to_fixed_26_6(f) (int(f * 64))
53 
54 
55 
56 
boundingRect(const QPointF * points,int pointCount)57 static const QRectF boundingRect(const QPointF *points, int pointCount)
58 {
59     const QPointF *e = points;
60     const QPointF *last = points + pointCount;
61     qreal minx, maxx, miny, maxy;
62     minx = maxx = e->x();
63     miny = maxy = e->y();
64     while (++e < last) {
65         if (e->x() < minx)
66             minx = e->x();
67         else if (e->x() > maxx)
68             maxx = e->x();
69         if (e->y() < miny)
70             miny = e->y();
71         else if (e->y() > maxy)
72             maxy = e->y();
73     }
74     return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
75 }
76 
77 
convertPath(const QPainterPath & path)78 QT_FT_Outline *QOutlineMapper::convertPath(const QPainterPath &path)
79 {
80     Q_ASSERT(!path.isEmpty());
81     int elmCount = path.elementCount();
82 #ifdef QT_DEBUG_CONVERT
83     printf("QOutlineMapper::convertPath(), size=%d\n", elmCount);
84 #endif
85     beginOutline(path.fillRule());
86 
87     for (int index=0; index<elmCount; ++index) {
88         const QPainterPath::Element &elm = path.elementAt(index);
89 
90         switch (elm.type) {
91 
92         case QPainterPath::MoveToElement:
93             if (index == elmCount - 1)
94                 continue;
95             moveTo(elm);
96             break;
97 
98         case QPainterPath::LineToElement:
99             lineTo(elm);
100             break;
101 
102         case QPainterPath::CurveToElement:
103             curveTo(elm, path.elementAt(index + 1), path.elementAt(index + 2));
104             index += 2;
105             break;
106 
107         default:
108             break; // This will never hit..
109         }
110     }
111 
112     endOutline();
113     return outline();
114 }
115 
convertPath(const QVectorPath & path)116 QT_FT_Outline *QOutlineMapper::convertPath(const QVectorPath &path)
117 {
118     int count = path.elementCount();
119 
120 #ifdef QT_DEBUG_CONVERT
121     printf("QOutlineMapper::convertPath(VP), size=%d\n", count);
122 #endif
123     beginOutline(path.hasWindingFill() ? Qt::WindingFill : Qt::OddEvenFill);
124 
125     if (path.elements()) {
126         // TODO: if we do closing of subpaths in convertElements instead we
127         // could avoid this loop
128         const QPainterPath::ElementType *elements = path.elements();
129         const QPointF *points = reinterpret_cast<const QPointF *>(path.points());
130 
131         for (int index = 0; index < count; ++index) {
132             switch (elements[index]) {
133                 case QPainterPath::MoveToElement:
134                     if (index == count - 1)
135                         continue;
136                     moveTo(points[index]);
137                     break;
138 
139                 case QPainterPath::LineToElement:
140                     lineTo(points[index]);
141                     break;
142 
143                 case QPainterPath::CurveToElement:
144                     curveTo(points[index], points[index+1], points[index+2]);
145                     index += 2;
146                     break;
147 
148                 default:
149                     break; // This will never hit..
150             }
151         }
152 
153     } else {
154         // ### We can kill this copying and just use the buffer straight...
155 
156         m_elements.resize(count);
157         if (count)
158             memcpy(m_elements.data(), path.points(), count* sizeof(QPointF));
159 
160         m_element_types.resize(0);
161     }
162 
163     endOutline();
164     return outline();
165 }
166 
167 
endOutline()168 void QOutlineMapper::endOutline()
169 {
170     closeSubpath();
171 
172     int element_count = m_elements.size();
173 
174     if (element_count == 0) {
175         memset(&m_outline, 0, sizeof(m_outline));
176         return;
177     }
178 
179     QPointF *elements;
180 
181     // Transform the outline
182     if (m_txop == QTransform::TxNone) {
183         elements = m_elements.data();
184     } else {
185         if (m_txop == QTransform::TxTranslate) {
186             for (int i=0; i<m_elements.size(); ++i) {
187                 const QPointF &e = m_elements.at(i);
188                 m_elements_dev << QPointF(e.x() + m_dx, e.y() + m_dy);
189             }
190         } else if (m_txop == QTransform::TxScale) {
191             for (int i=0; i<m_elements.size(); ++i) {
192                 const QPointF &e = m_elements.at(i);
193                 m_elements_dev << QPointF(m_m11 * e.x() + m_dx, m_m22 * e.y() + m_dy);
194             }
195         } else if (m_txop < QTransform::TxProject) {
196             for (int i=0; i<m_elements.size(); ++i) {
197                 const QPointF &e = m_elements.at(i);
198                 m_elements_dev << QPointF(m_m11 * e.x() + m_m21 * e.y() + m_dx,
199                                           m_m22 * e.y() + m_m12 * e.x() + m_dy);
200             }
201         } else {
202             const QVectorPath vp((qreal *)m_elements.data(), m_elements.size(), m_element_types.size() ? m_element_types.data() : 0);
203             QPainterPath path = vp.convertToPainterPath();
204             path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path);
205             if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
206                 path.setFillRule(Qt::WindingFill);
207             uint old_txop = m_txop;
208             m_txop = QTransform::TxNone;
209             if (path.isEmpty())
210                 m_valid = false;
211             else
212                 convertPath(path);
213             m_txop = old_txop;
214             return;
215         }
216         elements = m_elements_dev.data();
217     }
218 
219     controlPointRect = boundingRect(elements, element_count);
220 
221 #ifdef QT_DEBUG_CONVERT
222     printf(" - control point rect (%.2f, %.2f) %.2f x %.2f, clip=(%d,%d, %dx%d)\n",
223            controlPointRect.x(), controlPointRect.y(),
224            controlPointRect.width(), controlPointRect.height(),
225            m_clip_rect.x(), m_clip_rect.y(), m_clip_rect.width(), m_clip_rect.height());
226 #endif
227 
228 
229     // Check for out of dev bounds...
230     const bool do_clip = !m_in_clip_elements && ((controlPointRect.left() < -QT_RASTER_COORD_LIMIT
231                           || controlPointRect.right() > QT_RASTER_COORD_LIMIT
232                           || controlPointRect.top() < -QT_RASTER_COORD_LIMIT
233                           || controlPointRect.bottom() > QT_RASTER_COORD_LIMIT
234                           || controlPointRect.width() > QT_RASTER_COORD_LIMIT
235                           || controlPointRect.height() > QT_RASTER_COORD_LIMIT));
236 
237     if (do_clip) {
238         clipElements(elements, elementTypes(), element_count);
239     } else {
240         convertElements(elements, elementTypes(), element_count);
241     }
242 }
243 
convertElements(const QPointF * elements,const QPainterPath::ElementType * types,int element_count)244 void QOutlineMapper::convertElements(const QPointF *elements,
245                                        const QPainterPath::ElementType *types,
246                                        int element_count)
247 {
248 
249     if (types) {
250         // Translate into FT coords
251         const QPointF *e = elements;
252         for (int i=0; i<element_count; ++i) {
253             switch (*types) {
254             case QPainterPath::MoveToElement:
255                 {
256                     QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
257                                               qreal_to_fixed_26_6(e->y()) };
258                     if (i != 0)
259                         m_contours << m_points.size() - 1;
260                     m_points << pt_fixed;
261                     m_tags <<  QT_FT_CURVE_TAG_ON;
262                 }
263                 break;
264 
265             case QPainterPath::LineToElement:
266                 {
267                     QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
268                                               qreal_to_fixed_26_6(e->y()) };
269                     m_points << pt_fixed;
270                     m_tags << QT_FT_CURVE_TAG_ON;
271                 }
272                 break;
273 
274             case QPainterPath::CurveToElement:
275                 {
276                     QT_FT_Vector cp1_fixed = { qreal_to_fixed_26_6(e->x()),
277                                                qreal_to_fixed_26_6(e->y()) };
278                     ++e;
279                     QT_FT_Vector cp2_fixed = { qreal_to_fixed_26_6((e)->x()),
280                                                qreal_to_fixed_26_6((e)->y()) };
281                     ++e;
282                     QT_FT_Vector ep_fixed = { qreal_to_fixed_26_6((e)->x()),
283                                               qreal_to_fixed_26_6((e)->y()) };
284 
285                     m_points << cp1_fixed << cp2_fixed << ep_fixed;
286                     m_tags << QT_FT_CURVE_TAG_CUBIC
287                            << QT_FT_CURVE_TAG_CUBIC
288                            << QT_FT_CURVE_TAG_ON;
289 
290                     types += 2;
291                     i += 2;
292                 }
293                 break;
294             default:
295                 break;
296             }
297             ++types;
298             ++e;
299         }
300     } else {
301         // Plain polygon...
302         const QPointF *last = elements + element_count;
303         const QPointF *e = elements;
304         while (e < last) {
305             QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
306                                       qreal_to_fixed_26_6(e->y()) };
307             m_points << pt_fixed;
308             m_tags << QT_FT_CURVE_TAG_ON;
309             ++e;
310         }
311     }
312 
313     // close the very last subpath
314     m_contours << m_points.size() - 1;
315 
316     m_outline.n_contours = m_contours.size();
317     m_outline.n_points = m_points.size();
318 
319     m_outline.points = m_points.data();
320     m_outline.tags = m_tags.data();
321     m_outline.contours = m_contours.data();
322 
323 #ifdef QT_DEBUG_CONVERT
324     printf("QOutlineMapper::endOutline\n");
325 
326     printf(" - contours: %d\n", m_outline.n_contours);
327     for (int i=0; i<m_outline.n_contours; ++i) {
328         printf("   - %d\n", m_outline.contours[i]);
329     }
330 
331     printf(" - points: %d\n", m_outline.n_points);
332     for (int i=0; i<m_outline.n_points; ++i) {
333         printf("   - %d -- %.2f, %.2f, (%d, %d)\n", i,
334                (double) (m_outline.points[i].x / 64.0),
335                (double) (m_outline.points[i].y / 64.0),
336                (int) m_outline.points[i].x, (int) m_outline.points[i].y);
337     }
338 #endif
339 }
340 
clipElements(const QPointF * elements,const QPainterPath::ElementType * types,int element_count)341 void QOutlineMapper::clipElements(const QPointF *elements,
342                                     const QPainterPath::ElementType *types,
343                                     int element_count)
344 {
345     // We could save a bit of time by actually implementing them fully
346     // instead of going through convenience functionallity, but since
347     // this part of code hardly every used, it shouldn't matter.
348 
349     m_in_clip_elements = true;
350 
351     QPainterPath path;
352 
353     if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
354         path.setFillRule(Qt::WindingFill);
355 
356     if (types) {
357         for (int i=0; i<element_count; ++i) {
358             switch (types[i]) {
359             case QPainterPath::MoveToElement:
360                 path.moveTo(elements[i]);
361                 break;
362 
363             case QPainterPath::LineToElement:
364                 path.lineTo(elements[i]);
365                 break;
366 
367             case QPainterPath::CurveToElement:
368                 path.cubicTo(elements[i], elements[i+1], elements[i+2]);
369                 i += 2;
370                 break;
371             default:
372                 break;
373             }
374         }
375     } else {
376         path.moveTo(elements[0]);
377         for (int i=1; i<element_count; ++i)
378             path.lineTo(elements[i]);
379     }
380 
381     QPainterPath clipPath;
382     clipPath.addRect(m_clip_rect);
383     QPainterPath clippedPath = path.intersected(clipPath);
384     uint old_txop = m_txop;
385     m_txop = QTransform::TxNone;
386     if (clippedPath.isEmpty())
387         m_valid = false;
388     else
389         convertPath(clippedPath);
390     m_txop = old_txop;
391 
392     m_in_clip_elements = false;
393 }
394 
395 QT_END_NAMESPACE
396