1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
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)/65536.)
55 #define FloatToQ16Dot16(i) (int)((i) * 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) * 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)));
723 void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap)
725 if (a == b || width == 0 || d->clipRect.isEmpty())
728 Q_ASSERT(width > 0.0);
734 QPointF delta = pb - pa;
735 pa -= (0.5f * width) * delta;
736 pb += (0.5f * width) * delta;
739 QPointF offs = QPointF(qAbs(b.y() - a.y()), qAbs(b.x() - a.x())) * width * 0.5;
740 const QRectF clip(d->clipRect.topLeft() - offs, d->clipRect.bottomRight() + QPoint(1, 1) + offs);
742 if (!clip.contains(pa) || !clip.contains(pb)) {
746 const qreal o[2] = { pa.x(), pa.y() };
747 const qreal d[2] = { pb.x() - pa.x(), pb.y() - pa.y() };
749 const qreal low[2] = { clip.left(), clip.top() };
750 const qreal high[2] = { clip.right(), clip.bottom() };
752 for (int i = 0; i < 2; ++i) {
754 if (o[i] <= low[i] || o[i] >= high[i])
758 const qreal d_inv = 1 / d[i];
759 qreal t_low = (low[i] - o[i]) * d_inv;
760 qreal t_high = (high[i] - o[i]) * d_inv;
762 qSwap(t_low, t_high);
771 QPointF npa = pa + (pb - pa) * t1;
772 QPointF npb = pa + (pb - pa) * t2;
778 if (!d->antialiased) {
779 pa.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
780 pa.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
781 pb.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
782 pb.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
787 const QPointF d0 = a - b;
788 const qreal w0 = d0.x() * d0.x() + d0.y() * d0.y();
791 const QPointF d = pa - pb;
792 const qreal w = d.x() * d.x() + d.y() * d.y();
797 // adjust width which is given relative to |b - a|
798 width *= sqrt(w0 / w);
801 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
803 if (q26Dot6Compare(pa.y(), pb.y())) {
804 const qreal x = (pa.x() + pb.x()) * 0.5f;
805 const qreal dx = qAbs(pb.x() - pa.x()) * 0.5f;
807 const qreal y = pa.y();
808 const qreal dy = width * dx;
810 pa = QPointF(x, y - dy);
811 pb = QPointF(x, y + dy);
816 if (q26Dot6Compare(pa.x(), pb.x())) {
820 const qreal dy = pb.y() - pa.y();
821 const qreal halfWidth = 0.5f * width * dy;
823 qreal left = pa.x() - halfWidth;
824 qreal right = pa.x() + halfWidth;
826 left = qBound(qreal(d->clipRect.left()), left, qreal(d->clipRect.right() + 1));
827 right = qBound(qreal(d->clipRect.left()), right, qreal(d->clipRect.right() + 1));
829 pa.ry() = qBound(qreal(d->clipRect.top()), pa.y(), qreal(d->clipRect.bottom() + 1));
830 pb.ry() = qBound(qreal(d->clipRect.top()), pb.y(), qreal(d->clipRect.bottom() + 1));
832 if (q26Dot6Compare(left, right) || q26Dot6Compare(pa.y(), pb.y()))
835 if (d->antialiased) {
836 const Q16Dot16 iLeft = int(left);
837 const Q16Dot16 iRight = int(right);
838 const Q16Dot16 leftWidth = IntToQ16Dot16(iLeft + 1)
839 - FloatToQ16Dot16(left);
840 const Q16Dot16 rightWidth = FloatToQ16Dot16(right)
841 - IntToQ16Dot16(iRight);
843 Q16Dot16 coverage[3];
848 if (iLeft == iRight) {
849 coverage[0] = (leftWidth + rightWidth) * 255;
853 coverage[0] = leftWidth * 255;
856 if (leftWidth == Q16Dot16Factor) {
857 len[0] = iRight - iLeft;
858 } else if (iRight - iLeft > 1) {
859 coverage[1] = IntToQ16Dot16(255);
861 len[1] = iRight - iLeft - 1;
865 coverage[n] = rightWidth * 255;
872 const Q16Dot16 iTopFP = IntToQ16Dot16(int(pa.y()));
873 const Q16Dot16 iBottomFP = IntToQ16Dot16(int(pb.y()));
874 const Q16Dot16 yPa = FloatToQ16Dot16(pa.y());
875 const Q16Dot16 yPb = FloatToQ16Dot16(pb.y());
876 for (Q16Dot16 yFP = iTopFP; yFP <= iBottomFP; yFP += Q16Dot16Factor) {
877 const Q16Dot16 rowHeight = qMin(yFP + Q16Dot16Factor, yPb)
879 const int y = Q16Dot16ToInt(yFP);
880 for (int i = 0; i < n; ++i) {
881 buffer.addSpan(x[i], len[i], y,
882 Q16Dot16ToInt(Q16Dot16Multiply(rowHeight, coverage[i])));
886 int iTop = int(pa.y() + 0.5f);
887 int iBottom = pb.y() < 0.5f ? -1 : int(pb.y() - 0.5f);
888 int iLeft = int(left + 0.5f);
889 int iRight = right < 0.5f ? -1 : int(right - 0.5f);
891 int iWidth = iRight - iLeft + 1;
892 for (int y = iTop; y <= iBottom; ++y)
893 buffer.addSpan(iLeft, iWidth, y, 255);
899 QPointF delta = pb - pa;
900 delta *= 0.5f * width;
901 const QPointF perp(delta.y(), -delta.x());
908 if (pa.x() < pb.x()) {
920 top = snapTo26Dot6Grid(top);
921 bottom = snapTo26Dot6Grid(bottom);
922 left = snapTo26Dot6Grid(left);
923 right = snapTo26Dot6Grid(right);
925 const qreal topBound = qBound(qreal(d->clipRect.top()), top.y(), qreal(d->clipRect.bottom()));
926 const qreal bottomBound = qBound(qreal(d->clipRect.top()), bottom.y(), qreal(d->clipRect.bottom()));
928 const QPointF topLeftEdge = left - top;
929 const QPointF topRightEdge = right - top;
930 const QPointF bottomLeftEdge = bottom - left;
931 const QPointF bottomRightEdge = bottom - right;
933 const qreal topLeftSlope = topLeftEdge.x() / topLeftEdge.y();
934 const qreal bottomLeftSlope = bottomLeftEdge.x() / bottomLeftEdge.y();
936 const qreal topRightSlope = topRightEdge.x() / topRightEdge.y();
937 const qreal bottomRightSlope = bottomRightEdge.x() / bottomRightEdge.y();
939 const Q16Dot16 topLeftSlopeFP = FloatToQ16Dot16(topLeftSlope);
940 const Q16Dot16 topRightSlopeFP = FloatToQ16Dot16(topRightSlope);
942 const Q16Dot16 bottomLeftSlopeFP = FloatToQ16Dot16(bottomLeftSlope);
943 const Q16Dot16 bottomRightSlopeFP = FloatToQ16Dot16(bottomRightSlope);
945 const Q16Dot16 invTopLeftSlopeFP = FloatToQ16Dot16(1 / topLeftSlope);
946 const Q16Dot16 invTopRightSlopeFP = FloatToQ16Dot16(1 / topRightSlope);
948 const Q16Dot16 invBottomLeftSlopeFP = FloatToQ16Dot16(1 / bottomLeftSlope);
949 const Q16Dot16 invBottomRightSlopeFP = FloatToQ16Dot16(1 / bottomRightSlope);
951 if (d->antialiased) {
952 const Q16Dot16 iTopFP = IntToQ16Dot16(int(topBound));
953 const Q16Dot16 iLeftFP = IntToQ16Dot16(int(left.y()));
954 const Q16Dot16 iRightFP = IntToQ16Dot16(int(right.y()));
955 const Q16Dot16 iBottomFP = IntToQ16Dot16(int(bottomBound));
957 Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topLeftSlope);
958 Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topRightSlope);
959 Q16Dot16 leftIntersectBf = 0;
960 Q16Dot16 rightIntersectBf = 0;
962 if (iLeftFP < iTopFP)
963 leftIntersectBf = FloatToQ16Dot16(left.x() + (int(topBound) - left.y()) * bottomLeftSlope);
965 if (iRightFP < iTopFP)
966 rightIntersectBf = FloatToQ16Dot16(right.x() + (int(topBound) - right.y()) * bottomRightSlope);
968 Q16Dot16 rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom;
969 Q16Dot16 topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf;
970 Q16Dot16 bottomLeftIntersectAf, bottomLeftIntersectBf, bottomRightIntersectAf, bottomRightIntersectBf;
972 int leftMin, leftMax, rightMin, rightMax;
974 const Q16Dot16 yTopFP = FloatToQ16Dot16(top.y());
975 const Q16Dot16 yLeftFP = FloatToQ16Dot16(left.y());
976 const Q16Dot16 yRightFP = FloatToQ16Dot16(right.y());
977 const Q16Dot16 yBottomFP = FloatToQ16Dot16(bottom.y());
979 rowTop = qMax(iTopFP, yTopFP);
980 topLeftIntersectAf = leftIntersectAf +
981 Q16Dot16Multiply(topLeftSlopeFP, rowTop - iTopFP);
982 topRightIntersectAf = rightIntersectAf +
983 Q16Dot16Multiply(topRightSlopeFP, rowTop - iTopFP);
985 Q16Dot16 yFP = iTopFP;
986 while (yFP <= iBottomFP) {
987 rowBottomLeft = qMin(yFP + Q16Dot16Factor, yLeftFP);
988 rowBottomRight = qMin(yFP + Q16Dot16Factor, yRightFP);
989 rowTopLeft = qMax(yFP, yLeftFP);
990 rowTopRight = qMax(yFP, yRightFP);
991 rowBottom = qMin(yFP + Q16Dot16Factor, yBottomFP);
993 if (yFP == iLeftFP) {
994 const int y = Q16Dot16ToInt(yFP);
995 leftIntersectBf = FloatToQ16Dot16(left.x() + (y - left.y()) * bottomLeftSlope);
996 topLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowTopLeft - yFP);
997 bottomLeftIntersectAf = leftIntersectAf + Q16Dot16Multiply(topLeftSlopeFP, rowBottomLeft - yFP);
999 topLeftIntersectBf = leftIntersectBf;
1000 bottomLeftIntersectAf = leftIntersectAf + topLeftSlopeFP;
1003 if (yFP == iRightFP) {
1004 const int y = Q16Dot16ToInt(yFP);
1005 rightIntersectBf = FloatToQ16Dot16(right.x() + (y - right.y()) * bottomRightSlope);
1006 topRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowTopRight - yFP);
1007 bottomRightIntersectAf = rightIntersectAf + Q16Dot16Multiply(topRightSlopeFP, rowBottomRight - yFP);
1009 topRightIntersectBf = rightIntersectBf;
1010 bottomRightIntersectAf = rightIntersectAf + topRightSlopeFP;
1013 if (yFP == iBottomFP) {
1014 bottomLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowBottom - yFP);
1015 bottomRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowBottom - yFP);
1017 bottomLeftIntersectBf = leftIntersectBf + bottomLeftSlopeFP;
1018 bottomRightIntersectBf = rightIntersectBf + bottomRightSlopeFP;
1021 if (yFP < iLeftFP) {
1022 leftMin = Q16Dot16ToInt(bottomLeftIntersectAf);
1023 leftMax = Q16Dot16ToInt(topLeftIntersectAf);
1024 } else if (yFP == iLeftFP) {
1025 leftMin = Q16Dot16ToInt(qMax(bottomLeftIntersectAf, topLeftIntersectBf));
1026 leftMax = Q16Dot16ToInt(qMax(topLeftIntersectAf, bottomLeftIntersectBf));
1028 leftMin = Q16Dot16ToInt(topLeftIntersectBf);
1029 leftMax = Q16Dot16ToInt(bottomLeftIntersectBf);
1032 leftMin = qBound(d->clipRect.left(), leftMin, d->clipRect.right());
1033 leftMax = qBound(d->clipRect.left(), leftMax, d->clipRect.right());
1035 if (yFP < iRightFP) {
1036 rightMin = Q16Dot16ToInt(topRightIntersectAf);
1037 rightMax = Q16Dot16ToInt(bottomRightIntersectAf);
1038 } else if (yFP == iRightFP) {
1039 rightMin = Q16Dot16ToInt(qMin(topRightIntersectAf, bottomRightIntersectBf));
1040 rightMax = Q16Dot16ToInt(qMin(bottomRightIntersectAf, topRightIntersectBf));
1042 rightMin = Q16Dot16ToInt(bottomRightIntersectBf);
1043 rightMax = Q16Dot16ToInt(topRightIntersectBf);
1046 rightMin = qBound(d->clipRect.left(), rightMin, d->clipRect.right());
1047 rightMax = qBound(d->clipRect.left(), rightMax, d->clipRect.right());
1049 if (leftMax > rightMax)
1051 if (rightMin < leftMin)
1054 Q16Dot16 rowHeight = rowBottom - rowTop;
1057 while (x <= leftMax) {
1058 Q16Dot16 excluded = 0;
1061 excluded += intersectPixelFP(x, rowTop, rowBottomLeft,
1062 bottomLeftIntersectAf, topLeftIntersectAf,
1063 topLeftSlopeFP, invTopLeftSlopeFP);
1065 excluded += intersectPixelFP(x, rowTopLeft, rowBottom,
1066 topLeftIntersectBf, bottomLeftIntersectBf,
1067 bottomLeftSlopeFP, invBottomLeftSlopeFP);
1069 if (x >= rightMin) {
1070 if (yFP <= iRightFP)
1071 excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
1072 topRightIntersectAf, bottomRightIntersectAf,
1073 topRightSlopeFP, invTopRightSlopeFP);
1074 if (yFP >= iRightFP)
1075 excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
1076 bottomRightIntersectBf, topRightIntersectBf,
1077 bottomRightSlopeFP, invBottomRightSlopeFP);
1080 Q16Dot16 coverage = rowHeight - excluded;
1081 buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
1082 Q16Dot16ToInt(255 * coverage));
1086 buffer.addSpan(x, rightMin - x, Q16Dot16ToInt(yFP),
1087 Q16Dot16ToInt(255 * rowHeight));
1090 while (x <= rightMax) {
1091 Q16Dot16 excluded = 0;
1092 if (yFP <= iRightFP)
1093 excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
1094 topRightIntersectAf, bottomRightIntersectAf,
1095 topRightSlopeFP, invTopRightSlopeFP);
1096 if (yFP >= iRightFP)
1097 excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
1098 bottomRightIntersectBf, topRightIntersectBf,
1099 bottomRightSlopeFP, invBottomRightSlopeFP);
1101 Q16Dot16 coverage = rowHeight - excluded;
1102 buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
1103 Q16Dot16ToInt(255 * coverage));
1107 leftIntersectAf += topLeftSlopeFP;
1108 leftIntersectBf += bottomLeftSlopeFP;
1109 rightIntersectAf += topRightSlopeFP;
1110 rightIntersectBf += bottomRightSlopeFP;
1111 topLeftIntersectAf = leftIntersectAf;
1112 topRightIntersectAf = rightIntersectAf;
1114 yFP += Q16Dot16Factor;
1118 int iTop = int(top.y() + 0.5f);
1119 int iLeft = left.y() < 0.5f ? -1 : int(left.y() - 0.5f);
1120 int iRight = right.y() < 0.5f ? -1 : int(right.y() - 0.5f);
1121 int iBottom = bottom.y() < 0.5f? -1 : int(bottom.y() - 0.5f);
1122 int iMiddle = qMin(iLeft, iRight);
1124 Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope);
1125 Q16Dot16 leftIntersectBf = FloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope);
1126 Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope);
1127 Q16Dot16 rightIntersectBf = FloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope);
1131 #define DO_SEGMENT(next, li, ri, ls, rs) \
1132 ny = qMin(next + 1, d->clipRect.top()); \
1134 li += ls * (ny - y); \
1135 ri += rs * (ny - y); \
1138 if (next > d->clipRect.bottom()) \
1139 next = d->clipRect.bottom(); \
1140 for (; y <= next; ++y) { \
1141 const int x1 = qMax(Q16Dot16ToInt(li), d->clipRect.left()); \
1142 const int x2 = qMin(Q16Dot16ToInt(ri), d->clipRect.right()); \
1144 buffer.addSpan(x1, x2 - x1 + 1, y, 255); \
1149 DO_SEGMENT(iMiddle, leftIntersectAf, rightIntersectAf, topLeftSlopeFP, topRightSlopeFP)
1150 DO_SEGMENT(iRight, leftIntersectBf, rightIntersectAf, bottomLeftSlopeFP, topRightSlopeFP)
1151 DO_SEGMENT(iLeft, leftIntersectAf, rightIntersectBf, topLeftSlopeFP, bottomRightSlopeFP);
1152 DO_SEGMENT(iBottom, leftIntersectBf, rightIntersectBf, bottomLeftSlopeFP, bottomRightSlopeFP);
1158 void QRasterizer::rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule)
1160 if (outline->n_points < 3 || outline->n_contours == 0)
1163 const QT_FT_Vector *points = outline->points;
1165 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
1167 // ### QT_FT_Outline already has a bounding rect which is
1168 // ### precomputed at this point, so we should probably just be
1169 // ### using that instead...
1170 QT_FT_Pos min_y = points[0].y, max_y = points[0].y;
1171 for (int i = 1; i < outline->n_points; ++i) {
1172 const QT_FT_Vector &p = points[i];
1173 min_y = qMin(p.y, min_y);
1174 max_y = qMax(p.y, max_y);
1177 int iTopBound = qMax(d->clipRect.top(), int((min_y + 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
1178 int iBottomBound = qMin(d->clipRect.bottom(), int((max_y - 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
1180 if (iTopBound > iBottomBound)
1183 d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
1186 for (int i = 0; i < outline->n_contours; ++i) {
1187 const int last = outline->contours[i];
1188 for (int j = first; j < last; ++j) {
1189 if (outline->tags[j+1] == QT_FT_CURVE_TAG_CUBIC) {
1190 Q_ASSERT(outline->tags[j+2] == QT_FT_CURVE_TAG_CUBIC);
1191 d->scanConverter.mergeCurve(points[j], points[j+1], points[j+2], points[j+3]);
1194 d->scanConverter.mergeLine(points[j], points[j+1]);
1201 d->scanConverter.end();
1204 void QRasterizer::rasterize(const QPainterPath &path, Qt::FillRule fillRule)
1209 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
1211 QRectF bounds = path.controlPointRect();
1213 int iTopBound = qMax(d->clipRect.top(), int(bounds.top() + 0.5 + (COORD_OFFSET - COORD_ROUNDING)/64.));
1214 int iBottomBound = qMin(d->clipRect.bottom(), int(bounds.bottom() - 0.5 + (COORD_OFFSET - COORD_ROUNDING)/64.));
1216 if (iTopBound > iBottomBound)
1219 d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
1221 int subpathStart = 0;
1222 QT_FT_Vector last = { 0, 0 };
1223 for (int i = 0; i < path.elementCount(); ++i) {
1224 switch (path.elementAt(i).type) {
1225 case QPainterPath::LineToElement:
1227 QT_FT_Vector p1 = last;
1228 QT_FT_Vector p2 = PointToVector(path.elementAt(i));
1229 d->scanConverter.mergeLine(p1, p2);
1233 case QPainterPath::MoveToElement:
1236 QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
1237 // close previous subpath
1238 if (first.x != last.x || first.y != last.y)
1239 d->scanConverter.mergeLine(last, first);
1242 last = PointToVector(path.elementAt(i));
1245 case QPainterPath::CurveToElement:
1247 QT_FT_Vector p1 = last;
1248 QT_FT_Vector p2 = PointToVector(path.elementAt(i));
1249 QT_FT_Vector p3 = PointToVector(path.elementAt(++i));
1250 QT_FT_Vector p4 = PointToVector(path.elementAt(++i));
1251 d->scanConverter.mergeCurve(p1, p2, p3, p4);
1261 QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
1264 if (first.x != last.x || first.y != last.y)
1265 d->scanConverter.mergeLine(last, first);
1267 d->scanConverter.end();