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