1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qoutlinemapper_p.h"
43 #include <private/qpainterpath_p.h>
45 #include <private/qbezier_p.h>
51 static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
53 #define qreal_to_fixed_26_6(f) (int(f * 64))
58 static const QRectF boundingRect(const QPointF *points, int pointCount)
60 const QPointF *e = points;
61 const QPointF *last = points + pointCount;
62 qreal minx, maxx, miny, maxy;
68 else if (e->x() > maxx)
72 else if (e->y() > maxy)
75 return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
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());
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());
92 QT_FT_Outline *QOutlineMapper::convertPath(const QPainterPath &path)
94 Q_ASSERT(!path.isEmpty());
95 int elmCount = path.elementCount();
96 #ifdef QT_DEBUG_CONVERT
97 printf("QOutlineMapper::convertPath(), size=%d\n", elmCount);
99 beginOutline(path.fillRule());
101 for (int index=0; index<elmCount; ++index) {
102 const QPainterPath::Element &elm = path.elementAt(index);
106 case QPainterPath::MoveToElement:
107 if (index == elmCount - 1)
112 case QPainterPath::LineToElement:
116 case QPainterPath::CurveToElement:
117 curveTo(elm, path.elementAt(index + 1), path.elementAt(index + 2));
122 break; // This will never hit..
130 QT_FT_Outline *QOutlineMapper::convertPath(const QVectorPath &path)
132 int count = path.elementCount();
134 #ifdef QT_DEBUG_CONVERT
135 printf("QOutlineMapper::convertPath(VP), size=%d\n", count);
137 beginOutline(path.hasWindingFill() ? Qt::WindingFill : Qt::OddEvenFill);
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());
145 for (int index = 0; index < count; ++index) {
146 switch (elements[index]) {
147 case QPainterPath::MoveToElement:
148 if (index == count - 1)
150 moveTo(points[index]);
153 case QPainterPath::LineToElement:
154 lineTo(points[index]);
157 case QPainterPath::CurveToElement:
158 curveTo(points[index], points[index+1], points[index+2]);
163 break; // This will never hit..
168 // ### We can kill this copying and just use the buffer straight...
170 m_elements.resize(count);
172 memcpy(m_elements.data(), path.points(), count* sizeof(QPointF));
174 m_element_types.resize(0);
182 void QOutlineMapper::endOutline()
186 if (m_elements.isEmpty()) {
187 memset(&m_outline, 0, sizeof(m_outline));
191 QPointF *elements = m_elements.data();
193 // Transform the outline
194 if (m_txop == QTransform::TxNone) {
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);
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);
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);
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;
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));
236 controlPointRect = boundingRect(elements, m_elements.size());
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());
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));
255 clipElements(elements, elementTypes(), m_elements.size());
257 convertElements(elements, elementTypes(), m_elements.size());
261 void QOutlineMapper::convertElements(const QPointF *elements,
262 const QPainterPath::ElementType *types,
267 // Translate into FT coords
268 const QPointF *e = elements;
269 for (int i=0; i<element_count; ++i) {
271 case QPainterPath::MoveToElement:
273 QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
274 qreal_to_fixed_26_6(e->y()) };
276 m_contours << m_points.size() - 1;
277 m_points << pt_fixed;
278 m_tags << QT_FT_CURVE_TAG_ON;
282 case QPainterPath::LineToElement:
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;
291 case QPainterPath::CurveToElement:
293 QT_FT_Vector cp1_fixed = { qreal_to_fixed_26_6(e->x()),
294 qreal_to_fixed_26_6(e->y()) };
296 QT_FT_Vector cp2_fixed = { qreal_to_fixed_26_6((e)->x()),
297 qreal_to_fixed_26_6((e)->y()) };
299 QT_FT_Vector ep_fixed = { qreal_to_fixed_26_6((e)->x()),
300 qreal_to_fixed_26_6((e)->y()) };
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;
319 const QPointF *last = elements + element_count;
320 const QPointF *e = elements;
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;
330 // close the very last subpath
331 m_contours << m_points.size() - 1;
333 m_outline.n_contours = m_contours.size();
334 m_outline.n_points = m_points.size();
336 m_outline.points = m_points.data();
337 m_outline.tags = m_tags.data();
338 m_outline.contours = m_contours.data();
340 #ifdef QT_DEBUG_CONVERT
341 printf("QOutlineMapper::endOutline\n");
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]);
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);
358 void QOutlineMapper::clipElements(const QPointF *elements,
359 const QPainterPath::ElementType *types,
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.
366 m_in_clip_elements = true;
370 if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
371 path.setFillRule(Qt::WindingFill);
374 for (int i=0; i<element_count; ++i) {
376 case QPainterPath::MoveToElement:
377 path.moveTo(elements[i]);
380 case QPainterPath::LineToElement:
381 path.lineTo(elements[i]);
384 case QPainterPath::CurveToElement:
385 path.cubicTo(elements[i], elements[i+1], elements[i+2]);
393 path.moveTo(elements[0]);
394 for (int i=1; i<element_count; ++i)
395 path.lineTo(elements[i]);
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())
406 convertPath(clippedPath);
409 m_in_clip_elements = false;