Make QPen default to 1-width non-cosmetic.
[profile/ivi/qtbase.git] / src / gui / painting / qcosmeticstroker.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 (qt_pen_is_cosmetic(state->lastPen, state->renderHints))
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     if (p1 == p2) {
360         drawPoints(&p1, 1);
361         return;
362     }
363
364     QPointF start = p1 * state->matrix;
365     QPointF end = p2 * state->matrix;
366
367     patternOffset = state->lastPen.dashOffset()*64;
368     lastPixel.x = -1;
369
370     stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0);
371
372     blend(current_span, spans, &state->penData);
373     current_span = 0;
374 }
375
376 void QCosmeticStroker::drawPoints(const QPoint *points, int num)
377 {
378     const QPoint *end = points + num;
379     while (points < end) {
380         QPointF p = QPointF(*points) * state->matrix;
381         drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
382         ++points;
383     }
384
385     blend(current_span, spans, &state->penData);
386     current_span = 0;
387 }
388
389 void QCosmeticStroker::drawPoints(const QPointF *points, int num)
390 {
391     const QPointF *end = points + num;
392     while (points < end) {
393         QPointF p = (*points) * state->matrix;
394         drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
395         ++points;
396     }
397
398     blend(current_span, spans, &state->penData);
399     current_span = 0;
400 }
401
402 void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2)
403 {
404     // this is basically the same code as used in the aliased stroke method,
405     // but it only determines the direction and last point of a line
406     //
407     // This is being used to have proper dropout control for closed contours
408     // by calculating the direction and last pixel of the last segment in the contour.
409     // the info is then used to perform dropout control when drawing the first line segment
410     // of the contour
411     lastPixel.x = -1;
412     lastPixel.y = -1;
413
414     if (clipLine(rx1, ry1, rx2, ry2))
415         return;
416
417     const int half = legacyRounding ? 31 : 0;
418     int x1 = toF26Dot6(rx1) + half;
419     int y1 = toF26Dot6(ry1) + half;
420     int x2 = toF26Dot6(rx2) + half;
421     int y2 = toF26Dot6(ry2) + half;
422
423     int dx = qAbs(x2 - x1);
424     int dy = qAbs(y2 - y1);
425
426     if (dx < dy) {
427         // vertical
428         bool swapped = false;
429         if (y1 > y2) {
430             swapped = true;
431             qSwap(y1, y2);
432             qSwap(x1, x2);
433         }
434         int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
435         int x = x1 << 10;
436
437         int y = (y1 + 32) >> 6;
438         int ys = (y2 + 32) >> 6;
439
440         if (y != ys) {
441             x += ( ((((y << 6) + 32 - y1)))  * xinc ) >> 6;
442
443             if (swapped) {
444                 lastPixel.x = x >> 16;
445                 lastPixel.y = y;
446                 lastDir = QCosmeticStroker::BottomToTop;
447             } else {
448                 lastPixel.x = (x + (ys - y - 1)*xinc) >> 16;
449                 lastPixel.y = ys - 1;
450                 lastDir = QCosmeticStroker::TopToBottom;
451             }
452             lastAxisAligned = qAbs(xinc) < (1 << 14);
453         }
454     } else {
455         // horizontal
456         if (!dx)
457             return;
458
459         bool swapped = false;
460         if (x1 > x2) {
461             swapped = true;
462             qSwap(x1, x2);
463             qSwap(y1, y2);
464         }
465         int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
466         int y = y1 << 10;
467
468         int x = (x1 + 32) >> 6;
469         int xs = (x2 + 32) >> 6;
470
471         if (x != xs) {
472             y += ( ((((x << 6) + 32 - x1)))  * yinc ) >> 6;
473
474             if (swapped) {
475                 lastPixel.x = x;
476                 lastPixel.y = y >> 16;
477                 lastDir = QCosmeticStroker::RightToLeft;
478             } else {
479                 lastPixel.x = xs - 1;
480                 lastPixel.y = (y + (xs - x - 1)*yinc) >> 16;
481                 lastDir = QCosmeticStroker::LeftToRight;
482             }
483             lastAxisAligned = qAbs(yinc) < (1 << 14);
484         }
485     }
486 //    qDebug() << "   moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir;
487 }
488
489 static inline const QPainterPath::ElementType *subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end,
490                                                  const qreal *points, bool *closed)
491 {
492     const QPainterPath::ElementType *start = t;
493     ++t;
494
495     // find out if the subpath is closed
496     while (t < end) {
497         if (*t == QPainterPath::MoveToElement)
498             break;
499         ++t;
500     }
501
502     int offset = t - start - 1;
503 //    qDebug() << "subpath" << offset << points[0] << points[1] << points[2*offset] << points[2*offset+1];
504     *closed = (points[0] == points[2*offset] && points[1] == points[2*offset + 1]);
505
506     return t;
507 }
508
509 void QCosmeticStroker::drawPath(const QVectorPath &path)
510 {
511 //    qDebug() << ">>>> drawpath" << path.convertToPainterPath()
512 //             << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose();
513     if (path.isEmpty())
514         return;
515
516     const qreal *points = path.points();
517     const QPainterPath::ElementType *type = path.elements();
518
519     if (type) {
520         const QPainterPath::ElementType *end = type + path.elementCount();
521
522         while (type < end) {
523             Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement);
524
525             QPointF p = QPointF(points[0], points[1]) * state->matrix;
526             patternOffset = state->lastPen.dashOffset()*64;
527             lastPixel.x = -1;
528
529             bool closed;
530             const QPainterPath::ElementType *e = subPath(type, end, points, &closed);
531             if (closed) {
532                 const qreal *p = points + 2*(e-type);
533                 QPointF p1 = QPointF(p[-4], p[-3]) * state->matrix;
534                 QPointF p2 = QPointF(p[-2], p[-1]) * state->matrix;
535                 calculateLastPoint(p1.x(), p1.y(), p2.x(), p2.y());
536             }
537             int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
538 //            qDebug() << "closed =" << closed << capString(caps);
539
540             points += 2;
541             ++type;
542
543             while (type < e) {
544                 QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
545                 switch (*type) {
546                 case QPainterPath::MoveToElement:
547                     Q_ASSERT(!"Logic error");
548                     break;
549
550                 case QPainterPath::LineToElement:
551                     if (!closed && drawCaps && type == e - 1)
552                         caps |= CapEnd;
553                     stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
554                     p = p2;
555                     points += 2;
556                     ++type;
557                     break;
558
559                 case QPainterPath::CurveToElement: {
560                     if (!closed && drawCaps && type == e - 3)
561                         caps |= CapEnd;
562                     QPointF p3 = QPointF(points[2], points[3]) * state->matrix;
563                     QPointF p4 = QPointF(points[4], points[5]) * state->matrix;
564                     renderCubic(p, p2, p3, p4, caps);
565                     p = p4;
566                     type += 3;
567                     points += 6;
568                     break;
569                 }
570                 case QPainterPath::CurveToDataElement:
571                     Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
572                     break;
573                 }
574                 caps = NoCaps;
575             }
576         }
577     } else { // !type, simple polygon
578         QPointF p = QPointF(points[0], points[1]) * state->matrix;
579         QPointF movedTo = p;
580         patternOffset = state->lastPen.dashOffset()*64;
581         lastPixel.x = -1;
582
583         const qreal *end = points + 2*path.elementCount();
584         // handle closed path case
585         bool closed = path.hasImplicitClose() || (points[0] == end[-2] && points[1] == end[-1]);
586         int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
587         if (closed) {
588             QPointF p2 = QPointF(end[-2], end[-1]) * state->matrix;
589             calculateLastPoint(p2.x(), p2.y(), p.x(), p.y());
590         }
591
592         points += 2;
593         while (points < end) {
594             QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
595
596             if (!closed && drawCaps && points == end - 2)
597                 caps |= CapEnd;
598
599             stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
600
601             p = p2;
602             points += 2;
603             caps = NoCaps;
604         }
605         if (path.hasImplicitClose())
606             stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps);
607     }
608
609
610     blend(current_span, spans, &state->penData);
611     current_span = 0;
612 }
613
614 void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps)
615 {
616 //    qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps);
617     const int maxSubDivisions = 6;
618     PointF points[3*maxSubDivisions + 4];
619
620     points[3].x = p1.x();
621     points[3].y = p1.y();
622     points[2].x = p2.x();
623     points[2].y = p2.y();
624     points[1].x = p3.x();
625     points[1].y = p3.y();
626     points[0].x = p4.x();
627     points[0].y = p4.y();
628
629     PointF *p = points;
630     int level = maxSubDivisions;
631
632     renderCubicSubdivision(p, level, caps);
633 }
634
635 static void splitCubic(QCosmeticStroker::PointF *points)
636 {
637     const qreal half = .5;
638     qreal  a, b, c, d;
639
640     points[6].x = points[3].x;
641     c = points[1].x;
642     d = points[2].x;
643     points[1].x = a = ( points[0].x + c ) * half;
644     points[5].x = b = ( points[3].x + d ) * half;
645     c = ( c + d ) * half;
646     points[2].x = a = ( a + c ) * half;
647     points[4].x = b = ( b + c ) * half;
648     points[3].x = ( a + b ) * half;
649
650     points[6].y = points[3].y;
651     c = points[1].y;
652     d = points[2].y;
653     points[1].y = a = ( points[0].y + c ) * half;
654     points[5].y = b = ( points[3].y + d ) * half;
655     c = ( c + d ) * half;
656     points[2].y = a = ( a + c ) * half;
657     points[4].y = b = ( b + c ) * half;
658     points[3].y = ( a + b ) * half;
659 }
660
661 void QCosmeticStroker::renderCubicSubdivision(QCosmeticStroker::PointF *points, int level, int caps)
662 {
663     if (level) {
664         qreal dx = points[3].x - points[0].x;
665         qreal dy = points[3].y - points[0].y;
666         qreal len = ((qreal).25) * (qAbs(dx) + qAbs(dy));
667
668         if (qAbs(dx * (points[0].y - points[2].y) - dy * (points[0].x - points[2].x)) >= len ||
669             qAbs(dx * (points[0].y - points[1].y) - dy * (points[0].x - points[1].x)) >= len) {
670             splitCubic(points);
671
672             --level;
673             renderCubicSubdivision(points + 3, level, caps & CapBegin);
674             renderCubicSubdivision(points, level, caps & CapEnd);
675             return;
676         }
677     }
678
679     stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps);
680 }
681
682 static inline int swapCaps(int caps)
683 {
684     return ((caps & QCosmeticStroker::CapBegin) << 1) |
685            ((caps & QCosmeticStroker::CapEnd) >> 1);
686 }
687
688 // adjust line by half a pixel
689 static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc)
690 {
691     if (caps & QCosmeticStroker::CapBegin) {
692         x1 -= 32;
693         y -= yinc >> 1;
694     }
695     if (caps & QCosmeticStroker::CapEnd) {
696         x2 += 32;
697     }
698 }
699
700 /*
701   The hard part about this is dropout control and avoiding douple drawing of points when
702   the drawing shifts from horizontal to vertical or back.
703   */
704 template<DrawPixel drawPixel, class Dasher>
705 static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
706 {
707     if (stroker->clipLine(rx1, ry1, rx2, ry2))
708         return;
709
710     const int half = stroker->legacyRounding ? 31 : 0;
711     int x1 = toF26Dot6(rx1) + half;
712     int y1 = toF26Dot6(ry1) + half;
713     int x2 = toF26Dot6(rx2) + half;
714     int y2 = toF26Dot6(ry2) + half;
715
716     int dx = qAbs(x2 - x1);
717     int dy = qAbs(y2 - y1);
718
719     QCosmeticStroker::Point last = stroker->lastPixel;
720
721 //    qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.;
722
723     if (dx < dy) {
724         // vertical
725         QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom;
726
727         bool swapped = false;
728         if (y1 > y2) {
729             swapped = true;
730             qSwap(y1, y2);
731             qSwap(x1, x2);
732             caps = swapCaps(caps);
733             dir = QCosmeticStroker::BottomToTop;
734         }
735         int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
736         int x = x1 << 10;
737
738         if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir)
739             caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
740
741         capAdjust(caps, y1, y2, x, xinc);
742
743         int y = (y1 + 32) >> 6;
744         int ys = (y2 + 32) >> 6;
745
746         if (y != ys) {
747             x += ( ((((y << 6) + 32 - y1)))  * xinc ) >> 6;
748
749             // calculate first and last pixel and perform dropout control
750             QCosmeticStroker::Point first;
751             first.x = x >> 16;
752             first.y = y;
753             last.x = (x + (ys - y - 1)*xinc) >> 16;
754             last.y = ys - 1;
755             if (swapped)
756                 qSwap(first, last);
757
758             bool axisAligned = qAbs(xinc) < (1 << 14);
759             if (stroker->lastPixel.x >= 0) {
760                 if (first.x == stroker->lastPixel.x &&
761                     first.y == stroker->lastPixel.y) {
762                     // remove duplicated pixel
763                     if (swapped) {
764                         --ys;
765                     } else {
766                         ++y;
767                         x += xinc;
768                     }
769                 } else if (stroker->lastDir != dir &&
770                            (((axisAligned && stroker->lastAxisAligned) &&
771                              stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
772                             (qAbs(stroker->lastPixel.x - first.x) > 1 ||
773                              qAbs(stroker->lastPixel.y - first.y) > 1))) {
774                     // have a missing pixel, insert it
775                     if (swapped) {
776                         ++ys;
777                     } else {
778                         --y;
779                         x -= xinc;
780                     }
781                 }
782             }
783             stroker->lastDir = dir;
784             stroker->lastAxisAligned = axisAligned;
785
786             Dasher dasher(stroker, swapped, y << 6, ys << 6);
787
788             do {
789                 if (dasher.on())
790                     drawPixel(stroker, x >> 16, y, 255);
791                 dasher.adjust();
792                 x += xinc;
793             } while (++y < ys);
794         }
795     } else {
796         // horizontal
797         if (!dx)
798             return;
799
800         QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight;
801
802         bool swapped = false;
803         if (x1 > x2) {
804             swapped = true;
805             qSwap(x1, x2);
806             qSwap(y1, y2);
807             caps = swapCaps(caps);
808             dir = QCosmeticStroker::RightToLeft;
809         }
810         int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
811         int y = y1 << 10;
812
813         if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir)
814             caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
815
816         capAdjust(caps, x1, x2, y, yinc);
817
818         int x = (x1 + 32) >> 6;
819         int xs = (x2 + 32) >> 6;
820
821         if (x != xs) {
822             y += ( ((((x << 6) + 32 - x1)))  * yinc ) >> 6;
823
824             // calculate first and last pixel to perform dropout control
825             QCosmeticStroker::Point first;
826             first.x = x;
827             first.y = y >> 16;
828             last.x = xs - 1;
829             last.y = (y + (xs - x - 1)*yinc) >> 16;
830             if (swapped)
831                 qSwap(first, last);
832
833             bool axisAligned = qAbs(yinc) < (1 << 14);
834             if (stroker->lastPixel.x >= 0) {
835                 if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) {
836                     // remove duplicated pixel
837                     if (swapped) {
838                         --xs;
839                     } else {
840                         ++x;
841                         y += yinc;
842                     }
843                 } else if (stroker->lastDir != dir &&
844                            (((axisAligned && stroker->lastAxisAligned) &&
845                              stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
846                             (qAbs(stroker->lastPixel.x - first.x) > 1 ||
847                              qAbs(stroker->lastPixel.y - first.y) > 1))) {
848                     // have a missing pixel, insert it
849                     if (swapped) {
850                         ++xs;
851                     } else {
852                         --x;
853                         y -= yinc;
854                     }
855                 }
856             }
857             stroker->lastDir = dir;
858             stroker->lastAxisAligned = axisAligned;
859
860             Dasher dasher(stroker, swapped, x << 6, xs << 6);
861
862             do {
863                 if (dasher.on())
864                     drawPixel(stroker, x, y >> 16, 255);
865                 dasher.adjust();
866                 y += yinc;
867             } while (++x < xs);
868         }
869     }
870     stroker->lastPixel = last;
871 }
872
873
874 template<DrawPixel drawPixel, class Dasher>
875 static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
876 {
877     if (stroker->clipLine(rx1, ry1, rx2, ry2))
878         return;
879
880     int x1 = toF26Dot6(rx1);
881     int y1 = toF26Dot6(ry1);
882     int x2 = toF26Dot6(rx2);
883     int y2 = toF26Dot6(ry2);
884
885     int dx = x2 - x1;
886     int dy = y2 - y1;
887
888     if (qAbs(dx) < qAbs(dy)) {
889         // vertical
890
891         int xinc = F16Dot16FixedDiv(dx, dy);
892
893         bool swapped = false;
894         if (y1 > y2) {
895             qSwap(y1, y2);
896             qSwap(x1, x2);
897             swapped = true;
898             caps = swapCaps(caps);
899         }
900
901         int x = (x1 - 32) << 10;
902         x -= ( ((y1 & 63) - 32)  * xinc ) >> 6;
903
904         capAdjust(caps, y1, y2, x, xinc);
905
906         Dasher dasher(stroker, swapped, y1, y2);
907
908         int y = y1 >> 6;
909         int ys = y2 >> 6;
910
911         int alphaStart, alphaEnd;
912         if (y == ys) {
913             alphaStart = y2 - y1;
914             Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
915             alphaEnd = 0;
916         } else {
917             alphaStart = 64 - (y1 & 63);
918             alphaEnd = (y2 & 63);
919         }
920 //        qDebug() << "vertical" << x1/64. << y1/64. << x2/64. << y2/64.;
921 //        qDebug() << "          x=" << x << "dx=" << dx << "xi=" << (x>>16) << "xsi=" << ((x+(ys-y)*dx)>>16) << "y=" << y << "ys=" << ys;
922
923         // draw first pixel
924         if (dasher.on()) {
925             uint alpha = (quint8)(x >> 8);
926             drawPixel(stroker, x>>16, y, (255-alpha) * alphaStart >> 6);
927             drawPixel(stroker, (x>>16) + 1, y, alpha * alphaStart >> 6);
928         }
929         dasher.adjust();
930         x += xinc;
931         ++y;
932         if (y < ys) {
933             do {
934                 if (dasher.on()) {
935                     uint alpha = (quint8)(x >> 8);
936                     drawPixel(stroker, x>>16, y, (255-alpha));
937                     drawPixel(stroker, (x>>16) + 1, y, alpha);
938                 }
939                 dasher.adjust();
940                 x += xinc;
941             } while (++y < ys);
942         }
943         // draw last pixel
944         if (alphaEnd && dasher.on()) {
945             uint alpha = (quint8)(x >> 8);
946             drawPixel(stroker, x>>16, y, (255-alpha) * alphaEnd >> 6);
947             drawPixel(stroker, (x>>16) + 1, y, alpha * alphaEnd >> 6);
948         }
949     } else {
950         // horizontal
951         if (!dx)
952             return;
953
954         int yinc = F16Dot16FixedDiv(dy, dx);
955
956         bool swapped = false;
957         if (x1 > x2) {
958             qSwap(x1, x2);
959             qSwap(y1, y2);
960             swapped = true;
961             caps = swapCaps(caps);
962         }
963
964         int y = (y1 - 32) << 10;
965         y -= ( ((x1 & 63) - 32)  * yinc ) >> 6;
966
967         capAdjust(caps, x1, x2, y, yinc);
968
969         Dasher dasher(stroker, swapped, x1, x2);
970
971         int x = x1 >> 6;
972         int xs = x2 >> 6;
973
974 //        qDebug() << "horizontal" << x1/64. << y1/64. << x2/64. << y2/64.;
975 //        qDebug() << "          y=" << y << "dy=" << dy << "x=" << x << "xs=" << xs << "yi=" << (y>>16) << "ysi=" << ((y+(xs-x)*dy)>>16);
976         int alphaStart, alphaEnd;
977         if (x == xs) {
978             alphaStart = x2 - x1;
979             Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
980             alphaEnd = 0;
981         } else {
982             alphaStart = 64 - (x1 & 63);
983             alphaEnd = (x2 & 63);
984         }
985
986         // draw first pixel
987         if (dasher.on()) {
988             uint alpha = (quint8)(y >> 8);
989             drawPixel(stroker, x, y>>16, (255-alpha) * alphaStart >> 6);
990             drawPixel(stroker, x, (y>>16) + 1, alpha * alphaStart >> 6);
991         }
992         dasher.adjust();
993         y += yinc;
994         ++x;
995         // draw line
996         if (x < xs) {
997             do {
998                 if (dasher.on()) {
999                     uint alpha = (quint8)(y >> 8);
1000                     drawPixel(stroker, x, y>>16, (255-alpha));
1001                     drawPixel(stroker, x, (y>>16) + 1, alpha);
1002                 }
1003                 dasher.adjust();
1004                 y += yinc;
1005             } while (++x < xs);
1006         }
1007         // draw last pixel
1008         if (alphaEnd && dasher.on()) {
1009             uint alpha = (quint8)(y >> 8);
1010             drawPixel(stroker, x, y>>16, (255-alpha) * alphaEnd >> 6);
1011             drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6);
1012         }
1013     }
1014 }
1015
1016 QT_END_NAMESPACE