Replace 'i < len-1 && func(i+1)' by 'i+1 < len && func(i+1)'
[profile/ivi/qtbase.git] / src / gui / painting / qcosmeticstroker.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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qcosmeticstroker_p.h"
43 #include "private/qpainterpath_p.h"
44 #include <qdebug.h>
45 #include <math.h>
46
47 QT_BEGIN_NAMESPACE
48
49 #if 0
50 inline QString capString(int caps)
51 {
52     QString str;
53     if (caps & QCosmeticStroker::CapBegin) {
54         str += "CapBegin ";
55     }
56     if (caps & QCosmeticStroker::CapEnd) {
57         str += "CapEnd ";
58     }
59     return str;
60 }
61 #endif
62
63 #define toF26Dot6(x) ((int)((x)*64.))
64
65 static inline uint sourceOver(uint d, uint color)
66 {
67     return color + BYTE_MUL(d, qAlpha(~color));
68 }
69
70 inline static int F16Dot16FixedDiv(int x, int y)
71 {
72     if (qAbs(x) > 0x7fff)
73         return (((qlonglong)x) << 16) / y;
74     return (x << 16) / y;
75 }
76
77 typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage);
78
79 namespace {
80
81 struct Dasher {
82     QCosmeticStroker *stroker;
83     int *pattern;
84     int offset;
85     int dashIndex;
86     int dashOn;
87
88     Dasher(QCosmeticStroker *s, bool reverse, int start, int stop)
89         : stroker(s)
90     {
91         int delta = stop - start;
92         if (reverse) {
93             pattern = stroker->reversePattern;
94             offset = stroker->patternLength - stroker->patternOffset - delta - ((start & 63) - 32);
95             dashOn = 0;
96         } else {
97             pattern = stroker->pattern;
98             offset = stroker->patternOffset - ((start & 63) - 32);
99             dashOn = 1;
100         }
101         offset %= stroker->patternLength;
102         if (offset < 0)
103             offset += stroker->patternLength;
104
105         dashIndex = 0;
106         while (offset>= pattern[dashIndex])
107             ++dashIndex;
108
109 //        qDebug() << "   dasher" << offset/64. << reverse << dashIndex;
110         stroker->patternOffset += delta;
111         stroker->patternOffset %= stroker->patternLength;
112     }
113
114     bool on() const {
115         return (dashIndex + dashOn) & 1;
116     }
117     void adjust() {
118         offset += 64;
119         if (offset >= pattern[dashIndex]) {
120             ++dashIndex;
121             dashIndex %= stroker->patternSize;
122         }
123         offset %= stroker->patternLength;
124 //        qDebug() << "dasher.adjust" << offset/64. << dashIndex;
125     }
126 };
127
128 struct NoDasher {
129     NoDasher(QCosmeticStroker *, bool, int, int) {}
130     bool on() const { return true; }
131     void adjust(int = 0) {}
132 };
133
134 };
135
136 template<DrawPixel drawPixel, class Dasher>
137 static void drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
138 template<DrawPixel drawPixel, class Dasher>
139 static void drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
140
141 inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage)
142 {
143     const QRect &cl = stroker->clip;
144     if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
145         return;
146
147     int lastx = stroker->spans[stroker->current_span-1].x + stroker->spans[stroker->current_span-1].len ;
148     int lasty = stroker->spans[stroker->current_span-1].y;
149
150     if (stroker->current_span == QCosmeticStroker::NSPANS || y < lasty || (y == lasty && x < lastx)) {
151         stroker->blend(stroker->current_span, stroker->spans, &stroker->state->penData);
152         stroker->current_span = 0;
153     }
154
155     stroker->spans[stroker->current_span].x = ushort(x);
156     stroker->spans[stroker->current_span].len = 1;
157     stroker->spans[stroker->current_span].y = y;
158     stroker->spans[stroker->current_span].coverage = coverage*stroker->opacity >> 8;
159     ++stroker->current_span;
160 }
161
162 inline void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage)
163 {
164     const QRect &cl = stroker->clip;
165     if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
166         return;
167
168     int offset = x + stroker->ppl*y;
169     uint c = BYTE_MUL(stroker->color, coverage);
170     stroker->pixels[offset] = sourceOver(stroker->pixels[offset], c);
171 }
172
173 inline void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int)
174 {
175     const QRect &cl = stroker->clip;
176     if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
177         return;
178
179     int offset = x + stroker->ppl*y;
180     stroker->pixels[offset] = sourceOver(stroker->pixels[offset], stroker->color);
181 }
182
183 enum StrokeSelection {
184     Aliased = 0,
185     AntiAliased = 1,
186     Solid = 0,
187     Dashed = 2,
188     RegularDraw = 0,
189     FastDraw = 4
190 };
191
192 static StrokeLine strokeLine(int strokeSelection)
193 {
194     StrokeLine stroke;
195
196     switch (strokeSelection) {
197     case Aliased|Solid|RegularDraw:
198         stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, NoDasher>;
199         break;
200     case Aliased|Solid|FastDraw:
201         stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, NoDasher>;
202         break;
203     case Aliased|Dashed|RegularDraw:
204         stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, Dasher>;
205         break;
206     case Aliased|Dashed|FastDraw:
207         stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, Dasher>;
208         break;
209     case AntiAliased|Solid|RegularDraw:
210         stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, NoDasher>;
211         break;
212     case AntiAliased|Solid|FastDraw:
213         stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, NoDasher>;
214         break;
215     case AntiAliased|Dashed|RegularDraw:
216         stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, Dasher>;
217         break;
218     case AntiAliased|Dashed|FastDraw:
219         stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, Dasher>;
220         break;
221     default:
222         Q_ASSERT(false);
223         stroke = 0;
224     }
225     return stroke;
226 }
227
228 void QCosmeticStroker::setup()
229 {
230     blend = state->penData.blend;
231     if (state->clip && state->clip->enabled && state->clip->hasRectClip && !state->clip->clipRect.isEmpty()) {
232         clip &= state->clip->clipRect;
233         blend = state->penData.unclipped_blend;
234     }
235
236     int strokeSelection = 0;
237     if (blend == state->penData.unclipped_blend
238         && state->penData.type == QSpanData::Solid
239         && (state->penData.rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
240             || state->penData.rasterBuffer->format == QImage::Format_RGB32)
241         && state->compositionMode() == QPainter::CompositionMode_SourceOver)
242         strokeSelection |= FastDraw;
243
244     if (state->renderHints & QPainter::Antialiasing)
245         strokeSelection |= AntiAliased;
246
247     const QVector<qreal> &penPattern = state->lastPen.dashPattern();
248     if (penPattern.isEmpty()) {
249         Q_ASSERT(!pattern && !reversePattern);
250         pattern = 0;
251         reversePattern = 0;
252         patternLength = 0;
253         patternSize = 0;
254     } else {
255         pattern = (int *)malloc(penPattern.size()*sizeof(int));
256         reversePattern = (int *)malloc(penPattern.size()*sizeof(int));
257         patternSize = penPattern.size();
258
259         patternLength = 0;
260         for (int i = 0; i < patternSize; ++i) {
261             patternLength += (int) qMax(1. , penPattern.at(i)*64.);
262             pattern[i] = patternLength;
263         }
264         patternLength = 0;
265         for (int i = 0; i < patternSize; ++i) {
266             patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.);
267             reversePattern[i] = patternLength;
268         }
269         strokeSelection |= Dashed;
270 //        qDebug() << "setup: size=" << patternSize << "length=" << patternLength/64.;
271     }
272
273     stroke = strokeLine(strokeSelection);
274
275     qreal width = state->lastPen.widthF();
276     if (width == 0)
277         opacity = 256;
278     else if (state->lastPen.isCosmetic())
279         opacity = (int) 256*width;
280     else
281         opacity = (int) 256*width*state->txscale;
282     opacity = qBound(0, opacity, 256);
283
284     drawCaps = state->lastPen.capStyle() != Qt::FlatCap;
285
286     if (strokeSelection & FastDraw) {
287         color = INTERPOLATE_PIXEL_256(state->penData.solid.color, opacity, 0, 0);
288         QRasterBuffer *buffer = state->penData.rasterBuffer;
289         pixels = (uint *)buffer->buffer();
290         ppl = buffer->bytesPerLine()>>2;
291     }
292
293     // setup FP clip bounds
294     xmin = clip.left() - 1;
295     xmax = clip.right() + 2;
296     ymin = clip.top() - 1;
297     ymax = clip.bottom() + 2;
298
299     lastPixel.x = -1;
300 }
301
302 // returns true if the whole line gets clipped away
303 bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2)
304 {
305     // basic/rough clipping is done in floating point coordinates to avoid
306     // integer overflow problems.
307     if (x1 < xmin) {
308         if (x2 <= xmin)
309             goto clipped;
310         y1 += (y2 - y1)/(x2 - x1) * (xmin - x1);
311         x1 = xmin;
312     } else if (x1 > xmax) {
313         if (x2 >= xmax)
314             goto clipped;
315         y1 += (y2 - y1)/(x2 - x1) * (xmax - x1);
316         x1 = xmax;
317     }
318     if (x2 < xmin) {
319         lastPixel.x = -1;
320         y2 += (y2 - y1)/(x2 - x1) * (xmin - x2);
321         x2 = xmin;
322     } else if (x2 > xmax) {
323         lastPixel.x = -1;
324         y2 += (y2 - y1)/(x2 - x1) * (xmax - x2);
325         x2 = xmax;
326     }
327
328     if (y1 < ymin) {
329         if (y2 <= ymin)
330             goto clipped;
331         x1 += (x2 - x1)/(y2 - y1) * (ymin - y1);
332         y1 = ymin;
333     } else if (y1 > ymax) {
334         if (y2 >= ymax)
335             goto clipped;
336         x1 += (x2 - x1)/(y2 - y1) * (ymax - y1);
337         y1 = ymax;
338     }
339     if (y2 < ymin) {
340         lastPixel.x = -1;
341         x2 += (x2 - x1)/(y2 - y1) * (ymin - y2);
342         y2 = ymin;
343     } else if (y2 > ymax) {
344         lastPixel.x = -1;
345         x2 += (x2 - x1)/(y2 - y1) * (ymax - y2);
346         y2 = ymax;
347     }
348
349     return false;
350
351   clipped:
352     lastPixel.x = -1;
353     return true;
354 }
355
356
357 void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2)
358 {
359     QPointF start = p1 * state->matrix;
360     QPointF end = p2 * state->matrix;
361
362     patternOffset = state->lastPen.dashOffset()*64;
363     lastPixel.x = -1;
364
365     stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0);
366
367     blend(current_span, spans, &state->penData);
368     current_span = 0;
369 }
370
371 void QCosmeticStroker::drawPoints(const QPoint *points, int num)
372 {
373     const QPoint *end = points + num;
374     while (points < end) {
375         QPointF p = QPointF(*points) * state->matrix;
376         drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
377         ++points;
378     }
379
380     blend(current_span, spans, &state->penData);
381     current_span = 0;
382 }
383
384 void QCosmeticStroker::drawPoints(const QPointF *points, int num)
385 {
386     const QPointF *end = points + num;
387     while (points < end) {
388         QPointF p = (*points) * state->matrix;
389         drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
390         ++points;
391     }
392
393     blend(current_span, spans, &state->penData);
394     current_span = 0;
395 }
396
397 void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2)
398 {
399     // this is basically the same code as used in the aliased stroke method,
400     // but it only determines the direction and last point of a line
401     //
402     // This is being used to have proper dropout control for closed contours
403     // by calculating the direction and last pixel of the last segment in the contour.
404     // the info is then used to perform dropout control when drawing the first line segment
405     // of the contour
406     lastPixel.x = -1;
407     lastPixel.y = -1;
408
409     if (clipLine(rx1, ry1, rx2, ry2))
410         return;
411
412     const int half = 32;
413     int x1 = toF26Dot6(rx1) + half;
414     int y1 = toF26Dot6(ry1) + half;
415     int x2 = toF26Dot6(rx2) + half;
416     int y2 = toF26Dot6(ry2) + half;
417
418     int dx = qAbs(x2 - x1);
419     int dy = qAbs(y2 - y1);
420
421     if (dx < dy) {
422         // vertical
423         bool swapped = false;
424         if (y1 > y2) {
425             swapped = true;
426             qSwap(y1, y2);
427             qSwap(x1, x2);
428         }
429         int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
430         int x = x1 << 10;
431
432         int y = y1 >> 6;
433         int ys = y2 >> 6;
434
435         if (y != ys) {
436             x += ( ((((y << 6) + 32 - y1)))  * xinc ) >> 6;
437
438             if (swapped) {
439                 lastPixel.x = x >> 16;
440                 lastPixel.y = y;
441                 lastDir = QCosmeticStroker::BottomToTop;
442             } else {
443                 lastPixel.x = (x + (ys - y - 1)*xinc) >> 16;
444                 lastPixel.y = ys - 1;
445                 lastDir = QCosmeticStroker::TopToBottom;
446             }
447             lastAxisAligned = qAbs(xinc) < (1 << 14);
448         }
449     } else {
450         // horizontal
451         if (!dx)
452             return;
453
454         bool swapped = false;
455         if (x1 > x2) {
456             swapped = true;
457             qSwap(x1, x2);
458             qSwap(y1, y2);
459         }
460         int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
461         int y = y1 << 10;
462
463         int x = x1 >> 6;
464         int xs = x2 >> 6;
465
466         if (x != xs) {
467             y += ( ((((x << 6) + 32 - x1)))  * yinc ) >> 6;
468
469             if (swapped) {
470                 lastPixel.x = x;
471                 lastPixel.y = y >> 16;
472                 lastDir = QCosmeticStroker::RightToLeft;
473             } else {
474                 lastPixel.x = xs - 1;
475                 lastPixel.y = (y + (xs - x - 1)*yinc) >> 16;
476                 lastDir = QCosmeticStroker::LeftToRight;
477             }
478             lastAxisAligned = qAbs(yinc) < (1 << 14);
479         }
480     }
481 //    qDebug() << "   moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir;
482 }
483
484 static inline const QPainterPath::ElementType *subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end,
485                                                  const qreal *points, bool *closed)
486 {
487     const QPainterPath::ElementType *start = t;
488     ++t;
489
490     // find out if the subpath is closed
491     while (t < end) {
492         if (*t == QPainterPath::MoveToElement)
493             break;
494         ++t;
495     }
496
497     int offset = t - start - 1;
498 //    qDebug() << "subpath" << offset << points[0] << points[1] << points[2*offset] << points[2*offset+1];
499     *closed = (points[0] == points[2*offset] && points[1] == points[2*offset + 1]);
500
501     return t;
502 }
503
504 void QCosmeticStroker::drawPath(const QVectorPath &path)
505 {
506 //    qDebug() << ">>>> drawpath" << path.convertToPainterPath()
507 //             << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose();
508     if (path.isEmpty())
509         return;
510
511     const qreal *points = path.points();
512     const QPainterPath::ElementType *type = path.elements();
513
514     if (type) {
515         const QPainterPath::ElementType *end = type + path.elementCount();
516
517         while (type < end) {
518             Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement);
519
520             QPointF p = QPointF(points[0], points[1]) * state->matrix;
521             QPointF movedTo = p;
522             patternOffset = state->lastPen.dashOffset()*64;
523             lastPixel.x = -1;
524
525             bool closed;
526             const QPainterPath::ElementType *e = subPath(type, end, points, &closed);
527             if (closed) {
528                 const qreal *p = points + 2*(e-type);
529                 QPointF p1 = QPointF(p[-4], p[-3]) * state->matrix;
530                 QPointF p2 = QPointF(p[-2], p[-1]) * state->matrix;
531                 calculateLastPoint(p1.x(), p1.y(), p2.x(), p2.y());
532             }
533             int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
534 //            qDebug() << "closed =" << closed << capString(caps);
535
536             points += 2;
537             ++type;
538
539             while (type < e) {
540                 QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
541                 switch (*type) {
542                 case QPainterPath::MoveToElement:
543                     Q_ASSERT(!"Logic error");
544                     break;
545
546                 case QPainterPath::LineToElement:
547                     if (!closed && drawCaps && type == e - 1)
548                         caps |= CapEnd;
549                     stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
550                     p = p2;
551                     points += 2;
552                     ++type;
553                     break;
554
555                 case QPainterPath::CurveToElement: {
556                     if (!closed && drawCaps && type == e - 3)
557                         caps |= CapEnd;
558                     QPointF p3 = QPointF(points[2], points[3]) * state->matrix;
559                     QPointF p4 = QPointF(points[4], points[5]) * state->matrix;
560                     renderCubic(p, p2, p3, p4, caps);
561                     p = p4;
562                     type += 3;
563                     points += 6;
564                     break;
565                 }
566                 case QPainterPath::CurveToDataElement:
567                     Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
568                     break;
569                 }
570                 caps = NoCaps;
571             }
572         }
573     } else { // !type, simple polygon
574         QPointF p = QPointF(points[0], points[1]) * state->matrix;
575         QPointF movedTo = p;
576         patternOffset = state->lastPen.dashOffset()*64;
577         lastPixel.x = -1;
578
579         const qreal *end = points + 2*path.elementCount();
580         // handle closed path case
581         bool closed = path.hasImplicitClose() || (points[0] == end[-2] && points[1] == end[-1]);
582         int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
583         if (closed) {
584             QPointF p2 = QPointF(end[-2], end[-1]) * state->matrix;
585             calculateLastPoint(p2.x(), p2.y(), p.x(), p.y());
586         }
587
588         points += 2;
589         while (points < end) {
590             QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
591
592             if (!closed && drawCaps && points == end - 2)
593                 caps |= CapEnd;
594
595             stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
596
597             p = p2;
598             points += 2;
599             caps = NoCaps;
600         }
601         if (path.hasImplicitClose())
602             stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps);
603     }
604
605
606     blend(current_span, spans, &state->penData);
607     current_span = 0;
608 }
609
610 void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps)
611 {
612 //    qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps);
613     const int maxSubDivisions = 6;
614     PointF points[3*maxSubDivisions + 4];
615
616     points[3].x = p1.x();
617     points[3].y = p1.y();
618     points[2].x = p2.x();
619     points[2].y = p2.y();
620     points[1].x = p3.x();
621     points[1].y = p3.y();
622     points[0].x = p4.x();
623     points[0].y = p4.y();
624
625     PointF *p = points;
626     int level = maxSubDivisions;
627
628     renderCubicSubdivision(p, level, caps);
629 }
630
631 static void splitCubic(QCosmeticStroker::PointF *points)
632 {
633     const qreal half = .5;
634     qreal  a, b, c, d;
635
636     points[6].x = points[3].x;
637     c = points[1].x;
638     d = points[2].x;
639     points[1].x = a = ( points[0].x + c ) * half;
640     points[5].x = b = ( points[3].x + d ) * half;
641     c = ( c + d ) * half;
642     points[2].x = a = ( a + c ) * half;
643     points[4].x = b = ( b + c ) * half;
644     points[3].x = ( a + b ) * half;
645
646     points[6].y = points[3].y;
647     c = points[1].y;
648     d = points[2].y;
649     points[1].y = a = ( points[0].y + c ) * half;
650     points[5].y = b = ( points[3].y + d ) * half;
651     c = ( c + d ) * half;
652     points[2].y = a = ( a + c ) * half;
653     points[4].y = b = ( b + c ) * half;
654     points[3].y = ( a + b ) * half;
655 }
656
657 void QCosmeticStroker::renderCubicSubdivision(QCosmeticStroker::PointF *points, int level, int caps)
658 {
659     if (level) {
660         qreal dx = points[3].x - points[0].x;
661         qreal dy = points[3].y - points[0].y;
662         qreal len = ((qreal).25) * (qAbs(dx) + qAbs(dy));
663
664         if (qAbs(dx * (points[0].y - points[2].y) - dy * (points[0].x - points[2].x)) >= len ||
665             qAbs(dx * (points[0].y - points[1].y) - dy * (points[0].x - points[1].x)) >= len) {
666             splitCubic(points);
667
668             --level;
669             renderCubicSubdivision(points + 3, level, caps & CapBegin);
670             renderCubicSubdivision(points, level, caps & CapEnd);
671             return;
672         }
673     }
674
675     stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps);
676 }
677
678 static inline int swapCaps(int caps)
679 {
680     return ((caps & QCosmeticStroker::CapBegin) << 1) |
681            ((caps & QCosmeticStroker::CapEnd) >> 1);
682 }
683
684 // adjust line by half a pixel
685 static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc)
686 {
687     if (caps & QCosmeticStroker::CapBegin) {
688         x1 -= 32;
689         y -= yinc >> 1;
690     }
691     if (caps & QCosmeticStroker::CapEnd) {
692         x2 += 32;
693     }
694 }
695
696 /*
697   The hard part about this is dropout control and avoiding douple drawing of points when
698   the drawing shifts from horizontal to vertical or back.
699   */
700 template<DrawPixel drawPixel, class Dasher>
701 static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
702 {
703     if (stroker->clipLine(rx1, ry1, rx2, ry2))
704         return;
705
706     static const int half = 32;
707     int x1 = toF26Dot6(rx1) + half;
708     int y1 = toF26Dot6(ry1) + half;
709     int x2 = toF26Dot6(rx2) + half;
710     int y2 = toF26Dot6(ry2) + half;
711
712     int dx = qAbs(x2 - x1);
713     int dy = qAbs(y2 - y1);
714
715     QCosmeticStroker::Point last = stroker->lastPixel;
716
717 //    qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.;
718
719     if (dx < dy) {
720         // vertical
721         QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom;
722
723         bool swapped = false;
724         if (y1 > y2) {
725             swapped = true;
726             qSwap(y1, y2);
727             qSwap(x1, x2);
728             caps = swapCaps(caps);
729             dir = QCosmeticStroker::BottomToTop;
730         }
731         int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
732         int x = x1 << 10;
733
734         if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir)
735             caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
736
737         capAdjust(caps, y1, y2, x, xinc);
738
739         int y = y1 >> 6;
740         int ys = y2 >> 6;
741
742         if (y != ys) {
743             x += ( ((((y << 6) + 32 - y1)))  * xinc ) >> 6;
744
745             // calculate first and last pixel and perform dropout control
746             QCosmeticStroker::Point first;
747             first.x = x >> 16;
748             first.y = y;
749             last.x = (x + (ys - y - 1)*xinc) >> 16;
750             last.y = ys - 1;
751             if (swapped)
752                 qSwap(first, last);
753
754             bool axisAligned = qAbs(xinc) < (1 << 14);
755             if (stroker->lastPixel.x >= 0) {
756                 if (first.x == stroker->lastPixel.x &&
757                     first.y == stroker->lastPixel.y) {
758                     // remove duplicated pixel
759                     if (swapped) {
760                         --ys;
761                     } else {
762                         ++y;
763                         x += xinc;
764                     }
765                 } else if (stroker->lastDir != dir &&
766                            (((axisAligned && stroker->lastAxisAligned) &&
767                              stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
768                             (qAbs(stroker->lastPixel.x - first.x) > 1 ||
769                              qAbs(stroker->lastPixel.y - first.y) > 1))) {
770                     // have a missing pixel, insert it
771                     if (swapped) {
772                         ++ys;
773                     } else {
774                         --y;
775                         x -= xinc;
776                     }
777                 }
778             }
779             stroker->lastDir = dir;
780             stroker->lastAxisAligned = axisAligned;
781
782             Dasher dasher(stroker, swapped, y << 6, ys << 6);
783
784             do {
785                 if (dasher.on())
786                     drawPixel(stroker, x >> 16, y, 255);
787                 dasher.adjust();
788                 x += xinc;
789             } while (++y < ys);
790         }
791     } else {
792         // horizontal
793         if (!dx)
794             return;
795
796         QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight;
797
798         bool swapped = false;
799         if (x1 > x2) {
800             swapped = true;
801             qSwap(x1, x2);
802             qSwap(y1, y2);
803             caps = swapCaps(caps);
804             dir = QCosmeticStroker::RightToLeft;
805         }
806         int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
807         int y = y1 << 10;
808
809         if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir)
810             caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
811
812         capAdjust(caps, x1, x2, y, yinc);
813
814         int x = x1 >> 6;
815         int xs = x2 >> 6;
816
817         if (x != xs) {
818             y += ( ((((x << 6) + 32 - x1)))  * yinc ) >> 6;
819
820             // calculate first and last pixel to perform dropout control
821             QCosmeticStroker::Point first;
822             first.x = x;
823             first.y = y >> 16;
824             last.x = xs - 1;
825             last.y = (y + (xs - x - 1)*yinc) >> 16;
826             if (swapped)
827                 qSwap(first, last);
828
829             bool axisAligned = qAbs(yinc) < (1 << 14);
830             if (stroker->lastPixel.x >= 0) {
831                 if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) {
832                     // remove duplicated pixel
833                     if (swapped) {
834                         --xs;
835                     } else {
836                         ++x;
837                         y += yinc;
838                     }
839                 } else if (stroker->lastDir != dir &&
840                            (((axisAligned && stroker->lastAxisAligned) &&
841                              stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
842                             (qAbs(stroker->lastPixel.x - first.x) > 1 ||
843                              qAbs(stroker->lastPixel.y - first.y) > 1))) {
844                     // have a missing pixel, insert it
845                     if (swapped) {
846                         ++xs;
847                     } else {
848                         --x;
849                         y -= yinc;
850                     }
851                 }
852             }
853             stroker->lastDir = dir;
854             stroker->lastAxisAligned = axisAligned;
855
856             Dasher dasher(stroker, swapped, x << 6, xs << 6);
857
858             do {
859                 if (dasher.on())
860                     drawPixel(stroker, x, y >> 16, 255);
861                 dasher.adjust();
862                 y += yinc;
863             } while (++x < xs);
864         }
865     }
866     stroker->lastPixel = last;
867 }
868
869
870 template<DrawPixel drawPixel, class Dasher>
871 static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
872 {
873     if (stroker->clipLine(rx1, ry1, rx2, ry2))
874         return;
875
876     int x1 = toF26Dot6(rx1);
877     int y1 = toF26Dot6(ry1);
878     int x2 = toF26Dot6(rx2);
879     int y2 = toF26Dot6(ry2);
880
881     int dx = x2 - x1;
882     int dy = y2 - y1;
883
884     if (qAbs(dx) < qAbs(dy)) {
885         // vertical
886
887         int xinc = F16Dot16FixedDiv(dx, dy);
888
889         bool swapped = false;
890         if (y1 > y2) {
891             qSwap(y1, y2);
892             qSwap(x1, x2);
893             swapped = true;
894             caps = swapCaps(caps);
895         }
896
897         int x = (x1 - 32) << 10;
898         x -= ( ((y1 & 63) - 32)  * xinc ) >> 6;
899
900         capAdjust(caps, y1, y2, x, xinc);
901
902         Dasher dasher(stroker, swapped, y1, y2);
903
904         int y = y1 >> 6;
905         int ys = y2 >> 6;
906
907         int alphaStart, alphaEnd;
908         if (y == ys) {
909             alphaStart = y2 - y1;
910             Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
911             alphaEnd = 0;
912         } else {
913             alphaStart = 64 - (y1 & 63);
914             alphaEnd = (y2 & 63);
915         }
916 //        qDebug() << "vertical" << x1/64. << y1/64. << x2/64. << y2/64.;
917 //        qDebug() << "          x=" << x << "dx=" << dx << "xi=" << (x>>16) << "xsi=" << ((x+(ys-y)*dx)>>16) << "y=" << y << "ys=" << ys;
918
919         // draw first pixel
920         if (dasher.on()) {
921             uint alpha = (quint8)(x >> 8);
922             drawPixel(stroker, x>>16, y, (255-alpha) * alphaStart >> 6);
923             drawPixel(stroker, (x>>16) + 1, y, alpha * alphaStart >> 6);
924         }
925         dasher.adjust();
926         x += xinc;
927         ++y;
928         if (y < ys) {
929             do {
930                 if (dasher.on()) {
931                     uint alpha = (quint8)(x >> 8);
932                     drawPixel(stroker, x>>16, y, (255-alpha));
933                     drawPixel(stroker, (x>>16) + 1, y, alpha);
934                 }
935                 dasher.adjust();
936                 x += xinc;
937             } while (++y < ys);
938         }
939         // draw last pixel
940         if (alphaEnd && dasher.on()) {
941             uint alpha = (quint8)(x >> 8);
942             drawPixel(stroker, x>>16, y, (255-alpha) * alphaEnd >> 6);
943             drawPixel(stroker, (x>>16) + 1, y, alpha * alphaEnd >> 6);
944         }
945     } else {
946         // horizontal
947         if (!dx)
948             return;
949
950         int yinc = F16Dot16FixedDiv(dy, dx);
951
952         bool swapped = false;
953         if (x1 > x2) {
954             qSwap(x1, x2);
955             qSwap(y1, y2);
956             swapped = true;
957             caps = swapCaps(caps);
958         }
959
960         int y = (y1 - 32) << 10;
961         y -= ( ((x1 & 63) - 32)  * yinc ) >> 6;
962
963         capAdjust(caps, x1, x2, y, yinc);
964
965         Dasher dasher(stroker, swapped, x1, x2);
966
967         int x = x1 >> 6;
968         int xs = x2 >> 6;
969
970 //        qDebug() << "horizontal" << x1/64. << y1/64. << x2/64. << y2/64.;
971 //        qDebug() << "          y=" << y << "dy=" << dy << "x=" << x << "xs=" << xs << "yi=" << (y>>16) << "ysi=" << ((y+(xs-x)*dy)>>16);
972         int alphaStart, alphaEnd;
973         if (x == xs) {
974             alphaStart = x2 - x1;
975             Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
976             alphaEnd = 0;
977         } else {
978             alphaStart = 64 - (x1 & 63);
979             alphaEnd = (x2 & 63);
980         }
981
982         // draw first pixel
983         if (dasher.on()) {
984             uint alpha = (quint8)(y >> 8);
985             drawPixel(stroker, x, y>>16, (255-alpha) * alphaStart >> 6);
986             drawPixel(stroker, x, (y>>16) + 1, alpha * alphaStart >> 6);
987         }
988         dasher.adjust();
989         y += yinc;
990         ++x;
991         // draw line
992         if (x < xs) {
993             do {
994                 if (dasher.on()) {
995                     uint alpha = (quint8)(y >> 8);
996                     drawPixel(stroker, x, y>>16, (255-alpha));
997                     drawPixel(stroker, x, (y>>16) + 1, alpha);
998                 }
999                 dasher.adjust();
1000                 y += yinc;
1001             } while (++x < xs);
1002         }
1003         // draw last pixel
1004         if (alphaEnd && dasher.on()) {
1005             uint alpha = (quint8)(y >> 8);
1006             drawPixel(stroker, x, y>>16, (255-alpha) * alphaEnd >> 6);
1007             drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6);
1008         }
1009     }
1010 }
1011
1012 QT_END_NAMESPACE