Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qoutlinemapper.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include "qbezier_p.h"
8#include "qmath.h"
11
12#include <stdlib.h>
13
14QT_BEGIN_NAMESPACE
15
16#define qreal_to_fixed_26_6(f) (qRound(f * 64))
17
18
19
20
21static const QRectF boundingRect(const QPointF *points, int pointCount)
22{
23 const QPointF *e = points;
24 const QPointF *last = points + pointCount;
25 qreal minx, maxx, miny, maxy;
26 minx = maxx = e->x();
27 miny = maxy = e->y();
28 while (++e < last) {
29 if (e->x() < minx)
30 minx = e->x();
31 else if (e->x() > maxx)
32 maxx = e->x();
33 if (e->y() < miny)
34 miny = e->y();
35 else if (e->y() > maxy)
36 maxy = e->y();
37 }
38 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
39}
40
41void QOutlineMapper::setClipRect(QRect clipRect)
42{
43 auto limitCoords = [](QRect r) {
44 const QRect limitRect(QPoint(-QT_RASTER_COORD_LIMIT, -QT_RASTER_COORD_LIMIT),
46 r &= limitRect;
47 r.setWidth(qMin(r.width(), QT_RASTER_COORD_LIMIT));
48 r.setHeight(qMin(r.height(), QT_RASTER_COORD_LIMIT));
49 return r;
50 };
51
52 if (clipRect != m_clip_rect) {
53 m_clip_rect = limitCoords(clipRect);
54 const int mw = 1 << 10; // margin width. No need to trigger clipping for slight overshooting
55 m_clip_trigger_rect = QRectF(limitCoords(m_clip_rect.adjusted(-mw, -mw, mw, mw)));
56 }
57}
58
59void QOutlineMapper::curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) {
60#ifdef QT_DEBUG_CONVERT
61 printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y());
62#endif
63
64 if (!m_elements.size())
65 return;
66 QBezier bezier = QBezier::fromPoints(m_elements.last(), cp1, cp2, ep);
67
68 bool outsideClip = false;
69 // Test one point first before doing a full intersection test.
70 if (!QRectF(m_clip_rect).contains(m_transform.map(ep))) {
71 QRectF potentialCurveArea = m_transform.mapRect(bezier.bounds());
72 outsideClip = !potentialCurveArea.intersects(m_clip_rect);
73 }
74 if (outsideClip) {
75 // The curve is entirely outside the clip rect, so just
76 // approximate it with a line that closes the path.
77 lineTo(ep);
78 } else {
79 bezier.addToPolygon(m_elements, m_curve_threshold);
80 m_element_types.reserve(m_elements.size());
81 for (int i = m_elements.size() - m_element_types.size(); i; --i)
82 m_element_types << QPainterPath::LineToElement;
83 }
84 Q_ASSERT(m_elements.size() == m_element_types.size());
85}
86
87
88QT_FT_Outline *QOutlineMapper::convertPath(const QPainterPath &path)
89{
90 Q_ASSERT(!path.isEmpty());
91 int elmCount = path.elementCount();
92#ifdef QT_DEBUG_CONVERT
93 printf("QOutlineMapper::convertPath(), size=%d\n", elmCount);
94#endif
95 beginOutline(path.fillRule());
96
97 for (int index=0; index<elmCount; ++index) {
98 const QPainterPath::Element &elm = path.elementAt(index);
99
100 switch (elm.type) {
101
102 case QPainterPath::MoveToElement:
103 if (index == elmCount - 1)
104 continue;
105 moveTo(elm);
106 break;
107
108 case QPainterPath::LineToElement:
109 lineTo(elm);
110 break;
111
112 case QPainterPath::CurveToElement:
113 curveTo(elm, path.elementAt(index + 1), path.elementAt(index + 2));
114 index += 2;
115 break;
116
117 default:
118 break; // This will never hit..
119 }
120 }
121
123 return outline();
124}
125
126QT_FT_Outline *QOutlineMapper::convertPath(const QVectorPath &path)
127{
128 int count = path.elementCount();
129
130#ifdef QT_DEBUG_CONVERT
131 printf("QOutlineMapper::convertPath(VP), size=%d\n", count);
132#endif
133 beginOutline(path.hasWindingFill() ? Qt::WindingFill : Qt::OddEvenFill);
134
135 if (path.elements()) {
136 // TODO: if we do closing of subpaths in convertElements instead we
137 // could avoid this loop
138 const QPainterPath::ElementType *elements = path.elements();
139 const QPointF *points = reinterpret_cast<const QPointF *>(path.points());
140
141 for (int index = 0; index < count; ++index) {
142 switch (elements[index]) {
143 case QPainterPath::MoveToElement:
144 if (index == count - 1)
145 continue;
146 moveTo(points[index]);
147 break;
148
149 case QPainterPath::LineToElement:
150 lineTo(points[index]);
151 break;
152
153 case QPainterPath::CurveToElement:
154 curveTo(points[index], points[index+1], points[index+2]);
155 index += 2;
156 break;
157
158 default:
159 break; // This will never hit..
160 }
161 }
162
163 } else {
164 // ### We can kill this copying and just use the buffer straight...
165
166 m_elements.resize(count);
167 if (count)
168 memcpy(static_cast<void *>(m_elements.data()), static_cast<const void *>(path.points()), count* sizeof(QPointF));
169
170 m_element_types.resize(0);
171 }
172
174 return outline();
175}
176
177
179{
181
182 if (m_elements.isEmpty()) {
183 memset(&m_outline, 0, sizeof(m_outline));
184 return;
185 }
186
187 QPointF *elements = m_elements.data();
188
189 // Transform the outline
190 if (m_transform.isIdentity()) {
191 // Nothing to do
192 } else if (m_transform.type() < QTransform::TxProject) {
193 for (int i = 0; i < m_elements.size(); ++i)
194 elements[i] = m_transform.map(elements[i]);
195 } else {
196 const QVectorPath vp((qreal *)elements, m_elements.size(),
197 m_element_types.size() ? m_element_types.data() : nullptr);
198 QPainterPath path = vp.convertToPainterPath();
199 path = m_transform.map(path);
200 if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
201 path.setFillRule(Qt::WindingFill);
202 if (path.isEmpty()) {
203 m_valid = false;
204 } else {
205 QTransform oldTransform = m_transform;
206 m_transform.reset();
207 convertPath(path);
208 m_transform = oldTransform;
209 }
210 return;
211 }
212
213 controlPointRect = boundingRect(elements, m_elements.size());
214
215#ifdef QT_DEBUG_CONVERT
216 printf(" - control point rect (%.2f, %.2f) %.2f x %.2f, clip=(%d,%d, %dx%d)\n",
217 controlPointRect.x(), controlPointRect.y(),
218 controlPointRect.width(), controlPointRect.height(),
219 m_clip_rect.x(), m_clip_rect.y(), m_clip_rect.width(), m_clip_rect.height());
220#endif
221
222 // Avoid rasterizing outside cliprect: faster, and ensures coords < QT_RASTER_COORD_LIMIT
223 if (!m_in_clip_elements && !m_clip_trigger_rect.contains(controlPointRect)) {
224 clipElements(elements, elementTypes(), m_elements.size());
225 } else {
226 convertElements(elements, elementTypes(), m_elements.size());
227 }
228}
229
230void QOutlineMapper::convertElements(const QPointF *elements,
231 const QPainterPath::ElementType *types,
232 int element_count)
233{
234
235 if (types) {
236 // Translate into FT coords
237 const QPointF *e = elements;
238 for (int i=0; i<element_count; ++i) {
239 switch (*types) {
240 case QPainterPath::MoveToElement:
241 {
242 QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
243 qreal_to_fixed_26_6(e->y()) };
244 if (i != 0)
245 m_contours << m_points.size() - 1;
246 m_points << pt_fixed;
247 m_tags << QT_FT_CURVE_TAG_ON;
248 }
249 break;
250
251 case QPainterPath::LineToElement:
252 {
253 QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
254 qreal_to_fixed_26_6(e->y()) };
255 m_points << pt_fixed;
256 m_tags << QT_FT_CURVE_TAG_ON;
257 }
258 break;
259
260 case QPainterPath::CurveToElement:
261 {
262 QT_FT_Vector cp1_fixed = { qreal_to_fixed_26_6(e->x()),
263 qreal_to_fixed_26_6(e->y()) };
264 ++e;
265 QT_FT_Vector cp2_fixed = { qreal_to_fixed_26_6((e)->x()),
266 qreal_to_fixed_26_6((e)->y()) };
267 ++e;
268 QT_FT_Vector ep_fixed = { qreal_to_fixed_26_6((e)->x()),
269 qreal_to_fixed_26_6((e)->y()) };
270
271 m_points << cp1_fixed << cp2_fixed << ep_fixed;
272 m_tags << QT_FT_CURVE_TAG_CUBIC
273 << QT_FT_CURVE_TAG_CUBIC
274 << QT_FT_CURVE_TAG_ON;
275
276 types += 2;
277 i += 2;
278 }
279 break;
280 default:
281 break;
282 }
283 ++types;
284 ++e;
285 }
286 } else {
287 // Plain polygon...
288 const QPointF *last = elements + element_count;
289 const QPointF *e = elements;
290 while (e < last) {
291 QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
292 qreal_to_fixed_26_6(e->y()) };
293 m_points << pt_fixed;
294 m_tags << QT_FT_CURVE_TAG_ON;
295 ++e;
296 }
297 }
298
299 // close the very last subpath
300 m_contours << m_points.size() - 1;
301
302 m_outline.n_contours = m_contours.size();
303 m_outline.n_points = m_points.size();
304
305 m_outline.points = m_points.data();
306 m_outline.tags = m_tags.data();
307 m_outline.contours = m_contours.data();
308
309#ifdef QT_DEBUG_CONVERT
310 printf("QOutlineMapper::endOutline\n");
311
312 printf(" - contours: %d\n", m_outline.n_contours);
313 for (int i=0; i<m_outline.n_contours; ++i) {
314 printf(" - %d\n", m_outline.contours[i]);
315 }
316
317 printf(" - points: %d\n", m_outline.n_points);
318 for (int i=0; i<m_outline.n_points; ++i) {
319 printf(" - %d -- %.2f, %.2f, (%d, %d)\n", i,
320 (double) (m_outline.points[i].x / 64.0),
321 (double) (m_outline.points[i].y / 64.0),
322 (int) m_outline.points[i].x, (int) m_outline.points[i].y);
323 }
324#endif
325}
326
327void QOutlineMapper::clipElements(const QPointF *elements,
328 const QPainterPath::ElementType *types,
329 int element_count)
330{
331 // We could save a bit of time by actually implementing them fully
332 // instead of going through convenience functionality, but since
333 // this part of code hardly every used, it shouldn't matter.
334
335 QScopedValueRollback<bool> in_clip_elements(m_in_clip_elements, true);
336
337 QPainterPath path;
338
339 if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
340 path.setFillRule(Qt::WindingFill);
341
342 if (types) {
343 for (int i=0; i<element_count; ++i) {
344 switch (types[i]) {
345 case QPainterPath::MoveToElement:
346 path.moveTo(elements[i]);
347 break;
348
349 case QPainterPath::LineToElement:
350 path.lineTo(elements[i]);
351 break;
352
353 case QPainterPath::CurveToElement:
354 path.cubicTo(elements[i], elements[i+1], elements[i+2]);
355 i += 2;
356 break;
357 default:
358 break;
359 }
360 }
361 } else {
362 path.moveTo(elements[0]);
363 for (int i=1; i<element_count; ++i)
364 path.lineTo(elements[i]);
365 }
366
367 QPainterPath clipPath;
368 clipPath.addRect(m_clip_rect);
369 QPainterPath clippedPath = path.intersected(clipPath);
370 if (clippedPath.isEmpty()) {
371 m_valid = false;
372 } else {
373 QTransform oldTransform = m_transform;
374 m_transform.reset();
375 convertPath(clippedPath);
376 m_transform = oldTransform;
377 }
378}
379
380QT_END_NAMESPACE
void clipElements(const QPointF *points, const QPainterPath::ElementType *types, int count)
void convertElements(const QPointF *points, const QPainterPath::ElementType *types, int count)
void curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep)
void setClipRect(QRect clipRect)
QT_FT_Outline * convertPath(const QPainterPath &path)
#define qreal_to_fixed_26_6(f)
QT_BEGIN_NAMESPACE constexpr int QT_RASTER_COORD_LIMIT