New algorithm for drawing thin lines
[profile/ivi/qtbase.git] / src / gui / painting / qpaintengineex.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qpaintengineex_p.h"
43 #include "qpainter_p.h"
44 #include "qstroker_p.h"
45 #include "qbezier_p.h"
46 #include <private/qpainterpath_p.h>
47
48 #include <qvarlengtharray.h>
49 #include <qdebug.h>
50
51
52 QT_BEGIN_NAMESPACE
53
54 /*******************************************************************************
55  *
56  * class QVectorPath
57  *
58  */
59 QVectorPath::~QVectorPath()
60 {
61     if (m_hints & ShouldUseCacheHint) {
62         CacheEntry *e = m_cache;
63         while (e) {
64             if (e->data)
65                 e->cleanup(e->engine, e->data);
66             CacheEntry *n = e->next;
67             delete e;
68             e = n;
69         }
70     }
71 }
72
73
74 QRectF QVectorPath::controlPointRect() const
75 {
76     if (m_hints & ControlPointRect)
77         return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
78
79     if (m_count == 0) {
80         m_cp_rect.x1 = m_cp_rect.x2 = m_cp_rect.y1 = m_cp_rect.y2 = 0;
81         m_hints |= ControlPointRect;
82         return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
83     }
84     Q_ASSERT(m_points && m_count > 0);
85
86     const qreal *pts = m_points;
87     m_cp_rect.x1 = m_cp_rect.x2 = *pts;
88     ++pts;
89     m_cp_rect.y1 = m_cp_rect.y2 = *pts;
90     ++pts;
91
92     const qreal *epts = m_points + (m_count << 1);
93     while (pts < epts) {
94         qreal x = *pts;
95         if (x < m_cp_rect.x1) m_cp_rect.x1 = x;
96         else if (x > m_cp_rect.x2) m_cp_rect.x2 = x;
97         ++pts;
98
99         qreal y = *pts;
100         if (y < m_cp_rect.y1) m_cp_rect.y1 = y;
101         else if (y > m_cp_rect.y2) m_cp_rect.y2 = y;
102         ++pts;
103     }
104
105     m_hints |= ControlPointRect;
106     return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
107 }
108
109
110 QVectorPath::CacheEntry *QVectorPath::addCacheData(QPaintEngineEx *engine, void *data,
111                                                    qvectorpath_cache_cleanup cleanup) const{
112     Q_ASSERT(!lookupCacheData(engine));
113     if ((m_hints & IsCachedHint) == 0) {
114         m_cache = 0;
115         m_hints |= IsCachedHint;
116     }
117     CacheEntry *e = new CacheEntry;
118     e->engine = engine;
119     e->data = data;
120     e->cleanup = cleanup;
121     e->next = m_cache;
122     m_cache = e;
123     return m_cache;
124 }
125
126
127 const QVectorPath &qtVectorPathForPath(const QPainterPath &path)
128 {
129     Q_ASSERT(path.d_func());
130     return path.d_func()->vectorPath();
131 }
132
133 #ifndef QT_NO_DEBUG_STREAM
134 QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path)
135 {
136     QRectF rf = path.controlPointRect();
137     s << "QVectorPath(size:" << path.elementCount()
138       << " hints:" << hex << path.hints()
139       << rf << ')';
140     return s;
141 }
142 #endif
143
144 /*******************************************************************************
145  *
146  * class QPaintEngineExPrivate:
147  *
148  */
149
150
151 struct StrokeHandler {
152     StrokeHandler(int reserve) : pts(reserve), types(reserve) {}
153     QDataBuffer<qreal> pts;
154     QDataBuffer<QPainterPath::ElementType> types;
155 };
156
157
158 QPaintEngineExPrivate::QPaintEngineExPrivate()
159     : dasher(&stroker),
160       strokeHandler(0),
161       activeStroker(0),
162       strokerPen(Qt::NoPen)
163 {
164 }
165
166
167 QPaintEngineExPrivate::~QPaintEngineExPrivate()
168 {
169     delete strokeHandler;
170 }
171
172
173 void QPaintEngineExPrivate::replayClipOperations()
174 {
175     Q_Q(QPaintEngineEx);
176
177     QPainter *p = q->painter();
178     if (!p || !p->d_ptr)
179         return;
180
181     QList<QPainterClipInfo> clipInfo = p->d_ptr->state->clipInfo;
182
183     QTransform transform = q->state()->matrix;
184
185     for (int i = 0; i <  clipInfo.size(); ++i) {
186         const QPainterClipInfo &info = clipInfo.at(i);
187
188         if (info.matrix != q->state()->matrix) {
189             q->state()->matrix = info.matrix;
190             q->transformChanged();
191         }
192
193         switch (info.clipType) {
194         case QPainterClipInfo::RegionClip:
195             q->clip(info.region, info.operation);
196             break;
197         case QPainterClipInfo::PathClip:
198             q->clip(info.path, info.operation);
199             break;
200         case QPainterClipInfo::RectClip:
201             q->clip(info.rect, info.operation);
202             break;
203         case QPainterClipInfo::RectFClip: {
204             qreal right = info.rectf.x() + info.rectf.width();
205             qreal bottom = info.rectf.y() + info.rectf.height();
206             qreal pts[] = { info.rectf.x(), info.rectf.y(),
207                             right, info.rectf.y(),
208                             right, bottom,
209                             info.rectf.x(), bottom };
210             QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
211             q->clip(vp, info.operation);
212             break;
213             }
214         }
215     }
216
217     if (transform != q->state()->matrix) {
218         q->state()->matrix = transform;
219         q->transformChanged();
220     }
221 }
222
223
224 bool QPaintEngineExPrivate::hasClipOperations() const
225 {
226     Q_Q(const QPaintEngineEx);
227
228     QPainter *p = q->painter();
229     if (!p || !p->d_ptr)
230         return false;
231
232     QList<QPainterClipInfo> clipInfo = p->d_ptr->state->clipInfo;
233
234     return !clipInfo.isEmpty();
235 }
236
237 /*******************************************************************************
238  *
239  * class QPaintEngineEx:
240  *
241  */
242
243 static QPainterPath::ElementType qpaintengineex_ellipse_types[] = {
244     QPainterPath::MoveToElement,
245     QPainterPath::CurveToElement,
246     QPainterPath::CurveToDataElement,
247     QPainterPath::CurveToDataElement,
248
249     QPainterPath::CurveToElement,
250     QPainterPath::CurveToDataElement,
251     QPainterPath::CurveToDataElement,
252
253     QPainterPath::CurveToElement,
254     QPainterPath::CurveToDataElement,
255     QPainterPath::CurveToDataElement,
256
257     QPainterPath::CurveToElement,
258     QPainterPath::CurveToDataElement,
259     QPainterPath::CurveToDataElement
260 };
261
262 static QPainterPath::ElementType qpaintengineex_line_types_16[] = {
263     QPainterPath::MoveToElement, QPainterPath::LineToElement,
264     QPainterPath::MoveToElement, QPainterPath::LineToElement,
265     QPainterPath::MoveToElement, QPainterPath::LineToElement,
266     QPainterPath::MoveToElement, QPainterPath::LineToElement,
267     QPainterPath::MoveToElement, QPainterPath::LineToElement,
268     QPainterPath::MoveToElement, QPainterPath::LineToElement,
269     QPainterPath::MoveToElement, QPainterPath::LineToElement,
270     QPainterPath::MoveToElement, QPainterPath::LineToElement,
271     QPainterPath::MoveToElement, QPainterPath::LineToElement,
272     QPainterPath::MoveToElement, QPainterPath::LineToElement,
273     QPainterPath::MoveToElement, QPainterPath::LineToElement,
274     QPainterPath::MoveToElement, QPainterPath::LineToElement,
275     QPainterPath::MoveToElement, QPainterPath::LineToElement,
276     QPainterPath::MoveToElement, QPainterPath::LineToElement,
277     QPainterPath::MoveToElement, QPainterPath::LineToElement,
278     QPainterPath::MoveToElement, QPainterPath::LineToElement
279 };
280
281 static QPainterPath::ElementType qpaintengineex_rect4_types_32[] = {
282     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 1
283     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 2
284     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 3
285     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 4
286     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 5
287     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 6
288     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 7
289     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 8
290     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 9
291     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 10
292     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 11
293     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 12
294     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 13
295     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 14
296     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 15
297     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 16
298     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 17
299     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 18
300     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 19
301     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 20
302     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 21
303     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 22
304     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 23
305     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 24
306     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 25
307     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 26
308     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 27
309     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 28
310     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 29
311     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 30
312     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 31
313     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 32
314 };
315
316
317 static QPainterPath::ElementType qpaintengineex_roundedrect_types[] = {
318     QPainterPath::MoveToElement,
319     QPainterPath::LineToElement,
320     QPainterPath::CurveToElement,
321     QPainterPath::CurveToDataElement,
322     QPainterPath::CurveToDataElement,
323     QPainterPath::LineToElement,
324     QPainterPath::CurveToElement,
325     QPainterPath::CurveToDataElement,
326     QPainterPath::CurveToDataElement,
327     QPainterPath::LineToElement,
328     QPainterPath::CurveToElement,
329     QPainterPath::CurveToDataElement,
330     QPainterPath::CurveToDataElement,
331     QPainterPath::LineToElement,
332     QPainterPath::CurveToElement,
333     QPainterPath::CurveToDataElement,
334     QPainterPath::CurveToDataElement
335 };
336
337
338
339 static void qpaintengineex_moveTo(qreal x, qreal y, void *data) {
340     ((StrokeHandler *) data)->pts.add(x);
341     ((StrokeHandler *) data)->pts.add(y);
342     ((StrokeHandler *) data)->types.add(QPainterPath::MoveToElement);
343 }
344
345 static void qpaintengineex_lineTo(qreal x, qreal y, void *data) {
346     ((StrokeHandler *) data)->pts.add(x);
347     ((StrokeHandler *) data)->pts.add(y);
348     ((StrokeHandler *) data)->types.add(QPainterPath::LineToElement);
349 }
350
351 static void qpaintengineex_cubicTo(qreal c1x, qreal c1y, qreal c2x, qreal c2y, qreal ex, qreal ey, void *data) {
352     ((StrokeHandler *) data)->pts.add(c1x);
353     ((StrokeHandler *) data)->pts.add(c1y);
354     ((StrokeHandler *) data)->types.add(QPainterPath::CurveToElement);
355
356     ((StrokeHandler *) data)->pts.add(c2x);
357     ((StrokeHandler *) data)->pts.add(c2y);
358     ((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement);
359
360     ((StrokeHandler *) data)->pts.add(ex);
361     ((StrokeHandler *) data)->pts.add(ey);
362     ((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement);
363 }
364
365 QPaintEngineEx::QPaintEngineEx()
366     : QPaintEngine(*new QPaintEngineExPrivate, AllFeatures)
367 {
368     extended = true;
369 }
370
371 QPaintEngineEx::QPaintEngineEx(QPaintEngineExPrivate &data)
372     : QPaintEngine(data, AllFeatures)
373 {
374     extended = true;
375 }
376
377 QPainterState *QPaintEngineEx::createState(QPainterState *orig) const
378 {
379     if (!orig)
380         return new QPainterState;
381     return new QPainterState(orig);
382 }
383
384 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
385
386 void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
387 {
388 #ifdef QT_DEBUG_DRAW
389     qDebug() << "QPaintEngineEx::stroke()" << pen;
390 #endif
391
392     Q_D(QPaintEngineEx);
393
394     if (path.isEmpty())
395         return;
396
397     if (!d->strokeHandler) {
398         d->strokeHandler = new StrokeHandler(path.elementCount()+4);
399         d->stroker.setMoveToHook(qpaintengineex_moveTo);
400         d->stroker.setLineToHook(qpaintengineex_lineTo);
401         d->stroker.setCubicToHook(qpaintengineex_cubicTo);
402     }
403
404     if (!qpen_fast_equals(pen, d->strokerPen)) {
405         d->strokerPen = pen;
406         d->stroker.setJoinStyle(pen.joinStyle());
407         d->stroker.setCapStyle(pen.capStyle());
408         d->stroker.setMiterLimit(pen.miterLimit());
409         qreal penWidth = pen.widthF();
410         if (penWidth == 0)
411             d->stroker.setStrokeWidth(1);
412         else
413             d->stroker.setStrokeWidth(penWidth);
414
415         Qt::PenStyle style = pen.style();
416         if (style == Qt::SolidLine) {
417             d->activeStroker = &d->stroker;
418         } else if (style == Qt::NoPen) {
419             d->activeStroker = 0;
420         } else {
421             d->dasher.setDashPattern(pen.dashPattern());
422             d->dasher.setDashOffset(pen.dashOffset());
423             d->activeStroker = &d->dasher;
424         }
425     }
426
427     if (!d->activeStroker) {
428         return;
429     }
430
431     if (pen.style() > Qt::SolidLine) {
432         if (pen.isCosmetic()) {
433             d->activeStroker->setClipRect(d->exDeviceRect);
434         } else {
435             QRectF clipRect = state()->matrix.inverted().mapRect(QRectF(d->exDeviceRect));
436             d->activeStroker->setClipRect(clipRect);
437         }
438     }
439
440     const QPainterPath::ElementType *types = path.elements();
441     const qreal *points = path.points();
442     int pointCount = path.elementCount();
443
444     const qreal *lastPoint = points + (pointCount<<1);
445
446     d->strokeHandler->types.reset();
447     d->strokeHandler->pts.reset();
448
449     // Some engines might decide to optimize for the non-shape hint later on...
450     uint flags = QVectorPath::WindingFill;
451
452     if (path.elementCount() > 2)
453         flags |= QVectorPath::NonConvexShapeMask;
454
455     if (d->stroker.capStyle() == Qt::RoundCap || d->stroker.joinStyle() == Qt::RoundJoin)
456         flags |= QVectorPath::CurvedShapeMask;
457
458     // ### Perspective Xforms are currently not supported...
459     if (!pen.isCosmetic()) {
460         // We include cosmetic pens in this case to avoid having to
461         // change the current transform. Normal transformed,
462         // non-cosmetic pens will be transformed as part of fill
463         // later, so they are also covered here..
464         d->activeStroker->setCurveThresholdFromTransform(state()->matrix);
465         d->activeStroker->begin(d->strokeHandler);
466         if (types) {
467             while (points < lastPoint) {
468                 switch (*types) {
469                 case QPainterPath::MoveToElement:
470                     d->activeStroker->moveTo(points[0], points[1]);
471                     points += 2;
472                     ++types;
473                     break;
474                 case QPainterPath::LineToElement:
475                     d->activeStroker->lineTo(points[0], points[1]);
476                     points += 2;
477                     ++types;
478                     break;
479                 case QPainterPath::CurveToElement:
480                     d->activeStroker->cubicTo(points[0], points[1],
481                                               points[2], points[3],
482                                               points[4], points[5]);
483                     points += 6;
484                     types += 3;
485                     flags |= QVectorPath::CurvedShapeMask;
486                     break;
487                 default:
488                     break;
489                 }
490             }
491             if (path.hasImplicitClose())
492                 d->activeStroker->lineTo(path.points()[0], path.points()[1]);
493
494         } else {
495             d->activeStroker->moveTo(points[0], points[1]);
496             points += 2;
497             while (points < lastPoint) {
498                 d->activeStroker->lineTo(points[0], points[1]);
499                 points += 2;
500             }
501             if (path.hasImplicitClose())
502                 d->activeStroker->lineTo(path.points()[0], path.points()[1]);
503         }
504         d->activeStroker->end();
505
506         if (!d->strokeHandler->types.size()) // an empty path...
507             return;
508
509         QVectorPath strokePath(d->strokeHandler->pts.data(),
510                                d->strokeHandler->types.size(),
511                                d->strokeHandler->types.data(),
512                                flags);
513         fill(strokePath, pen.brush());
514     } else {
515         // For cosmetic pens we need a bit of trickery... We to process xform the input points
516         if (state()->matrix.type() >= QTransform::TxProject) {
517             QPainterPath painterPath = state()->matrix.map(path.convertToPainterPath());
518             d->activeStroker->strokePath(painterPath, d->strokeHandler, QTransform());
519         } else {
520             d->activeStroker->setCurveThresholdFromTransform(QTransform());
521             d->activeStroker->begin(d->strokeHandler);
522             if (types) {
523                 while (points < lastPoint) {
524                     switch (*types) {
525                     case QPainterPath::MoveToElement: {
526                         QPointF pt = (*(QPointF *) points) * state()->matrix;
527                         d->activeStroker->moveTo(pt.x(), pt.y());
528                         points += 2;
529                         ++types;
530                         break;
531                     }
532                     case QPainterPath::LineToElement: {
533                         QPointF pt = (*(QPointF *) points) * state()->matrix;
534                         d->activeStroker->lineTo(pt.x(), pt.y());
535                         points += 2;
536                         ++types;
537                         break;
538                     }
539                     case QPainterPath::CurveToElement: {
540                         QPointF c1 = ((QPointF *) points)[0] * state()->matrix;
541                         QPointF c2 = ((QPointF *) points)[1] * state()->matrix;
542                         QPointF e =  ((QPointF *) points)[2] * state()->matrix;
543                         d->activeStroker->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
544                         points += 6;
545                         types += 3;
546                         flags |= QVectorPath::CurvedShapeMask;
547                         break;
548                     }
549                     default:
550                         break;
551                     }
552                 }
553                 if (path.hasImplicitClose()) {
554                     QPointF pt = * ((QPointF *) path.points()) * state()->matrix;
555                     d->activeStroker->lineTo(pt.x(), pt.y());
556                 }
557
558             } else {
559                 QPointF p = ((QPointF *)points)[0] * state()->matrix;
560                 d->activeStroker->moveTo(p.x(), p.y());
561                 points += 2;
562                 while (points < lastPoint) {
563                     QPointF p = ((QPointF *)points)[0] * state()->matrix;
564                     d->activeStroker->lineTo(p.x(), p.y());
565                     points += 2;
566                 }
567                 if (path.hasImplicitClose())
568                     d->activeStroker->lineTo(p.x(), p.y());
569             }
570             d->activeStroker->end();
571         }
572
573         QVectorPath strokePath(d->strokeHandler->pts.data(),
574                                d->strokeHandler->types.size(),
575                                d->strokeHandler->types.data(),
576                                flags);
577
578         QTransform xform = state()->matrix;
579         state()->matrix = QTransform();
580         transformChanged();
581
582         QBrush brush = pen.brush();
583         if (qbrush_style(brush) != Qt::SolidPattern)
584             brush.setTransform(brush.transform() * xform);
585
586         fill(strokePath, brush);
587
588         state()->matrix = xform;
589         transformChanged();
590     }
591 }
592
593 void QPaintEngineEx::draw(const QVectorPath &path)
594 {
595     const QBrush &brush = state()->brush;
596     if (qbrush_style(brush) != Qt::NoBrush)
597         fill(path, brush);
598
599     const QPen &pen = state()->pen;
600     if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush)
601         stroke(path, pen);
602 }
603
604
605 void QPaintEngineEx::clip(const QRect &r, Qt::ClipOperation op)
606 {
607     qreal right = r.x() + r.width();
608     qreal bottom = r.y() + r.height();
609     qreal pts[] = { qreal(r.x()), qreal(r.y()),
610                     right, qreal(r.y()),
611                     right, bottom,
612                     qreal(r.x()), bottom,
613                     qreal(r.x()), qreal(r.y()) };
614     QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
615     clip(vp, op);
616 }
617
618 void QPaintEngineEx::clip(const QRegion &region, Qt::ClipOperation op)
619 {
620     if (region.rectCount() == 1)
621         clip(region.boundingRect(), op);
622
623     QVector<QRect> rects = region.rects();
624     if (rects.size() <= 32) {
625         qreal pts[2*32*4];
626         int pos = 0;
627         for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) {
628             qreal x1 = i->x();
629             qreal y1 = i->y();
630             qreal x2 = i->x() + i->width();
631             qreal y2 = i->y() + i->height();
632
633             pts[pos++] = x1;
634             pts[pos++] = y1;
635
636             pts[pos++] = x2;
637             pts[pos++] = y1;
638
639             pts[pos++] = x2;
640             pts[pos++] = y2;
641
642             pts[pos++] = x1;
643             pts[pos++] = y2;
644         }
645         QVectorPath vp(pts, rects.size() * 4, qpaintengineex_rect4_types_32);
646         clip(vp, op);
647     } else {
648         QVarLengthArray<qreal> pts(rects.size() * 2 * 4);
649         QVarLengthArray<QPainterPath::ElementType> types(rects.size() * 4);
650         int ppos = 0;
651         int tpos = 0;
652
653         for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) {
654             qreal x1 = i->x();
655             qreal y1 = i->y();
656             qreal x2 = i->x() + i->width();
657             qreal y2 = i->y() + i->height();
658
659             pts[ppos++] = x1;
660             pts[ppos++] = y1;
661
662             pts[ppos++] = x2;
663             pts[ppos++] = y1;
664
665             pts[ppos++] = x2;
666             pts[ppos++] = y2;
667
668             pts[ppos++] = x1;
669             pts[ppos++] = y2;
670
671             types[tpos++] = QPainterPath::MoveToElement;
672             types[tpos++] = QPainterPath::LineToElement;
673             types[tpos++] = QPainterPath::LineToElement;
674             types[tpos++] = QPainterPath::LineToElement;
675         }
676
677         QVectorPath vp(pts.data(), rects.size() * 4, types.data());
678         clip(vp, op);
679     }
680
681 }
682
683 void QPaintEngineEx::clip(const QPainterPath &path, Qt::ClipOperation op)
684 {
685     if (path.isEmpty()) {
686         QVectorPath vp(0, 0);
687         clip(vp, op);
688     } else {
689         clip(qtVectorPathForPath(path), op);
690     }
691 }
692
693 void QPaintEngineEx::fillRect(const QRectF &r, const QBrush &brush)
694 {
695     qreal pts[] = { r.x(), r.y(), r.x() + r.width(), r.y(),
696                     r.x() + r.width(), r.y() + r.height(), r.x(), r.y() + r.height() };
697     QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
698     fill(vp, brush);
699 }
700
701 void QPaintEngineEx::fillRect(const QRectF &r, const QColor &color)
702 {
703     fillRect(r, QBrush(color));
704 }
705
706 void QPaintEngineEx::drawRects(const QRect *rects, int rectCount)
707 {
708     for (int i=0; i<rectCount; ++i) {
709         const QRect &r = rects[i];
710         // ### Is there a one off here?
711         qreal right = r.x() + r.width();
712         qreal bottom = r.y() + r.height();
713         qreal pts[] = { qreal(r.x()), qreal(r.y()),
714                         right, qreal(r.y()),
715                         right, bottom,
716                         qreal(r.x()), bottom,
717                         qreal(r.x()), qreal(r.y()) };
718         QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
719         draw(vp);
720     }
721 }
722
723 void QPaintEngineEx::drawRects(const QRectF *rects, int rectCount)
724 {
725     for (int i=0; i<rectCount; ++i) {
726         const QRectF &r = rects[i];
727         qreal right = r.x() + r.width();
728         qreal bottom = r.y() + r.height();
729         qreal pts[] = { r.x(), r.y(),
730                         right, r.y(),
731                         right, bottom,
732                         r.x(), bottom,
733                         r.x(), r.y() };
734         QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
735         draw(vp);
736     }
737 }
738
739
740 void QPaintEngineEx::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
741                                      Qt::SizeMode mode)
742 {
743     qreal x1 = rect.left();
744     qreal x2 = rect.right();
745     qreal y1 = rect.top();
746     qreal y2 = rect.bottom();
747
748     if (mode == Qt::RelativeSize) {
749         xRadius = xRadius * rect.width() / 200.;
750         yRadius = yRadius * rect.height() / 200.;
751     }
752
753     xRadius = qMin(xRadius, rect.width() / 2);
754     yRadius = qMin(yRadius, rect.height() / 2);
755
756     qreal pts[] = {
757         x1 + xRadius, y1,                   // MoveTo
758         x2 - xRadius, y1,                   // LineTo
759         x2 - (1 - KAPPA) * xRadius, y1,     // CurveTo
760         x2, y1 + (1 - KAPPA) * yRadius,
761         x2, y1 + yRadius,
762         x2, y2 - yRadius,                   // LineTo
763         x2, y2 - (1 - KAPPA) * yRadius,     // CurveTo
764         x2 - (1 - KAPPA) * xRadius, y2,
765         x2 - xRadius, y2,
766         x1 + xRadius, y2,                   // LineTo
767         x1 + (1 - KAPPA) * xRadius, y2,           // CurveTo
768         x1, y2 - (1 - KAPPA) * yRadius,
769         x1, y2 - yRadius,
770         x1, y1 + yRadius,                   // LineTo
771         x1, y1 + (1 - KAPPA) * yRadius,           // CurveTo
772         x1 + (1 - KAPPA) * xRadius, y1,
773         x1 + xRadius, y1
774     };
775
776     QVectorPath path(pts, 17, qpaintengineex_roundedrect_types, QVectorPath::RoundedRectHint);
777     draw(path);
778 }
779
780
781
782 void QPaintEngineEx::drawLines(const QLine *lines, int lineCount)
783 {
784     int elementCount = lineCount << 1;
785     while (elementCount > 0) {
786         int count = qMin(elementCount, 32);
787
788         qreal pts[64];
789         int count2 = count<<1;
790 #ifdef Q_WS_MAC
791         for (int i=0; i<count2; i+=2) {
792             pts[i] = ((int *) lines)[i+1];
793             pts[i+1] = ((int *) lines)[i];
794         }
795 #else
796         for (int i=0; i<count2; ++i)
797             pts[i] = ((int *) lines)[i];
798 #endif
799
800         QVectorPath path(pts, count, qpaintengineex_line_types_16, QVectorPath::LinesHint);
801         stroke(path, state()->pen);
802
803         elementCount -= 32;
804         lines += 16;
805     }
806 }
807
808 void QPaintEngineEx::drawLines(const QLineF *lines, int lineCount)
809 {
810     int elementCount = lineCount << 1;
811     while (elementCount > 0) {
812         int count = qMin(elementCount, 32);
813
814         QVectorPath path((qreal *) lines, count, qpaintengineex_line_types_16,
815                          QVectorPath::LinesHint);
816         stroke(path, state()->pen);
817
818         elementCount -= 32;
819         lines += 16;
820     }
821 }
822
823 void QPaintEngineEx::drawEllipse(const QRectF &r)
824 {
825     qreal pts[26]; // QPointF[13] without constructors...
826     union {
827         qreal *ptr;
828         QPointF *points;
829     } x;
830     x.ptr = pts;
831
832     int point_count = 0;
833     x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count);
834     QVectorPath vp((qreal *) pts, point_count + 1, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
835     draw(vp);
836 }
837
838 void QPaintEngineEx::drawEllipse(const QRect &r)
839 {
840     drawEllipse(QRectF(r));
841 }
842
843 void QPaintEngineEx::drawPath(const QPainterPath &path)
844 {
845     if (!path.isEmpty())
846         draw(qtVectorPathForPath(path));
847 }
848
849
850 void QPaintEngineEx::drawPoints(const QPointF *points, int pointCount)
851 {
852     QPen pen = state()->pen;
853     if (pen.capStyle() == Qt::FlatCap)
854         pen.setCapStyle(Qt::SquareCap);
855
856     if (pen.brush().isOpaque()) {
857         while (pointCount > 0) {
858             int count = qMin(pointCount, 16);
859             qreal pts[64];
860             int oset = -1;
861             for (int i=0; i<count; ++i) {
862                 pts[++oset] = points[i].x();
863                 pts[++oset] = points[i].y();
864                 pts[++oset] = points[i].x() + 1/63.;
865                 pts[++oset] = points[i].y();
866             }
867             QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
868             stroke(path, pen);
869             pointCount -= 16;
870             points += 16;
871         }
872     } else {
873         for (int i=0; i<pointCount; ++i) {
874             qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + 1/63., points[i].y() };
875             QVectorPath path(pts, 2, 0);
876             stroke(path, pen);
877         }
878     }
879 }
880
881 void QPaintEngineEx::drawPoints(const QPoint *points, int pointCount)
882 {
883     QPen pen = state()->pen;
884     if (pen.capStyle() == Qt::FlatCap)
885         pen.setCapStyle(Qt::SquareCap);
886
887     if (pen.brush().isOpaque()) {
888         while (pointCount > 0) {
889             int count = qMin(pointCount, 16);
890             qreal pts[64];
891             int oset = -1;
892             for (int i=0; i<count; ++i) {
893                 pts[++oset] = points[i].x();
894                 pts[++oset] = points[i].y();
895                 pts[++oset] = points[i].x() + 1/63.;
896                 pts[++oset] = points[i].y();
897             }
898             QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
899             stroke(path, pen);
900             pointCount -= 16;
901             points += 16;
902         }
903     } else {
904         for (int i=0; i<pointCount; ++i) {
905             qreal pts[] = { qreal(points[i].x()), qreal(points[i].y()),
906                             qreal(points[i].x() +1/63.), qreal(points[i].y()) };
907             QVectorPath path(pts, 2, 0);
908             stroke(path, pen);
909         }
910     }
911 }
912
913
914 void QPaintEngineEx::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
915 {
916     QVectorPath path((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
917
918     if (mode == PolylineMode)
919         stroke(path, state()->pen);
920     else
921         draw(path);
922 }
923
924 void QPaintEngineEx::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
925 {
926     int count = pointCount<<1;
927     QVarLengthArray<qreal> pts(count);
928
929 #ifdef Q_WS_MAC
930     for (int i=0; i<count; i+=2) {
931         pts[i] = ((int *) points)[i+1];
932         pts[i+1] = ((int *) points)[i];
933     }
934 #else
935     for (int i=0; i<count; ++i)
936         pts[i] = ((int *) points)[i];
937 #endif
938
939     QVectorPath path(pts.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
940
941     if (mode == PolylineMode)
942         stroke(path, state()->pen);
943     else
944         draw(path);
945
946 }
947
948 void QPaintEngineEx::drawPixmap(const QPointF &pos, const QPixmap &pm)
949 {
950     drawPixmap(QRectF(pos, pm.size()), pm, pm.rect());
951 }
952
953 void QPaintEngineEx::drawImage(const QPointF &pos, const QImage &image)
954 {
955     drawImage(QRectF(pos, image.size()), image, image.rect());
956 }
957
958 void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
959 {
960     QBrush brush(state()->pen.color(), pixmap);
961     QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
962     brush.setTransform(xform);
963
964     qreal pts[] = { r.x(), r.y(),
965                     r.x() + r.width(), r.y(),
966                     r.x() + r.width(), r.y() + r.height(),
967                     r.x(), r.y() + r.height() };
968
969     QVectorPath path(pts, 4, 0, QVectorPath::RectangleHint);
970     fill(path, brush);
971 }
972
973 void QPaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount,
974                                          const QPixmap &pixmap, QPainter::PixmapFragmentHints /*hints*/)
975 {
976     if (pixmap.isNull())
977         return;
978
979     qreal oldOpacity = state()->opacity;
980     QTransform oldTransform = state()->matrix;
981
982     for (int i = 0; i < fragmentCount; ++i) {
983         QTransform transform = oldTransform;
984         transform.translate(fragments[i].x, fragments[i].y);
985         transform.rotate(fragments[i].rotation);
986         state()->opacity = oldOpacity * fragments[i].opacity;
987         state()->matrix = transform;
988         opacityChanged();
989         transformChanged();
990
991         qreal w = fragments[i].scaleX * fragments[i].width;
992         qreal h = fragments[i].scaleY * fragments[i].height;
993         QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
994                           fragments[i].width, fragments[i].height);
995         drawPixmap(QRectF(-0.5 * w, -0.5 * h, w, h), pixmap, sourceRect);
996     }
997
998     state()->opacity = oldOpacity;
999     state()->matrix = oldTransform;
1000     opacityChanged();
1001     transformChanged();
1002 }
1003
1004 void QPaintEngineEx::setState(QPainterState *s)
1005 {
1006     QPaintEngine::state = s;
1007 }
1008
1009
1010 void QPaintEngineEx::updateState(const QPaintEngineState &)
1011 {
1012     // do nothing...
1013 }
1014
1015 Q_GUI_EXPORT QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path)
1016 {
1017     const qreal *points = path.points();
1018     const QPainterPath::ElementType *types = path.elements();
1019
1020     QPainterPath p;
1021     if (types) {
1022         int id = 0;
1023         for (int i=0; i<path.elementCount(); ++i) {
1024             switch(types[i]) {
1025             case QPainterPath::MoveToElement:
1026                 p.moveTo(QPointF(points[id], points[id+1]));
1027                 id+=2;
1028                 break;
1029             case QPainterPath::LineToElement:
1030                 p.lineTo(QPointF(points[id], points[id+1]));
1031                 id+=2;
1032                 break;
1033             case QPainterPath::CurveToElement: {
1034                 QPointF p1(points[id], points[id+1]);
1035                 QPointF p2(points[id+2], points[id+3]);
1036                 QPointF p3(points[id+4], points[id+5]);
1037                 p.cubicTo(p1, p2, p3);
1038                 id+=6;
1039                 break;
1040             }
1041             case QPainterPath::CurveToDataElement:
1042                 ;
1043                 break;
1044             }
1045         }
1046     } else {
1047         p.moveTo(QPointF(points[0], points[1]));
1048         int id = 2;
1049         for (int i=1; i<path.elementCount(); ++i) {
1050             p.lineTo(QPointF(points[id], points[id+1]));
1051             id+=2;
1052         }
1053     }
1054     if (path.hints() & QVectorPath::WindingFill)
1055         p.setFillRule(Qt::WindingFill);
1056
1057     return p;
1058 }
1059
1060
1061 QT_END_NAMESPACE