1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtGui module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qcosmeticstroker_p.h"
43 #include "private/qpainterpath_p.h"
50 inline QString capString(int caps)
53 if (caps & QCosmeticStroker::CapBegin) {
56 if (caps & QCosmeticStroker::CapEnd) {
63 #define toF26Dot6(x) ((int)((x)*64.))
65 static inline uint sourceOver(uint d, uint color)
67 return color + BYTE_MUL(d, qAlpha(~color));
70 inline static int F16Dot16FixedDiv(int x, int y)
73 return (((qlonglong)x) << 16) / y;
77 typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage);
82 QCosmeticStroker *stroker;
88 Dasher(QCosmeticStroker *s, bool reverse, int start, int stop)
91 int delta = stop - start;
93 pattern = stroker->reversePattern;
94 offset = stroker->patternLength - stroker->patternOffset - delta - ((start & 63) - 32);
97 pattern = stroker->pattern;
98 offset = stroker->patternOffset - ((start & 63) - 32);
101 offset %= stroker->patternLength;
103 offset += stroker->patternLength;
106 while (offset>= pattern[dashIndex])
109 // qDebug() << " dasher" << offset/64. << reverse << dashIndex;
110 stroker->patternOffset += delta;
111 stroker->patternOffset %= stroker->patternLength;
115 return (dashIndex + dashOn) & 1;
119 if (offset >= pattern[dashIndex]) {
121 dashIndex %= stroker->patternSize;
123 offset %= stroker->patternLength;
124 // qDebug() << "dasher.adjust" << offset/64. << dashIndex;
129 NoDasher(QCosmeticStroker *, bool, int, int) {}
130 bool on() const { return true; }
131 void adjust(int = 0) {}
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);
141 inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage)
143 const QRect &cl = stroker->clip;
144 if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
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;
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;
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;
162 inline void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage)
164 const QRect &cl = stroker->clip;
165 if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
168 int offset = x + stroker->ppl*y;
169 uint c = BYTE_MUL(stroker->color, coverage);
170 stroker->pixels[offset] = sourceOver(stroker->pixels[offset], c);
173 inline void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int)
175 const QRect &cl = stroker->clip;
176 if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
179 int offset = x + stroker->ppl*y;
180 stroker->pixels[offset] = sourceOver(stroker->pixels[offset], stroker->color);
183 enum StrokeSelection {
192 static StrokeLine strokeLine(int strokeSelection)
196 switch (strokeSelection) {
197 case Aliased|Solid|RegularDraw:
198 stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, NoDasher>;
200 case Aliased|Solid|FastDraw:
201 stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, NoDasher>;
203 case Aliased|Dashed|RegularDraw:
204 stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, Dasher>;
206 case Aliased|Dashed|FastDraw:
207 stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, Dasher>;
209 case AntiAliased|Solid|RegularDraw:
210 stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, NoDasher>;
212 case AntiAliased|Solid|FastDraw:
213 stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, NoDasher>;
215 case AntiAliased|Dashed|RegularDraw:
216 stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, Dasher>;
218 case AntiAliased|Dashed|FastDraw:
219 stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, Dasher>;
228 void QCosmeticStroker::setup()
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;
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;
244 if (state->renderHints & QPainter::Antialiasing)
245 strokeSelection |= AntiAliased;
247 const QVector<qreal> &penPattern = state->lastPen.dashPattern();
248 if (penPattern.isEmpty()) {
249 Q_ASSERT(!pattern && !reversePattern);
255 pattern = (int *)malloc(penPattern.size()*sizeof(int));
256 reversePattern = (int *)malloc(penPattern.size()*sizeof(int));
257 patternSize = penPattern.size();
260 for (int i = 0; i < patternSize; ++i) {
261 patternLength += (int) qMax(1. , penPattern.at(i)*64.);
262 pattern[i] = patternLength;
265 for (int i = 0; i < patternSize; ++i) {
266 patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.);
267 reversePattern[i] = patternLength;
269 strokeSelection |= Dashed;
270 // qDebug() << "setup: size=" << patternSize << "length=" << patternLength/64.;
273 stroke = strokeLine(strokeSelection);
275 qreal width = state->lastPen.widthF();
278 else if (state->lastPen.isCosmetic())
279 opacity = (int) 256*width;
281 opacity = (int) 256*width*state->txscale;
282 opacity = qBound(0, opacity, 256);
284 drawCaps = state->lastPen.capStyle() != Qt::FlatCap;
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;
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;
302 // returns true if the whole line gets clipped away
303 bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2)
305 // basic/rough clipping is done in floating point coordinates to avoid
306 // integer overflow problems.
310 y1 += (y2 - y1)/(x2 - x1) * (xmin - x1);
312 } else if (x1 > xmax) {
315 y1 += (y2 - y1)/(x2 - x1) * (xmax - x1);
320 y2 += (y2 - y1)/(x2 - x1) * (xmin - x2);
322 } else if (x2 > xmax) {
324 y2 += (y2 - y1)/(x2 - x1) * (xmax - x2);
331 x1 += (x2 - x1)/(y2 - y1) * (ymin - y1);
333 } else if (y1 > ymax) {
336 x1 += (x2 - x1)/(y2 - y1) * (ymax - y1);
341 x2 += (x2 - x1)/(y2 - y1) * (ymin - y2);
343 } else if (y2 > ymax) {
345 x2 += (x2 - x1)/(y2 - y1) * (ymax - y2);
357 void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2)
364 QPointF start = p1 * state->matrix;
365 QPointF end = p2 * state->matrix;
367 patternOffset = state->lastPen.dashOffset()*64;
370 stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0);
372 blend(current_span, spans, &state->penData);
376 void QCosmeticStroker::drawPoints(const QPoint *points, int num)
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);
385 blend(current_span, spans, &state->penData);
389 void QCosmeticStroker::drawPoints(const QPointF *points, int num)
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);
398 blend(current_span, spans, &state->penData);
402 void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2)
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
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
414 if (clipLine(rx1, ry1, rx2, ry2))
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;
423 int dx = qAbs(x2 - x1);
424 int dy = qAbs(y2 - y1);
428 bool swapped = false;
434 int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
437 int y = (y1 + 32) >> 6;
438 int ys = (y2 + 32) >> 6;
441 x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6;
444 lastPixel.x = x >> 16;
446 lastDir = QCosmeticStroker::BottomToTop;
448 lastPixel.x = (x + (ys - y - 1)*xinc) >> 16;
449 lastPixel.y = ys - 1;
450 lastDir = QCosmeticStroker::TopToBottom;
452 lastAxisAligned = qAbs(xinc) < (1 << 14);
459 bool swapped = false;
465 int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
468 int x = (x1 + 32) >> 6;
469 int xs = (x2 + 32) >> 6;
472 y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6;
476 lastPixel.y = y >> 16;
477 lastDir = QCosmeticStroker::RightToLeft;
479 lastPixel.x = xs - 1;
480 lastPixel.y = (y + (xs - x - 1)*yinc) >> 16;
481 lastDir = QCosmeticStroker::LeftToRight;
483 lastAxisAligned = qAbs(yinc) < (1 << 14);
486 // qDebug() << " moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir;
489 static inline const QPainterPath::ElementType *subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end,
490 const qreal *points, bool *closed)
492 const QPainterPath::ElementType *start = t;
495 // find out if the subpath is closed
497 if (*t == QPainterPath::MoveToElement)
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]);
509 void QCosmeticStroker::drawPath(const QVectorPath &path)
511 // qDebug() << ">>>> drawpath" << path.convertToPainterPath()
512 // << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose();
516 const qreal *points = path.points();
517 const QPainterPath::ElementType *type = path.elements();
520 const QPainterPath::ElementType *end = type + path.elementCount();
523 Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement);
525 QPointF p = QPointF(points[0], points[1]) * state->matrix;
526 patternOffset = state->lastPen.dashOffset()*64;
530 const QPainterPath::ElementType *e = subPath(type, end, points, &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());
537 int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
538 // qDebug() << "closed =" << closed << capString(caps);
544 QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
546 case QPainterPath::MoveToElement:
547 Q_ASSERT(!"Logic error");
550 case QPainterPath::LineToElement:
551 if (!closed && drawCaps && type == e - 1)
553 stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
559 case QPainterPath::CurveToElement: {
560 if (!closed && drawCaps && type == e - 3)
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);
570 case QPainterPath::CurveToDataElement:
571 Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
577 } else { // !type, simple polygon
578 QPointF p = QPointF(points[0], points[1]) * state->matrix;
580 patternOffset = state->lastPen.dashOffset()*64;
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;
588 QPointF p2 = QPointF(end[-2], end[-1]) * state->matrix;
589 calculateLastPoint(p2.x(), p2.y(), p.x(), p.y());
593 while (points < end) {
594 QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
596 if (!closed && drawCaps && points == end - 2)
599 stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
605 if (path.hasImplicitClose())
606 stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps);
610 blend(current_span, spans, &state->penData);
614 void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps)
616 // qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps);
617 const int maxSubDivisions = 6;
618 PointF points[3*maxSubDivisions + 4];
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();
630 int level = maxSubDivisions;
632 renderCubicSubdivision(p, level, caps);
635 static void splitCubic(QCosmeticStroker::PointF *points)
637 const qreal half = .5;
640 points[6].x = points[3].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;
650 points[6].y = points[3].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;
661 void QCosmeticStroker::renderCubicSubdivision(QCosmeticStroker::PointF *points, int level, int caps)
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));
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) {
673 renderCubicSubdivision(points + 3, level, caps & CapBegin);
674 renderCubicSubdivision(points, level, caps & CapEnd);
679 stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps);
682 static inline int swapCaps(int caps)
684 return ((caps & QCosmeticStroker::CapBegin) << 1) |
685 ((caps & QCosmeticStroker::CapEnd) >> 1);
688 // adjust line by half a pixel
689 static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc)
691 if (caps & QCosmeticStroker::CapBegin) {
695 if (caps & QCosmeticStroker::CapEnd) {
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.
704 template<DrawPixel drawPixel, class Dasher>
705 static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
707 if (stroker->clipLine(rx1, ry1, rx2, ry2))
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;
716 int dx = qAbs(x2 - x1);
717 int dy = qAbs(y2 - y1);
719 QCosmeticStroker::Point last = stroker->lastPixel;
721 // qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.;
725 QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom;
727 bool swapped = false;
732 caps = swapCaps(caps);
733 dir = QCosmeticStroker::BottomToTop;
735 int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
738 if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir)
739 caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
741 capAdjust(caps, y1, y2, x, xinc);
743 int y = (y1 + 32) >> 6;
744 int ys = (y2 + 32) >> 6;
747 x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6;
749 // calculate first and last pixel and perform dropout control
750 QCosmeticStroker::Point first;
753 last.x = (x + (ys - y - 1)*xinc) >> 16;
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
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
783 stroker->lastDir = dir;
784 stroker->lastAxisAligned = axisAligned;
786 Dasher dasher(stroker, swapped, y << 6, ys << 6);
790 drawPixel(stroker, x >> 16, y, 255);
800 QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight;
802 bool swapped = false;
807 caps = swapCaps(caps);
808 dir = QCosmeticStroker::RightToLeft;
810 int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
813 if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir)
814 caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
816 capAdjust(caps, x1, x2, y, yinc);
818 int x = (x1 + 32) >> 6;
819 int xs = (x2 + 32) >> 6;
822 y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6;
824 // calculate first and last pixel to perform dropout control
825 QCosmeticStroker::Point first;
829 last.y = (y + (xs - x - 1)*yinc) >> 16;
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
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
857 stroker->lastDir = dir;
858 stroker->lastAxisAligned = axisAligned;
860 Dasher dasher(stroker, swapped, x << 6, xs << 6);
864 drawPixel(stroker, x, y >> 16, 255);
870 stroker->lastPixel = last;
874 template<DrawPixel drawPixel, class Dasher>
875 static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
877 if (stroker->clipLine(rx1, ry1, rx2, ry2))
880 int x1 = toF26Dot6(rx1);
881 int y1 = toF26Dot6(ry1);
882 int x2 = toF26Dot6(rx2);
883 int y2 = toF26Dot6(ry2);
888 if (qAbs(dx) < qAbs(dy)) {
891 int xinc = F16Dot16FixedDiv(dx, dy);
893 bool swapped = false;
898 caps = swapCaps(caps);
901 int x = (x1 - 32) << 10;
902 x -= ( ((y1 & 63) - 32) * xinc ) >> 6;
904 capAdjust(caps, y1, y2, x, xinc);
906 Dasher dasher(stroker, swapped, y1, y2);
911 int alphaStart, alphaEnd;
913 alphaStart = y2 - y1;
914 Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
917 alphaStart = 64 - (y1 & 63);
918 alphaEnd = (y2 & 63);
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;
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);
935 uint alpha = (quint8)(x >> 8);
936 drawPixel(stroker, x>>16, y, (255-alpha));
937 drawPixel(stroker, (x>>16) + 1, y, alpha);
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);
954 int yinc = F16Dot16FixedDiv(dy, dx);
956 bool swapped = false;
961 caps = swapCaps(caps);
964 int y = (y1 - 32) << 10;
965 y -= ( ((x1 & 63) - 32) * yinc ) >> 6;
967 capAdjust(caps, x1, x2, y, yinc);
969 Dasher dasher(stroker, swapped, x1, x2);
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;
978 alphaStart = x2 - x1;
979 Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
982 alphaStart = 64 - (x1 & 63);
983 alphaEnd = (x2 & 63);
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);
999 uint alpha = (quint8)(y >> 8);
1000 drawPixel(stroker, x, y>>16, (255-alpha));
1001 drawPixel(stroker, x, (y>>16) + 1, alpha);
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);