1 /****************************************************************************
3 ** Copyright (C) 2013 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 "qrasterizer_p.h"
47 #include <private/qmath_p.h>
48 #include <private/qdatabuffer_p.h>
49 #include <private/qdrawhelper_p.h>
54 #define Q16Dot16ToFloat(i) ((i)/qreal(65536.))
55 #define FloatToQ16Dot16(i) (int)((i) * qreal(65536.))
56 #define IntToQ16Dot16(i) ((i) << 16)
57 #define Q16Dot16ToInt(i) ((i) >> 16)
58 #define Q16Dot16Factor 65536
60 #define Q16Dot16Multiply(x, y) (int)((qlonglong(x) * qlonglong(y)) >> 16)
61 #define Q16Dot16FastMultiply(x, y) (((x) * (y)) >> 16)
63 #define SPAN_BUFFER_SIZE 256
65 #define COORD_ROUNDING 1 // 0: round up, 1: round down
66 #define COORD_OFFSET 32 // 26.6, 32 is half a pixel
68 static inline QT_FT_Vector PointToVector(const QPointF &p)
70 QT_FT_Vector result = { QT_FT_Pos(p.x() * 64), QT_FT_Pos(p.y() * 64) };
76 QSpanBuffer(ProcessSpans blend, void *data, const QRect &clipRect)
80 , m_clipRect(clipRect)
89 void addSpan(int x, unsigned int len, int y, unsigned char coverage)
91 if (!coverage || !len)
94 Q_ASSERT(y >= m_clipRect.top());
95 Q_ASSERT(y <= m_clipRect.bottom());
96 Q_ASSERT(x >= m_clipRect.left());
97 Q_ASSERT(x + int(len) - 1 <= m_clipRect.right());
99 m_spans[m_spanCount].x = x;
100 m_spans[m_spanCount].len = len;
101 m_spans[m_spanCount].y = y;
102 m_spans[m_spanCount].coverage = coverage;
104 if (++m_spanCount == SPAN_BUFFER_SIZE)
111 m_blend(m_spanCount, m_spans, m_data);
115 QT_FT_Span m_spans[SPAN_BUFFER_SIZE];
118 ProcessSpans m_blend;
124 #define CHUNK_SIZE 64
131 void begin(int top, int bottom, int left, int right,
132 Qt::FillRule fillRule, QSpanBuffer *spanBuffer);
135 void mergeCurve(const QT_FT_Vector &a, const QT_FT_Vector &b,
136 const QT_FT_Vector &c, const QT_FT_Vector &d);
137 void mergeLine(QT_FT_Vector a, QT_FT_Vector b);
158 inline bool clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding);
159 inline void mergeIntersection(Intersection *head, const Intersection &isect);
163 void emitNode(const Intersection *node);
164 void emitSpans(int chunk);
166 inline void allocate(int size);
168 QDataBuffer<Line> m_lines;
185 Intersection *m_intersections;
187 QSpanBuffer *m_spanBuffer;
189 QDataBuffer<Line *> m_active;
191 template <typename T>
192 friend void qScanConvert(QScanConverter &d, T allVertical);
195 class QRasterizerPrivate
203 QScanConverter scanConverter;
206 QScanConverter::QScanConverter()
215 QScanConverter::~QScanConverter()
218 free(m_intersections);
221 void QScanConverter::begin(int top, int bottom, int left, int right,
222 Qt::FillRule fillRule, QSpanBuffer *spanBuffer)
226 m_leftFP = IntToQ16Dot16(left);
227 m_rightFP = IntToQ16Dot16(right + 1);
231 m_fillRuleMask = fillRule == Qt::WindingFill ? ~0x0 : 0x1;
232 m_spanBuffer = spanBuffer;
235 void QScanConverter::prepareChunk()
239 allocate(CHUNK_SIZE);
240 memset(m_intersections, 0, CHUNK_SIZE * sizeof(Intersection));
243 void QScanConverter::emitNode(const Intersection *node)
247 emitNode(node + node->left);
249 if (m_winding & m_fillRuleMask)
250 m_spanBuffer->addSpan(m_x, node->x - m_x, m_y, 0xff);
253 m_winding += node->winding;
261 void QScanConverter::emitSpans(int chunk)
263 for (int dy = 0; dy < CHUNK_SIZE; ++dy) {
268 emitNode(&m_intersections[dy]);
272 // split control points b[0] ... b[3] into
273 // left (b[0] ... b[3]) and right (b[3] ... b[6])
274 static void split(QT_FT_Vector *b)
279 const QT_FT_Pos temp = (b[1].x + b[2].x)/2;
281 b[1].x = (b[0].x + b[1].x)/2;
282 b[5].x = (b[2].x + b[3].x)/2;
283 b[2].x = (b[1].x + temp)/2;
284 b[4].x = (b[5].x + temp)/2;
285 b[3].x = (b[2].x + b[4].x)/2;
288 const QT_FT_Pos temp = (b[1].y + b[2].y)/2;
290 b[1].y = (b[0].y + b[1].y)/2;
291 b[5].y = (b[2].y + b[3].y)/2;
292 b[2].y = (b[1].y + temp)/2;
293 b[4].y = (b[5].y + temp)/2;
294 b[3].y = (b[2].y + b[4].y)/2;
298 static inline bool topOrder(const QScanConverter::Line &a, const QScanConverter::Line &b)
300 return a.top < b.top;
303 static inline bool xOrder(const QScanConverter::Line *a, const QScanConverter::Line *b)
311 inline bool operator()() const
317 // should be a member function but VC6 doesn't support member template functions
318 template <typename T>
319 void qScanConvert(QScanConverter &d, T allVertical)
321 if (!d.m_lines.size()) {
325 qSort(d.m_lines.data(), d.m_lines.data() + d.m_lines.size(), QT_PREPEND_NAMESPACE(topOrder));
327 for (int y = d.m_lines.first().top; y <= d.m_bottom; ++y) {
328 for (; line < d.m_lines.size() && d.m_lines.at(line).top == y; ++line) {
329 // add node to active list
331 QScanConverter::Line *l = &d.m_lines.at(line);
332 d.m_active.resize(d.m_active.size() + 1);
334 for (j = d.m_active.size() - 2; j >= 0 && QT_PREPEND_NAMESPACE(xOrder)(l, d.m_active.at(j)); --j)
335 d.m_active.at(j+1) = d.m_active.at(j);
336 d.m_active.at(j+1) = l;
338 d.m_active << &d.m_lines.at(line);
342 int numActive = d.m_active.size();
343 if (!allVertical()) {
344 // use insertion sort instead of qSort, as the active edge list is quite small
345 // and in the average case already sorted
346 for (int i = 1; i < numActive; ++i) {
347 QScanConverter::Line *l = d.m_active.at(i);
349 for (j = i-1; j >= 0 && QT_PREPEND_NAMESPACE(xOrder)(l, d.m_active.at(j)); --j)
350 d.m_active.at(j+1) = d.m_active.at(j);
351 d.m_active.at(j+1) = l;
357 for (int i = 0; i < numActive; ++i) {
358 QScanConverter::Line *node = d.m_active.at(i);
360 const int current = Q16Dot16ToInt(node->x);
361 if (winding & d.m_fillRuleMask)
362 d.m_spanBuffer->addSpan(x, current - x, y, 0xff);
365 winding += node->winding;
367 if (node->bottom == y) {
368 // remove node from active list
369 for (int j = i; j < numActive - 1; ++j)
370 d.m_active.at(j) = d.m_active.at(j+1);
372 d.m_active.resize(--numActive);
374 } else if (!allVertical())
375 node->x += node->delta;
381 void QScanConverter::end()
383 if (m_lines.isEmpty())
386 if (m_lines.size() <= 32) {
387 bool allVertical = true;
388 for (int i = 0; i < m_lines.size(); ++i) {
389 if (m_lines.at(i).delta) {
395 qScanConvert(*this, QBoolToType<true>());
397 qScanConvert(*this, QBoolToType<false>());
399 for (int chunkTop = m_top; chunkTop <= m_bottom; chunkTop += CHUNK_SIZE) {
402 Intersection isect = { 0, 0, 0, 0 };
404 const int chunkBottom = chunkTop + CHUNK_SIZE;
405 for (int i = 0; i < m_lines.size(); ++i) {
406 Line &line = m_lines.at(i);
408 if ((line.bottom < chunkTop) || (line.top > chunkBottom))
411 const int top = qMax(0, line.top - chunkTop);
412 const int bottom = qMin(CHUNK_SIZE, line.bottom + 1 - chunkTop);
413 allocate(m_size + bottom - top);
415 isect.winding = line.winding;
417 Intersection *it = m_intersections + top;
418 Intersection *end = m_intersections + bottom;
421 for (; it != end; ++it) {
422 isect.x = Q16Dot16ToInt(line.x);
423 line.x += line.delta;
424 mergeIntersection(it, isect);
427 isect.x = Q16Dot16ToInt(line.x);
428 for (; it != end; ++it)
429 mergeIntersection(it, isect);
437 if (m_alloc > 1024) {
438 free(m_intersections);
444 if (m_lines.size() > 1024)
445 m_lines.shrink(1024);
448 inline void QScanConverter::allocate(int size)
450 if (m_alloc < size) {
451 int newAlloc = qMax(size, 2 * m_alloc);
452 m_intersections = q_check_ptr((Intersection *)realloc(m_intersections, newAlloc * sizeof(Intersection)));
457 inline void QScanConverter::mergeIntersection(Intersection *it, const Intersection &isect)
459 Intersection *current = it;
461 while (isect.x != current->x) {
462 int &next = isect.x < current->x ? current->left : current->right;
466 Intersection *last = m_intersections + m_size;
467 next = last - current;
474 current->winding += isect.winding;
477 void QScanConverter::mergeCurve(const QT_FT_Vector &pa, const QT_FT_Vector &pb,
478 const QT_FT_Vector &pc, const QT_FT_Vector &pd)
480 // make room for 32 splits
481 QT_FT_Vector beziers[4 + 3 * 32];
483 QT_FT_Vector *b = beziers;
490 const QT_FT_Pos flatness = 16;
492 while (b >= beziers) {
493 QT_FT_Vector delta = { b[3].x - b[0].x, b[3].y - b[0].y };
494 QT_FT_Pos l = qAbs(delta.x) + qAbs(delta.y);
498 qlonglong d2 = qAbs(qlonglong(b[1].x-b[0].x) * qlonglong(delta.y) -
499 qlonglong(b[1].y-b[0].y) * qlonglong(delta.x));
500 qlonglong d3 = qAbs(qlonglong(b[2].x-b[0].x) * qlonglong(delta.y) -
501 qlonglong(b[2].y-b[0].y) * qlonglong(delta.x));
503 qlonglong d = d2 + d3;
505 belowThreshold = (d <= qlonglong(flatness) * qlonglong(l));
507 QT_FT_Pos d = qAbs(b[0].x-b[1].x) + qAbs(b[0].y-b[1].y) +
508 qAbs(b[0].x-b[2].x) + qAbs(b[0].y-b[2].y);
510 belowThreshold = (d <= flatness);
513 if (belowThreshold || b == beziers + 3 * 32) {
514 mergeLine(b[0], b[3]);
524 inline bool QScanConverter::clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding)
526 bool right = edgeFP == m_rightFP;
529 if ((slopeFP > 0) ^ right)
532 Line line = { edgeFP, 0, iTop, iBottom, winding };
538 Q16Dot16 lastFP = xFP + slopeFP * (iBottom - iTop);
540 if (lastFP == edgeFP) {
541 if ((slopeFP < 0) ^ right)
544 Line line = { edgeFP, 0, iTop, iBottom, winding };
550 // does line cross edge?
551 if ((lastFP < edgeFP) ^ (xFP < edgeFP)) {
552 Q16Dot16 deltaY = Q16Dot16((edgeFP - xFP) / Q16Dot16ToFloat(slopeFP));
554 if ((xFP < edgeFP) ^ right) {
555 // top segment needs to be clipped
556 int iHeight = Q16Dot16ToInt(deltaY + 1);
557 int iMiddle = iTop + iHeight;
559 Line line = { edgeFP, 0, iTop, iMiddle, winding };
562 if (iMiddle != iBottom) {
563 xFP += slopeFP * (iHeight + 1);
568 // bottom segment needs to be clipped
569 int iHeight = Q16Dot16ToInt(deltaY);
570 int iMiddle = iTop + iHeight;
572 if (iMiddle != iBottom) {
573 Line line = { edgeFP, 0, iMiddle + 1, iBottom, winding };
580 } else if ((xFP < edgeFP) ^ right) {
581 Line line = { edgeFP, 0, iTop, iBottom, winding };
589 void QScanConverter::mergeLine(QT_FT_Vector a, QT_FT_Vector b)
603 int iTop = qMax(m_top, int((a.y + 32 - COORD_ROUNDING) >> 6));
604 int iBottom = qMin(m_bottom, int((b.y - 32 - COORD_ROUNDING) >> 6));
606 if (iTop <= iBottom) {
607 Q16Dot16 aFP = Q16Dot16Factor/2 + (a.x << 10) - COORD_ROUNDING;
610 Line line = { qBound(m_leftFP, aFP, m_rightFP), 0, iTop, iBottom, winding };
613 const qreal slope = (b.x - a.x) / qreal(b.y - a.y);
615 const Q16Dot16 slopeFP = FloatToQ16Dot16(slope);
617 Q16Dot16 xFP = aFP + Q16Dot16Multiply(slopeFP,
619 + Q16Dot16Factor/2 - (a.y << 10));
621 if (clip(xFP, iTop, iBottom, slopeFP, m_leftFP, winding))
624 if (clip(xFP, iTop, iBottom, slopeFP, m_rightFP, winding))
627 Q_ASSERT(xFP >= m_leftFP);
629 Line line = { xFP, slopeFP, iTop, iBottom, winding };
635 QRasterizer::QRasterizer()
636 : d(new QRasterizerPrivate)
640 QRasterizer::~QRasterizer()
645 void QRasterizer::setAntialiased(bool antialiased)
647 d->antialiased = antialiased;
650 void QRasterizer::initialize(ProcessSpans blend, void *data)
656 void QRasterizer::setClipRect(const QRect &clipRect)
658 d->clipRect = clipRect;
661 static Q16Dot16 intersectPixelFP(int x, Q16Dot16 top, Q16Dot16 bottom, Q16Dot16 leftIntersectX, Q16Dot16 rightIntersectX, Q16Dot16 slope, Q16Dot16 invSlope)
663 Q16Dot16 leftX = IntToQ16Dot16(x);
664 Q16Dot16 rightX = IntToQ16Dot16(x) + Q16Dot16Factor;
666 Q16Dot16 leftIntersectY, rightIntersectY;
668 leftIntersectY = top + Q16Dot16Multiply(leftX - leftIntersectX, invSlope);
669 rightIntersectY = leftIntersectY + invSlope;
671 leftIntersectY = top + Q16Dot16Multiply(leftX - rightIntersectX, invSlope);
672 rightIntersectY = leftIntersectY + invSlope;
675 if (leftIntersectX >= leftX && rightIntersectX <= rightX) {
676 return Q16Dot16Multiply(bottom - top, leftIntersectX - leftX + ((rightIntersectX - leftIntersectX) >> 1));
677 } else if (leftIntersectX >= rightX) {
679 } else if (leftIntersectX >= leftX) {
681 return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, rightIntersectY - top);
683 return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, bottom - rightIntersectY);
685 } else if (rightIntersectX <= leftX) {
687 } else if (rightIntersectX <= rightX) {
689 return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, bottom - leftIntersectY);
691 return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, leftIntersectY - top);
695 return (bottom - rightIntersectY) + ((rightIntersectY - leftIntersectY) >> 1);
697 return (rightIntersectY - top) + ((leftIntersectY - rightIntersectY) >> 1);
702 static inline bool q26Dot6Compare(qreal p1, qreal p2)
704 return int((p2 - p1) * qreal(64.)) == 0;
707 static inline qreal qFloorF(qreal v)
709 #ifdef QT_USE_MATH_H_FLOATS
710 if (sizeof(qreal) == sizeof(float))
717 static inline QPointF snapTo26Dot6Grid(const QPointF &p)
719 return QPointF(qFloorF(p.x() * 64) * (1 / qreal(64)),
720 qFloorF(p.y() * 64) * (1 / qreal(64)));
724 The rasterize line function relies on some div by zero which should
725 result in +/-inf values. However, when floating point exceptions are
726 enabled, this will cause crashes, so we return high numbers instead.
727 As the returned value is used in further arithmetic, returning
728 FLT_MAX/DBL_MAX will also cause values, so instead return a value
729 that is well outside the int-range.
731 static inline qreal qSafeDivide(qreal x, qreal y)
734 return x > 0 ? 1e20 : -1e20;
738 /* Conversion to int fails if the value is too large to fit into INT_MAX or
739 too small to fit into INT_MIN, so we need this slightly safer conversion
740 when floating point exceptions are enabled
742 static inline int qSafeFloatToQ16Dot16(qreal x)
744 qreal tmp = x * 65536.;
745 if (tmp > qreal(INT_MAX))
747 else if (tmp < qreal(INT_MIN))
752 void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap)
754 if (a == b || width == 0 || d->clipRect.isEmpty())
757 Q_ASSERT(width > 0.0);
763 QPointF delta = pb - pa;
764 pa -= (0.5f * width) * delta;
765 pb += (0.5f * width) * delta;
768 QPointF offs = QPointF(qAbs(b.y() - a.y()), qAbs(b.x() - a.x())) * width * 0.5;
769 const QRectF clip(d->clipRect.topLeft() - offs, d->clipRect.bottomRight() + QPoint(1, 1) + offs);
771 if (!clip.contains(pa) || !clip.contains(pb)) {
775 const qreal o[2] = { pa.x(), pa.y() };
776 const qreal d[2] = { pb.x() - pa.x(), pb.y() - pa.y() };
778 const qreal low[2] = { clip.left(), clip.top() };
779 const qreal high[2] = { clip.right(), clip.bottom() };
781 for (int i = 0; i < 2; ++i) {
783 if (o[i] <= low[i] || o[i] >= high[i])
787 const qreal d_inv = 1 / d[i];
788 qreal t_low = (low[i] - o[i]) * d_inv;
789 qreal t_high = (high[i] - o[i]) * d_inv;
791 qSwap(t_low, t_high);
800 QPointF npa = pa + (pb - pa) * t1;
801 QPointF npb = pa + (pb - pa) * t2;
807 if (!d->antialiased) {
808 pa.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
809 pa.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
810 pb.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
811 pb.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
816 const QPointF d0 = a - b;
817 const qreal w0 = d0.x() * d0.x() + d0.y() * d0.y();
820 const QPointF d = pa - pb;
821 const qreal w = d.x() * d.x() + d.y() * d.y();
826 // adjust width which is given relative to |b - a|
827 width *= sqrt(w0 / w);
830 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
832 if (q26Dot6Compare(pa.y(), pb.y())) {
833 const qreal x = (pa.x() + pb.x()) * 0.5f;
834 const qreal dx = qAbs(pb.x() - pa.x()) * 0.5f;
836 const qreal y = pa.y();
837 const qreal dy = width * dx;
839 pa = QPointF(x, y - dy);
840 pb = QPointF(x, y + dy);
845 if (q26Dot6Compare(pa.x(), pb.x())) {
849 const qreal dy = pb.y() - pa.y();
850 const qreal halfWidth = 0.5f * width * dy;
852 qreal left = pa.x() - halfWidth;
853 qreal right = pa.x() + halfWidth;
855 left = qBound(qreal(d->clipRect.left()), left, qreal(d->clipRect.right() + 1));
856 right = qBound(qreal(d->clipRect.left()), right, qreal(d->clipRect.right() + 1));
858 pa.ry() = qBound(qreal(d->clipRect.top()), pa.y(), qreal(d->clipRect.bottom() + 1));
859 pb.ry() = qBound(qreal(d->clipRect.top()), pb.y(), qreal(d->clipRect.bottom() + 1));
861 if (q26Dot6Compare(left, right) || q26Dot6Compare(pa.y(), pb.y()))
864 if (d->antialiased) {
865 const Q16Dot16 iLeft = int(left);
866 const Q16Dot16 iRight = int(right);
867 const Q16Dot16 leftWidth = IntToQ16Dot16(iLeft + 1)
868 - FloatToQ16Dot16(left);
869 const Q16Dot16 rightWidth = FloatToQ16Dot16(right)
870 - IntToQ16Dot16(iRight);
872 Q16Dot16 coverage[3];
877 if (iLeft == iRight) {
878 coverage[0] = (leftWidth + rightWidth) * 255;
882 coverage[0] = leftWidth * 255;
885 if (leftWidth == Q16Dot16Factor) {
886 len[0] = iRight - iLeft;
887 } else if (iRight - iLeft > 1) {
888 coverage[1] = IntToQ16Dot16(255);
890 len[1] = iRight - iLeft - 1;
894 coverage[n] = rightWidth * 255;
901 const Q16Dot16 iTopFP = IntToQ16Dot16(int(pa.y()));
902 const Q16Dot16 iBottomFP = IntToQ16Dot16(int(pb.y()));
903 const Q16Dot16 yPa = FloatToQ16Dot16(pa.y());
904 const Q16Dot16 yPb = FloatToQ16Dot16(pb.y());
905 for (Q16Dot16 yFP = iTopFP; yFP <= iBottomFP; yFP += Q16Dot16Factor) {
906 const Q16Dot16 rowHeight = qMin(yFP + Q16Dot16Factor, yPb)
908 const int y = Q16Dot16ToInt(yFP);
909 if (y > d->clipRect.bottom())
911 for (int i = 0; i < n; ++i) {
912 buffer.addSpan(x[i], len[i], y,
913 Q16Dot16ToInt(Q16Dot16Multiply(rowHeight, coverage[i])));
917 int iTop = int(pa.y() + 0.5f);
918 int iBottom = pb.y() < 0.5f ? -1 : int(pb.y() - 0.5f);
919 int iLeft = int(left + 0.5f);
920 int iRight = right < 0.5f ? -1 : int(right - 0.5f);
922 int iWidth = iRight - iLeft + 1;
923 for (int y = iTop; y <= iBottom; ++y)
924 buffer.addSpan(iLeft, iWidth, y, 255);
930 QPointF delta = pb - pa;
931 delta *= 0.5f * width;
932 const QPointF perp(delta.y(), -delta.x());
939 if (pa.x() < pb.x()) {
951 top = snapTo26Dot6Grid(top);
952 bottom = snapTo26Dot6Grid(bottom);
953 left = snapTo26Dot6Grid(left);
954 right = snapTo26Dot6Grid(right);
956 const qreal topBound = qBound(qreal(d->clipRect.top()), top.y(), qreal(d->clipRect.bottom()));
957 const qreal bottomBound = qBound(qreal(d->clipRect.top()), bottom.y(), qreal(d->clipRect.bottom()));
959 const QPointF topLeftEdge = left - top;
960 const QPointF topRightEdge = right - top;
961 const QPointF bottomLeftEdge = bottom - left;
962 const QPointF bottomRightEdge = bottom - right;
964 const qreal topLeftSlope = qSafeDivide(topLeftEdge.x(), topLeftEdge.y());
965 const qreal bottomLeftSlope = qSafeDivide(bottomLeftEdge.x(), bottomLeftEdge.y());
967 const qreal topRightSlope = qSafeDivide(topRightEdge.x(), topRightEdge.y());
968 const qreal bottomRightSlope = qSafeDivide(bottomRightEdge.x(), bottomRightEdge.y());
970 const Q16Dot16 topLeftSlopeFP = qSafeFloatToQ16Dot16(topLeftSlope);
971 const Q16Dot16 topRightSlopeFP = qSafeFloatToQ16Dot16(topRightSlope);
973 const Q16Dot16 bottomLeftSlopeFP = qSafeFloatToQ16Dot16(bottomLeftSlope);
974 const Q16Dot16 bottomRightSlopeFP = qSafeFloatToQ16Dot16(bottomRightSlope);
976 const Q16Dot16 invTopLeftSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, topLeftSlope));
977 const Q16Dot16 invTopRightSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, topRightSlope));
979 const Q16Dot16 invBottomLeftSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, bottomLeftSlope));
980 const Q16Dot16 invBottomRightSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, bottomRightSlope));
982 if (d->antialiased) {
983 const Q16Dot16 iTopFP = IntToQ16Dot16(int(topBound));
984 const Q16Dot16 iLeftFP = IntToQ16Dot16(int(left.y()));
985 const Q16Dot16 iRightFP = IntToQ16Dot16(int(right.y()));
986 const Q16Dot16 iBottomFP = IntToQ16Dot16(int(bottomBound));
988 Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topLeftSlope);
989 Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topRightSlope);
990 Q16Dot16 leftIntersectBf = 0;
991 Q16Dot16 rightIntersectBf = 0;
993 if (iLeftFP < iTopFP)
994 leftIntersectBf = FloatToQ16Dot16(left.x() + (int(topBound) - left.y()) * bottomLeftSlope);
996 if (iRightFP < iTopFP)
997 rightIntersectBf = FloatToQ16Dot16(right.x() + (int(topBound) - right.y()) * bottomRightSlope);
999 Q16Dot16 rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom;
1000 Q16Dot16 topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf;
1001 Q16Dot16 bottomLeftIntersectAf, bottomLeftIntersectBf, bottomRightIntersectAf, bottomRightIntersectBf;
1003 int leftMin, leftMax, rightMin, rightMax;
1005 const Q16Dot16 yTopFP = FloatToQ16Dot16(top.y());
1006 const Q16Dot16 yLeftFP = FloatToQ16Dot16(left.y());
1007 const Q16Dot16 yRightFP = FloatToQ16Dot16(right.y());
1008 const Q16Dot16 yBottomFP = FloatToQ16Dot16(bottom.y());
1010 rowTop = qMax(iTopFP, yTopFP);
1011 topLeftIntersectAf = leftIntersectAf +
1012 Q16Dot16Multiply(topLeftSlopeFP, rowTop - iTopFP);
1013 topRightIntersectAf = rightIntersectAf +
1014 Q16Dot16Multiply(topRightSlopeFP, rowTop - iTopFP);
1016 Q16Dot16 yFP = iTopFP;
1017 while (yFP <= iBottomFP) {
1018 rowBottomLeft = qMin(yFP + Q16Dot16Factor, yLeftFP);
1019 rowBottomRight = qMin(yFP + Q16Dot16Factor, yRightFP);
1020 rowTopLeft = qMax(yFP, yLeftFP);
1021 rowTopRight = qMax(yFP, yRightFP);
1022 rowBottom = qMin(yFP + Q16Dot16Factor, yBottomFP);
1024 if (yFP == iLeftFP) {
1025 const int y = Q16Dot16ToInt(yFP);
1026 leftIntersectBf = FloatToQ16Dot16(left.x() + (y - left.y()) * bottomLeftSlope);
1027 topLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowTopLeft - yFP);
1028 bottomLeftIntersectAf = leftIntersectAf + Q16Dot16Multiply(topLeftSlopeFP, rowBottomLeft - yFP);
1030 topLeftIntersectBf = leftIntersectBf;
1031 bottomLeftIntersectAf = leftIntersectAf + topLeftSlopeFP;
1034 if (yFP == iRightFP) {
1035 const int y = Q16Dot16ToInt(yFP);
1036 rightIntersectBf = FloatToQ16Dot16(right.x() + (y - right.y()) * bottomRightSlope);
1037 topRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowTopRight - yFP);
1038 bottomRightIntersectAf = rightIntersectAf + Q16Dot16Multiply(topRightSlopeFP, rowBottomRight - yFP);
1040 topRightIntersectBf = rightIntersectBf;
1041 bottomRightIntersectAf = rightIntersectAf + topRightSlopeFP;
1044 if (yFP == iBottomFP) {
1045 bottomLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowBottom - yFP);
1046 bottomRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowBottom - yFP);
1048 bottomLeftIntersectBf = leftIntersectBf + bottomLeftSlopeFP;
1049 bottomRightIntersectBf = rightIntersectBf + bottomRightSlopeFP;
1052 if (yFP < iLeftFP) {
1053 leftMin = Q16Dot16ToInt(bottomLeftIntersectAf);
1054 leftMax = Q16Dot16ToInt(topLeftIntersectAf);
1055 } else if (yFP == iLeftFP) {
1056 leftMin = Q16Dot16ToInt(qMax(bottomLeftIntersectAf, topLeftIntersectBf));
1057 leftMax = Q16Dot16ToInt(qMax(topLeftIntersectAf, bottomLeftIntersectBf));
1059 leftMin = Q16Dot16ToInt(topLeftIntersectBf);
1060 leftMax = Q16Dot16ToInt(bottomLeftIntersectBf);
1063 leftMin = qBound(d->clipRect.left(), leftMin, d->clipRect.right());
1064 leftMax = qBound(d->clipRect.left(), leftMax, d->clipRect.right());
1066 if (yFP < iRightFP) {
1067 rightMin = Q16Dot16ToInt(topRightIntersectAf);
1068 rightMax = Q16Dot16ToInt(bottomRightIntersectAf);
1069 } else if (yFP == iRightFP) {
1070 rightMin = Q16Dot16ToInt(qMin(topRightIntersectAf, bottomRightIntersectBf));
1071 rightMax = Q16Dot16ToInt(qMin(bottomRightIntersectAf, topRightIntersectBf));
1073 rightMin = Q16Dot16ToInt(bottomRightIntersectBf);
1074 rightMax = Q16Dot16ToInt(topRightIntersectBf);
1077 rightMin = qBound(d->clipRect.left(), rightMin, d->clipRect.right());
1078 rightMax = qBound(d->clipRect.left(), rightMax, d->clipRect.right());
1080 if (leftMax > rightMax)
1082 if (rightMin < leftMin)
1085 Q16Dot16 rowHeight = rowBottom - rowTop;
1088 while (x <= leftMax) {
1089 Q16Dot16 excluded = 0;
1092 excluded += intersectPixelFP(x, rowTop, rowBottomLeft,
1093 bottomLeftIntersectAf, topLeftIntersectAf,
1094 topLeftSlopeFP, invTopLeftSlopeFP);
1096 excluded += intersectPixelFP(x, rowTopLeft, rowBottom,
1097 topLeftIntersectBf, bottomLeftIntersectBf,
1098 bottomLeftSlopeFP, invBottomLeftSlopeFP);
1100 if (x >= rightMin) {
1101 if (yFP <= iRightFP)
1102 excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
1103 topRightIntersectAf, bottomRightIntersectAf,
1104 topRightSlopeFP, invTopRightSlopeFP);
1105 if (yFP >= iRightFP)
1106 excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
1107 bottomRightIntersectBf, topRightIntersectBf,
1108 bottomRightSlopeFP, invBottomRightSlopeFP);
1111 Q16Dot16 coverage = rowHeight - excluded;
1112 buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
1113 Q16Dot16ToInt(255 * coverage));
1117 buffer.addSpan(x, rightMin - x, Q16Dot16ToInt(yFP),
1118 Q16Dot16ToInt(255 * rowHeight));
1121 while (x <= rightMax) {
1122 Q16Dot16 excluded = 0;
1123 if (yFP <= iRightFP)
1124 excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
1125 topRightIntersectAf, bottomRightIntersectAf,
1126 topRightSlopeFP, invTopRightSlopeFP);
1127 if (yFP >= iRightFP)
1128 excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
1129 bottomRightIntersectBf, topRightIntersectBf,
1130 bottomRightSlopeFP, invBottomRightSlopeFP);
1132 Q16Dot16 coverage = rowHeight - excluded;
1133 buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
1134 Q16Dot16ToInt(255 * coverage));
1138 leftIntersectAf += topLeftSlopeFP;
1139 leftIntersectBf += bottomLeftSlopeFP;
1140 rightIntersectAf += topRightSlopeFP;
1141 rightIntersectBf += bottomRightSlopeFP;
1142 topLeftIntersectAf = leftIntersectAf;
1143 topRightIntersectAf = rightIntersectAf;
1145 yFP += Q16Dot16Factor;
1149 int iTop = int(top.y() + 0.5f);
1150 int iLeft = left.y() < 0.5f ? -1 : int(left.y() - 0.5f);
1151 int iRight = right.y() < 0.5f ? -1 : int(right.y() - 0.5f);
1152 int iBottom = bottom.y() < 0.5f? -1 : int(bottom.y() - 0.5f);
1153 int iMiddle = qMin(iLeft, iRight);
1155 Q16Dot16 leftIntersectAf = qSafeFloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope);
1156 Q16Dot16 leftIntersectBf = qSafeFloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope);
1157 Q16Dot16 rightIntersectAf = qSafeFloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope);
1158 Q16Dot16 rightIntersectBf = qSafeFloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope);
1162 #define DO_SEGMENT(next, li, ri, ls, rs) \
1163 ny = qMin(next + 1, d->clipRect.top()); \
1165 li += ls * (ny - y); \
1166 ri += rs * (ny - y); \
1169 if (next > d->clipRect.bottom()) \
1170 next = d->clipRect.bottom(); \
1171 for (; y <= next; ++y) { \
1172 const int x1 = qMax(Q16Dot16ToInt(li), d->clipRect.left()); \
1173 const int x2 = qMin(Q16Dot16ToInt(ri), d->clipRect.right()); \
1175 buffer.addSpan(x1, x2 - x1 + 1, y, 255); \
1180 DO_SEGMENT(iMiddle, leftIntersectAf, rightIntersectAf, topLeftSlopeFP, topRightSlopeFP)
1181 DO_SEGMENT(iRight, leftIntersectBf, rightIntersectAf, bottomLeftSlopeFP, topRightSlopeFP)
1182 DO_SEGMENT(iLeft, leftIntersectAf, rightIntersectBf, topLeftSlopeFP, bottomRightSlopeFP);
1183 DO_SEGMENT(iBottom, leftIntersectBf, rightIntersectBf, bottomLeftSlopeFP, bottomRightSlopeFP);
1189 void QRasterizer::rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule)
1191 if (outline->n_points < 3 || outline->n_contours == 0)
1194 const QT_FT_Vector *points = outline->points;
1196 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
1198 // ### QT_FT_Outline already has a bounding rect which is
1199 // ### precomputed at this point, so we should probably just be
1200 // ### using that instead...
1201 QT_FT_Pos min_y = points[0].y, max_y = points[0].y;
1202 for (int i = 1; i < outline->n_points; ++i) {
1203 const QT_FT_Vector &p = points[i];
1204 min_y = qMin(p.y, min_y);
1205 max_y = qMax(p.y, max_y);
1208 int iTopBound = qMax(d->clipRect.top(), int((min_y + 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
1209 int iBottomBound = qMin(d->clipRect.bottom(), int((max_y - 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
1211 if (iTopBound > iBottomBound)
1214 d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
1217 for (int i = 0; i < outline->n_contours; ++i) {
1218 const int last = outline->contours[i];
1219 for (int j = first; j < last; ++j) {
1220 if (outline->tags[j+1] == QT_FT_CURVE_TAG_CUBIC) {
1221 Q_ASSERT(outline->tags[j+2] == QT_FT_CURVE_TAG_CUBIC);
1222 d->scanConverter.mergeCurve(points[j], points[j+1], points[j+2], points[j+3]);
1225 d->scanConverter.mergeLine(points[j], points[j+1]);
1232 d->scanConverter.end();
1235 void QRasterizer::rasterize(const QPainterPath &path, Qt::FillRule fillRule)
1240 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
1242 QRectF bounds = path.controlPointRect();
1244 int iTopBound = qMax(d->clipRect.top(), int(bounds.top() + qreal(0.5) + (COORD_OFFSET - COORD_ROUNDING)/qreal(64.)));
1245 int iBottomBound = qMin(d->clipRect.bottom(), int(bounds.bottom() - qreal(0.5) + (COORD_OFFSET - COORD_ROUNDING)/qreal(64.)));
1247 if (iTopBound > iBottomBound)
1250 d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
1252 int subpathStart = 0;
1253 QT_FT_Vector last = { 0, 0 };
1254 for (int i = 0; i < path.elementCount(); ++i) {
1255 switch (path.elementAt(i).type) {
1256 case QPainterPath::LineToElement:
1258 QT_FT_Vector p1 = last;
1259 QT_FT_Vector p2 = PointToVector(path.elementAt(i));
1260 d->scanConverter.mergeLine(p1, p2);
1264 case QPainterPath::MoveToElement:
1267 QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
1268 // close previous subpath
1269 if (first.x != last.x || first.y != last.y)
1270 d->scanConverter.mergeLine(last, first);
1273 last = PointToVector(path.elementAt(i));
1276 case QPainterPath::CurveToElement:
1278 QT_FT_Vector p1 = last;
1279 QT_FT_Vector p2 = PointToVector(path.elementAt(i));
1280 QT_FT_Vector p3 = PointToVector(path.elementAt(++i));
1281 QT_FT_Vector p4 = PointToVector(path.elementAt(++i));
1282 d->scanConverter.mergeCurve(p1, p2, p3, p4);
1292 QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
1295 if (first.x != last.x || first.y != last.y)
1296 d->scanConverter.mergeLine(last, first);
1298 d->scanConverter.end();