Qt 4.8
qrasterizer.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 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 "qrasterizer_p.h"
43 
44 #include <QPoint>
45 #include <QRect>
46 
47 #include <private/qmath_p.h>
48 #include <private/qdatabuffer_p.h>
49 #include <private/qdrawhelper_p.h>
50 
52 
53 typedef int Q16Dot16;
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
59 
60 #define Q16Dot16Multiply(x, y) (int)((qlonglong(x) * qlonglong(y)) >> 16)
61 #define Q16Dot16FastMultiply(x, y) (((x) * (y)) >> 16)
62 
63 #define SPAN_BUFFER_SIZE 256
64 
65 #define COORD_ROUNDING 1 // 0: round up, 1: round down
66 #define COORD_OFFSET 32 // 26.6, 32 is half a pixel
67 
68 static inline QT_FT_Vector PointToVector(const QPointF &p)
69 {
70  QT_FT_Vector result = { QT_FT_Pos(p.x() * 64), QT_FT_Pos(p.y() * 64) };
71  return result;
72 }
73 
74 class QSpanBuffer {
75 public:
76  QSpanBuffer(ProcessSpans blend, void *data, const QRect &clipRect)
77  : m_spanCount(0)
78  , m_blend(blend)
79  , m_data(data)
80  , m_clipRect(clipRect)
81  {
82  }
83 
85  {
86  flushSpans();
87  }
88 
89  void addSpan(int x, unsigned int len, int y, unsigned char coverage)
90  {
91  if (!coverage || !len)
92  return;
93 
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());
98 
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;
103 
104  if (++m_spanCount == SPAN_BUFFER_SIZE)
105  flushSpans();
106  }
107 
108 private:
109  void flushSpans()
110  {
112  m_spanCount = 0;
113  }
114 
117 
119  void *m_data;
120 
122 };
123 
124 #define CHUNK_SIZE 64
126 {
127 public:
128  QScanConverter();
129  ~QScanConverter();
130 
131  void begin(int top, int bottom, int left, int right,
132  Qt::FillRule fillRule, QSpanBuffer *spanBuffer);
133  void end();
134 
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);
138 
139  struct Line
140  {
143 
144  int top, bottom;
145 
146  int winding;
147  };
148 
149 private:
151  {
152  int x;
153  int winding;
154 
155  int left, right;
156  };
157 
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);
160 
161  void prepareChunk();
162 
163  void emitNode(const Intersection *node);
164  void emitSpans(int chunk);
165 
166  inline void allocate(int size);
167 
169 
170  int m_alloc;
171  int m_size;
172 
173  int m_top;
174  int m_bottom;
175 
178 
180 
181  int m_x;
182  int m_y;
184 
186 
188 
190 
191  template <typename T>
192  friend void qScanConvert(QScanConverter &d, T allVertical);
193 };
194 
196 {
197 public:
200  void *data;
202 
204 };
205 
207  : m_lines(0)
208  , m_alloc(0)
209  , m_size(0)
210  , m_intersections(0)
211  , m_active(0)
212 {
213 }
214 
216 {
217  if (m_intersections)
218  free(m_intersections);
219 }
220 
221 void QScanConverter::begin(int top, int bottom, int left, int right,
222  Qt::FillRule fillRule, QSpanBuffer *spanBuffer)
223 {
224  m_top = top;
225  m_bottom = bottom;
226  m_leftFP = IntToQ16Dot16(left);
227  m_rightFP = IntToQ16Dot16(right + 1);
228 
229  m_lines.reset();
230 
231  m_fillRuleMask = fillRule == Qt::WindingFill ? ~0x0 : 0x1;
232  m_spanBuffer = spanBuffer;
233 }
234 
236 {
237  m_size = CHUNK_SIZE;
238 
240  memset(m_intersections, 0, CHUNK_SIZE * sizeof(Intersection));
241 }
242 
244 {
245 tail_call:
246  if (node->left)
247  emitNode(node + node->left);
248 
250  m_spanBuffer->addSpan(m_x, node->x - m_x, m_y, 0xff);
251 
252  m_x = node->x;
253  m_winding += node->winding;
254 
255  if (node->right) {
256  node += node->right;
257  goto tail_call;
258  }
259 }
260 
262 {
263  for (int dy = 0; dy < CHUNK_SIZE; ++dy) {
264  m_x = 0;
265  m_y = chunk + dy;
266  m_winding = 0;
267 
269  }
270 }
271 
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)
275 {
276  b[6] = b[3];
277 
278  {
279  const QT_FT_Pos temp = (b[1].x + b[2].x)/2;
280 
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;
286  }
287  {
288  const QT_FT_Pos temp = (b[1].y + b[2].y)/2;
289 
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;
295  }
296 }
297 
298 static inline bool topOrder(const QScanConverter::Line &a, const QScanConverter::Line &b)
299 {
300  return a.top < b.top;
301 }
302 
303 static inline bool xOrder(const QScanConverter::Line *a, const QScanConverter::Line *b)
304 {
305  return a->x < b->x;
306 }
307 
308 template <bool B>
310 {
311  inline bool operator()() const
312  {
313  return B;
314  }
315 };
316 
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)
320 {
321  if (!d.m_lines.size()) {
322  d.m_active.reset();
323  return;
324  }
325  qSort(d.m_lines.data(), d.m_lines.data() + d.m_lines.size(), QT_PREPEND_NAMESPACE(topOrder));
326  int line = 0;
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
330  if (allVertical()) {
331  QScanConverter::Line *l = &d.m_lines.at(line);
332  d.m_active.resize(d.m_active.size() + 1);
333  int j;
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;
337  } else {
338  d.m_active << &d.m_lines.at(line);
339  }
340  }
341 
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);
348  int j;
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;
352  }
353  }
354 
355  int x = 0;
356  int winding = 0;
357  for (int i = 0; i < numActive; ++i) {
358  QScanConverter::Line *node = d.m_active.at(i);
359 
360  const int current = Q16Dot16ToInt(node->x);
361  if (winding & d.m_fillRuleMask)
362  d.m_spanBuffer->addSpan(x, current - x, y, 0xff);
363 
364  x = current;
365  winding += node->winding;
366 
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);
371 
372  d.m_active.resize(--numActive);
373  --i;
374  } else if (!allVertical())
375  node->x += node->delta;
376  }
377  }
378  d.m_active.reset();
379 }
380 
382 {
383  if (m_lines.isEmpty())
384  return;
385 
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) {
390  allVertical = false;
391  break;
392  }
393  }
394  if (allVertical)
396  else
398  } else {
399  for (int chunkTop = m_top; chunkTop <= m_bottom; chunkTop += CHUNK_SIZE) {
400  prepareChunk();
401 
402  Intersection isect = { 0, 0, 0, 0 };
403 
404  const int chunkBottom = chunkTop + CHUNK_SIZE;
405  for (int i = 0; i < m_lines.size(); ++i) {
406  Line &line = m_lines.at(i);
407 
408  if ((line.bottom < chunkTop) || (line.top > chunkBottom))
409  continue;
410 
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);
414 
415  isect.winding = line.winding;
416 
418  Intersection *end = m_intersections + bottom;
419 
420  if (line.delta) {
421  for (; it != end; ++it) {
422  isect.x = Q16Dot16ToInt(line.x);
423  line.x += line.delta;
424  mergeIntersection(it, isect);
425  }
426  } else {
427  isect.x = Q16Dot16ToInt(line.x);
428  for (; it != end; ++it)
429  mergeIntersection(it, isect);
430  }
431  }
432 
433  emitSpans(chunkTop);
434  }
435  }
436 
437  if (m_alloc > 1024) {
438  free(m_intersections);
439  m_alloc = 0;
440  m_size = 0;
441  m_intersections = 0;
442  }
443 
444  if (m_lines.size() > 1024)
445  m_lines.shrink(1024);
446 }
447 
448 inline void QScanConverter::allocate(int size)
449 {
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)));
453  m_alloc = newAlloc;
454  }
455 }
456 
458 {
459  Intersection *current = it;
460 
461  while (isect.x != current->x) {
462  int &next = isect.x < current->x ? current->left : current->right;
463  if (next)
464  current += next;
465  else {
467  next = last - current;
468  *last = isect;
469  ++m_size;
470  return;
471  }
472  }
473 
474  current->winding += isect.winding;
475 }
476 
478  const QT_FT_Vector &pc, const QT_FT_Vector &pd)
479 {
480  // make room for 32 splits
481  QT_FT_Vector beziers[4 + 3 * 32];
482 
483  QT_FT_Vector *b = beziers;
484 
485  b[0] = pa;
486  b[1] = pb;
487  b[2] = pc;
488  b[3] = pd;
489 
490  const QT_FT_Pos flatness = 16;
491 
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);
495 
496  bool belowThreshold;
497  if (l > 64) {
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));
502 
503  qlonglong d = d2 + d3;
504 
505  belowThreshold = (d <= qlonglong(flatness) * qlonglong(l));
506  } else {
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);
509 
510  belowThreshold = (d <= flatness);
511  }
512 
513  if (belowThreshold || b == beziers + 3 * 32) {
514  mergeLine(b[0], b[3]);
515  b -= 3;
516  continue;
517  }
518 
519  split(b);
520  b += 3;
521  }
522 }
523 
524 inline bool QScanConverter::clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding)
525 {
526  bool right = edgeFP == m_rightFP;
527 
528  if (xFP == edgeFP) {
529  if ((slopeFP > 0) ^ right)
530  return false;
531  else {
532  Line line = { edgeFP, 0, iTop, iBottom, winding };
533  m_lines.add(line);
534  return true;
535  }
536  }
537 
538  Q16Dot16 lastFP = xFP + slopeFP * (iBottom - iTop);
539 
540  if (lastFP == edgeFP) {
541  if ((slopeFP < 0) ^ right)
542  return false;
543  else {
544  Line line = { edgeFP, 0, iTop, iBottom, winding };
545  m_lines.add(line);
546  return true;
547  }
548  }
549 
550  // does line cross edge?
551  if ((lastFP < edgeFP) ^ (xFP < edgeFP)) {
552  Q16Dot16 deltaY = Q16Dot16((edgeFP - xFP) / Q16Dot16ToFloat(slopeFP));
553 
554  if ((xFP < edgeFP) ^ right) {
555  // top segment needs to be clipped
556  int iHeight = Q16Dot16ToInt(deltaY + 1);
557  int iMiddle = iTop + iHeight;
558 
559  Line line = { edgeFP, 0, iTop, iMiddle, winding };
560  m_lines.add(line);
561 
562  if (iMiddle != iBottom) {
563  xFP += slopeFP * (iHeight + 1);
564  iTop = iMiddle + 1;
565  } else
566  return true;
567  } else {
568  // bottom segment needs to be clipped
569  int iHeight = Q16Dot16ToInt(deltaY);
570  int iMiddle = iTop + iHeight;
571 
572  if (iMiddle != iBottom) {
573  Line line = { edgeFP, 0, iMiddle + 1, iBottom, winding };
574  m_lines.add(line);
575 
576  iBottom = iMiddle;
577  }
578  }
579  return false;
580  } else if ((xFP < edgeFP) ^ right) {
581  Line line = { edgeFP, 0, iTop, iBottom, winding };
582  m_lines.add(line);
583  return true;
584  }
585 
586  return false;
587 }
588 
590 {
591  int winding = 1;
592 
593  if (a.y > b.y) {
594  qSwap(a, b);
595  winding = -1;
596  }
597 
598  a.x += COORD_OFFSET;
599  a.y += COORD_OFFSET;
600  b.x += COORD_OFFSET;
601  b.y += COORD_OFFSET;
602 
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));
605 
606  if (iTop <= iBottom) {
607  Q16Dot16 aFP = Q16Dot16Factor/2 + (a.x << 10) - COORD_ROUNDING;
608 
609  if (b.x == a.x) {
610  Line line = { qBound(m_leftFP, aFP, m_rightFP), 0, iTop, iBottom, winding };
611  m_lines.add(line);
612  } else {
613  const qreal slope = (b.x - a.x) / qreal(b.y - a.y);
614 
615  const Q16Dot16 slopeFP = FloatToQ16Dot16(slope);
616 
617  Q16Dot16 xFP = aFP + Q16Dot16Multiply(slopeFP,
618  IntToQ16Dot16(iTop)
619  + Q16Dot16Factor/2 - (a.y << 10));
620 
621  if (clip(xFP, iTop, iBottom, slopeFP, m_leftFP, winding))
622  return;
623 
624  if (clip(xFP, iTop, iBottom, slopeFP, m_rightFP, winding))
625  return;
626 
627  Q_ASSERT(xFP >= m_leftFP);
628 
629  Line line = { xFP, slopeFP, iTop, iBottom, winding };
630  m_lines.add(line);
631  }
632  }
633 }
634 
636  : d(new QRasterizerPrivate)
637 {
638 }
639 
641 {
642  delete d;
643 }
644 
645 void QRasterizer::setAntialiased(bool antialiased)
646 {
647  d->antialiased = antialiased;
648 }
649 
651 {
652  d->blend = blend;
653  d->data = data;
654 }
655 
656 void QRasterizer::setClipRect(const QRect &clipRect)
657 {
658  d->clipRect = clipRect;
659 }
660 
661 static Q16Dot16 intersectPixelFP(int x, Q16Dot16 top, Q16Dot16 bottom, Q16Dot16 leftIntersectX, Q16Dot16 rightIntersectX, Q16Dot16 slope, Q16Dot16 invSlope)
662 {
663  Q16Dot16 leftX = IntToQ16Dot16(x);
664  Q16Dot16 rightX = IntToQ16Dot16(x) + Q16Dot16Factor;
665 
666  Q16Dot16 leftIntersectY, rightIntersectY;
667  if (slope > 0) {
668  leftIntersectY = top + Q16Dot16Multiply(leftX - leftIntersectX, invSlope);
669  rightIntersectY = leftIntersectY + invSlope;
670  } else {
671  leftIntersectY = top + Q16Dot16Multiply(leftX - rightIntersectX, invSlope);
672  rightIntersectY = leftIntersectY + invSlope;
673  }
674 
675  if (leftIntersectX >= leftX && rightIntersectX <= rightX) {
676  return Q16Dot16Multiply(bottom - top, leftIntersectX - leftX + ((rightIntersectX - leftIntersectX) >> 1));
677  } else if (leftIntersectX >= rightX) {
678  return bottom - top;
679  } else if (leftIntersectX >= leftX) {
680  if (slope > 0) {
681  return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, rightIntersectY - top);
682  } else {
683  return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, bottom - rightIntersectY);
684  }
685  } else if (rightIntersectX <= leftX) {
686  return 0;
687  } else if (rightIntersectX <= rightX) {
688  if (slope > 0) {
689  return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, bottom - leftIntersectY);
690  } else {
691  return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, leftIntersectY - top);
692  }
693  } else {
694  if (slope > 0) {
695  return (bottom - rightIntersectY) + ((rightIntersectY - leftIntersectY) >> 1);
696  } else {
697  return (rightIntersectY - top) + ((leftIntersectY - rightIntersectY) >> 1);
698  }
699  }
700 }
701 
702 static inline bool q26Dot6Compare(qreal p1, qreal p2)
703 {
704  return int((p2 - p1) * qreal(64.)) == 0;
705 }
706 
707 static inline qreal qFloorF(qreal v)
708 {
709 #ifdef QT_USE_MATH_H_FLOATS
710  if (sizeof(qreal) == sizeof(float))
711  return floorf(v);
712  else
713 #endif
714  return floor(v);
715 }
716 
717 static inline QPointF snapTo26Dot6Grid(const QPointF &p)
718 {
719  return QPointF(qFloorF(p.x() * 64) * (1 / qreal(64)),
720  qFloorF(p.y() * 64) * (1 / qreal(64)));
721 }
722 
723 /*
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.
730  */
731 static inline qreal qSafeDivide(qreal x, qreal y)
732 {
733  if (y == 0)
734  return x > 0 ? 1e20 : -1e20;
735  return x / y;
736 }
737 
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
741  */
742 static inline int qSafeFloatToQ16Dot16(qreal x)
743 {
744  qreal tmp = x * 65536.;
745  if (tmp > qreal(INT_MAX))
746  return INT_MAX;
747  else if (tmp < qreal(INT_MIN))
748  return -INT_MAX;
749  return int(tmp);
750 }
751 
752 void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap)
753 {
754  if (a == b || width == 0 || d->clipRect.isEmpty())
755  return;
756 
757  Q_ASSERT(width > 0.0);
758 
759  QPointF pa = a;
760  QPointF pb = b;
761 
762  if (squareCap) {
763  QPointF delta = pb - pa;
764  pa -= (0.5f * width) * delta;
765  pb += (0.5f * width) * delta;
766  }
767 
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);
770 
771  if (!clip.contains(pa) || !clip.contains(pb)) {
772  qreal t1 = 0;
773  qreal t2 = 1;
774 
775  const qreal o[2] = { pa.x(), pa.y() };
776  const qreal d[2] = { pb.x() - pa.x(), pb.y() - pa.y() };
777 
778  const qreal low[2] = { clip.left(), clip.top() };
779  const qreal high[2] = { clip.right(), clip.bottom() };
780 
781  for (int i = 0; i < 2; ++i) {
782  if (d[i] == 0) {
783  if (o[i] <= low[i] || o[i] >= high[i])
784  return;
785  continue;
786  }
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;
790  if (t_low > t_high)
791  qSwap(t_low, t_high);
792  if (t1 < t_low)
793  t1 = t_low;
794  if (t2 > t_high)
795  t2 = t_high;
796  if (t1 >= t2)
797  return;
798  }
799 
800  QPointF npa = pa + (pb - pa) * t1;
801  QPointF npb = pa + (pb - pa) * t2;
802 
803  pa = npa;
804  pb = npb;
805  }
806 
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.;
812  }
813 
814  {
815  // old delta
816  const QPointF d0 = a - b;
817  const qreal w0 = d0.x() * d0.x() + d0.y() * d0.y();
818 
819  // new delta
820  const QPointF d = pa - pb;
821  const qreal w = d.x() * d.x() + d.y() * d.y();
822 
823  if (w == 0)
824  return;
825 
826  // adjust width which is given relative to |b - a|
827  width *= sqrt(w0 / w);
828  }
829 
830  QSpanBuffer buffer(d->blend, d->data, d->clipRect);
831 
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;
835 
836  const qreal y = pa.y();
837  const qreal dy = width * dx;
838 
839  pa = QPointF(x, y - dy);
840  pb = QPointF(x, y + dy);
841 
842  width = 1 / width;
843  }
844 
845  if (q26Dot6Compare(pa.x(), pb.x())) {
846  if (pa.y() > pb.y())
847  qSwap(pa, pb);
848 
849  const qreal dy = pb.y() - pa.y();
850  const qreal halfWidth = 0.5f * width * dy;
851 
852  qreal left = pa.x() - halfWidth;
853  qreal right = pa.x() + halfWidth;
854 
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));
857 
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));
860 
861  if (q26Dot6Compare(left, right) || q26Dot6Compare(pa.y(), pb.y()))
862  return;
863 
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);
871 
872  Q16Dot16 coverage[3];
873  int x[3];
874  int len[3];
875 
876  int n = 1;
877  if (iLeft == iRight) {
878  coverage[0] = (leftWidth + rightWidth) * 255;
879  x[0] = iLeft;
880  len[0] = 1;
881  } else {
882  coverage[0] = leftWidth * 255;
883  x[0] = iLeft;
884  len[0] = 1;
885  if (leftWidth == Q16Dot16Factor) {
886  len[0] = iRight - iLeft;
887  } else if (iRight - iLeft > 1) {
888  coverage[1] = IntToQ16Dot16(255);
889  x[1] = iLeft + 1;
890  len[1] = iRight - iLeft - 1;
891  ++n;
892  }
893  if (rightWidth) {
894  coverage[n] = rightWidth * 255;
895  x[n] = iRight;
896  len[n] = 1;
897  ++n;
898  }
899  }
900 
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)
907  - qMax(yFP, yPa);
908  const int y = Q16Dot16ToInt(yFP);
909  if (y > d->clipRect.bottom())
910  break;
911  for (int i = 0; i < n; ++i) {
912  buffer.addSpan(x[i], len[i], y,
913  Q16Dot16ToInt(Q16Dot16Multiply(rowHeight, coverage[i])));
914  }
915  }
916  } else { // aliased
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);
921 
922  int iWidth = iRight - iLeft + 1;
923  for (int y = iTop; y <= iBottom; ++y)
924  buffer.addSpan(iLeft, iWidth, y, 255);
925  }
926  } else {
927  if (pa.y() > pb.y())
928  qSwap(pa, pb);
929 
930  QPointF delta = pb - pa;
931  delta *= 0.5f * width;
932  const QPointF perp(delta.y(), -delta.x());
933 
934  QPointF top;
935  QPointF left;
936  QPointF right;
937  QPointF bottom;
938 
939  if (pa.x() < pb.x()) {
940  top = pa + perp;
941  left = pa - perp;
942  right = pb + perp;
943  bottom = pb - perp;
944  } else {
945  top = pa - perp;
946  left = pb - perp;
947  right = pa + perp;
948  bottom = pb + perp;
949  }
950 
951  top = snapTo26Dot6Grid(top);
952  bottom = snapTo26Dot6Grid(bottom);
953  left = snapTo26Dot6Grid(left);
954  right = snapTo26Dot6Grid(right);
955 
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()));
958 
959  const QPointF topLeftEdge = left - top;
960  const QPointF topRightEdge = right - top;
961  const QPointF bottomLeftEdge = bottom - left;
962  const QPointF bottomRightEdge = bottom - right;
963 
964  const qreal topLeftSlope = qSafeDivide(topLeftEdge.x(), topLeftEdge.y());
965  const qreal bottomLeftSlope = qSafeDivide(bottomLeftEdge.x(), bottomLeftEdge.y());
966 
967  const qreal topRightSlope = qSafeDivide(topRightEdge.x(), topRightEdge.y());
968  const qreal bottomRightSlope = qSafeDivide(bottomRightEdge.x(), bottomRightEdge.y());
969 
970  const Q16Dot16 topLeftSlopeFP = qSafeFloatToQ16Dot16(topLeftSlope);
971  const Q16Dot16 topRightSlopeFP = qSafeFloatToQ16Dot16(topRightSlope);
972 
973  const Q16Dot16 bottomLeftSlopeFP = qSafeFloatToQ16Dot16(bottomLeftSlope);
974  const Q16Dot16 bottomRightSlopeFP = qSafeFloatToQ16Dot16(bottomRightSlope);
975 
976  const Q16Dot16 invTopLeftSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, topLeftSlope));
977  const Q16Dot16 invTopRightSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, topRightSlope));
978 
979  const Q16Dot16 invBottomLeftSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, bottomLeftSlope));
980  const Q16Dot16 invBottomRightSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, bottomRightSlope));
981 
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));
987 
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;
992 
993  if (iLeftFP < iTopFP)
994  leftIntersectBf = FloatToQ16Dot16(left.x() + (int(topBound) - left.y()) * bottomLeftSlope);
995 
996  if (iRightFP < iTopFP)
997  rightIntersectBf = FloatToQ16Dot16(right.x() + (int(topBound) - right.y()) * bottomRightSlope);
998 
999  Q16Dot16 rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom;
1000  Q16Dot16 topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf;
1001  Q16Dot16 bottomLeftIntersectAf, bottomLeftIntersectBf, bottomRightIntersectAf, bottomRightIntersectBf;
1002 
1003  int leftMin, leftMax, rightMin, rightMax;
1004 
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());
1009 
1010  rowTop = qMax(iTopFP, yTopFP);
1011  topLeftIntersectAf = leftIntersectAf +
1012  Q16Dot16Multiply(topLeftSlopeFP, rowTop - iTopFP);
1013  topRightIntersectAf = rightIntersectAf +
1014  Q16Dot16Multiply(topRightSlopeFP, rowTop - iTopFP);
1015 
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);
1023 
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);
1029  } else {
1030  topLeftIntersectBf = leftIntersectBf;
1031  bottomLeftIntersectAf = leftIntersectAf + topLeftSlopeFP;
1032  }
1033 
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);
1039  } else {
1040  topRightIntersectBf = rightIntersectBf;
1041  bottomRightIntersectAf = rightIntersectAf + topRightSlopeFP;
1042  }
1043 
1044  if (yFP == iBottomFP) {
1045  bottomLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowBottom - yFP);
1046  bottomRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowBottom - yFP);
1047  } else {
1048  bottomLeftIntersectBf = leftIntersectBf + bottomLeftSlopeFP;
1049  bottomRightIntersectBf = rightIntersectBf + bottomRightSlopeFP;
1050  }
1051 
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));
1058  } else {
1059  leftMin = Q16Dot16ToInt(topLeftIntersectBf);
1060  leftMax = Q16Dot16ToInt(bottomLeftIntersectBf);
1061  }
1062 
1063  leftMin = qBound(d->clipRect.left(), leftMin, d->clipRect.right());
1064  leftMax = qBound(d->clipRect.left(), leftMax, d->clipRect.right());
1065 
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));
1072  } else {
1073  rightMin = Q16Dot16ToInt(bottomRightIntersectBf);
1074  rightMax = Q16Dot16ToInt(topRightIntersectBf);
1075  }
1076 
1077  rightMin = qBound(d->clipRect.left(), rightMin, d->clipRect.right());
1078  rightMax = qBound(d->clipRect.left(), rightMax, d->clipRect.right());
1079 
1080  if (leftMax > rightMax)
1081  leftMax = rightMax;
1082  if (rightMin < leftMin)
1083  rightMin = leftMin;
1084 
1085  Q16Dot16 rowHeight = rowBottom - rowTop;
1086 
1087  int x = leftMin;
1088  while (x <= leftMax) {
1089  Q16Dot16 excluded = 0;
1090 
1091  if (yFP <= iLeftFP)
1092  excluded += intersectPixelFP(x, rowTop, rowBottomLeft,
1093  bottomLeftIntersectAf, topLeftIntersectAf,
1094  topLeftSlopeFP, invTopLeftSlopeFP);
1095  if (yFP >= iLeftFP)
1096  excluded += intersectPixelFP(x, rowTopLeft, rowBottom,
1097  topLeftIntersectBf, bottomLeftIntersectBf,
1098  bottomLeftSlopeFP, invBottomLeftSlopeFP);
1099 
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);
1109  }
1110 
1111  Q16Dot16 coverage = rowHeight - excluded;
1112  buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
1113  Q16Dot16ToInt(255 * coverage));
1114  ++x;
1115  }
1116  if (x < rightMin) {
1117  buffer.addSpan(x, rightMin - x, Q16Dot16ToInt(yFP),
1118  Q16Dot16ToInt(255 * rowHeight));
1119  x = rightMin;
1120  }
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);
1131 
1132  Q16Dot16 coverage = rowHeight - excluded;
1133  buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
1134  Q16Dot16ToInt(255 * coverage));
1135  ++x;
1136  }
1137 
1138  leftIntersectAf += topLeftSlopeFP;
1139  leftIntersectBf += bottomLeftSlopeFP;
1140  rightIntersectAf += topRightSlopeFP;
1141  rightIntersectBf += bottomRightSlopeFP;
1142  topLeftIntersectAf = leftIntersectAf;
1143  topRightIntersectAf = rightIntersectAf;
1144 
1145  yFP += Q16Dot16Factor;
1146  rowTop = yFP;
1147  }
1148  } else { // aliased
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);
1154 
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);
1159 
1160  int ny;
1161  int y = iTop;
1162 #define DO_SEGMENT(next, li, ri, ls, rs) \
1163  ny = qMin(next + 1, d->clipRect.top()); \
1164  if (y < ny) { \
1165  li += ls * (ny - y); \
1166  ri += rs * (ny - y); \
1167  y = ny; \
1168  } \
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()); \
1174  if (x2 >= x1) \
1175  buffer.addSpan(x1, x2 - x1 + 1, y, 255); \
1176  li += ls; \
1177  ri += rs; \
1178  }
1179 
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);
1184 #undef DO_SEGMENT
1185  }
1186  }
1187 }
1188 
1189 void QRasterizer::rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule)
1190 {
1191  if (outline->n_points < 3 || outline->n_contours == 0)
1192  return;
1193 
1194  const QT_FT_Vector *points = outline->points;
1195 
1196  QSpanBuffer buffer(d->blend, d->data, d->clipRect);
1197 
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);
1206  }
1207 
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));
1210 
1211  if (iTopBound > iBottomBound)
1212  return;
1213 
1214  d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
1215 
1216  int first = 0;
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]);
1223  j += 2;
1224  } else {
1225  d->scanConverter.mergeLine(points[j], points[j+1]);
1226  }
1227  }
1228 
1229  first = last + 1;
1230  }
1231 
1232  d->scanConverter.end();
1233 }
1234 
1236 {
1237  if (path.isEmpty())
1238  return;
1239 
1240  QSpanBuffer buffer(d->blend, d->data, d->clipRect);
1241 
1242  QRectF bounds = path.controlPointRect();
1243 
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.)));
1246 
1247  if (iTopBound > iBottomBound)
1248  return;
1249 
1250  d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
1251 
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) {
1257  {
1258  QT_FT_Vector p1 = last;
1259  QT_FT_Vector p2 = PointToVector(path.elementAt(i));
1260  d->scanConverter.mergeLine(p1, p2);
1261  last = p2;
1262  break;
1263  }
1265  {
1266  if (i != 0) {
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);
1271  }
1272  subpathStart = i;
1273  last = PointToVector(path.elementAt(i));
1274  break;
1275  }
1277  {
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);
1283  last = p4;
1284  break;
1285  }
1286  default:
1287  Q_ASSERT(false);
1288  break;
1289  }
1290  }
1291 
1292  QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
1293 
1294  // close path
1295  if (first.x != last.x || first.y != last.y)
1296  d->scanConverter.mergeLine(last, first);
1297 
1298  d->scanConverter.end();
1299 }
1300 
ElementType type
the type of element
Definition: qpainterpath.h:81
void mergeIntersection(Intersection *head, const Intersection &isect)
double d
Definition: qnumeric_p.h:62
T * q_check_ptr(T *p)
Definition: qglobal.h:1857
void initialize(ProcessSpans blend, void *data)
void begin(int top, int bottom, int left, int right, Qt::FillRule fillRule, QSpanBuffer *spanBuffer)
bool isEmpty() const
Returns true if either there are no elements in this path, or if the only element is a MoveToElement;...
Definition: qpainterpath.h:392
#define QT_FT_CURVE_TAG_CUBIC
double qreal
Definition: qglobal.h:1193
#define DO_SEGMENT(next, li, ri, ls, rs)
friend void qScanConvert(QScanConverter &d, T allVertical)
#define Q16Dot16Factor
Definition: qrasterizer.cpp:58
unsigned char c[8]
Definition: qnumeric_p.h:62
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
void mergeCurve(const QT_FT_Vector &a, const QT_FT_Vector &b, const QT_FT_Vector &c, const QT_FT_Vector &d)
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
unsigned char coverage
static Q16Dot16 intersectPixelFP(int x, Q16Dot16 top, Q16Dot16 bottom, Q16Dot16 leftIntersectX, Q16Dot16 rightIntersectX, Q16Dot16 slope, Q16Dot16 invSlope)
QSpanBuffer * m_spanBuffer
static int qSafeFloatToQ16Dot16(qreal x)
#define it(className, varName)
The QPainterPath class provides a container for painting operations, enabling graphical shapes to be ...
Definition: qpainterpath.h:67
unsigned short len
void rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule)
QDataBuffer< Line > m_lines
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:214
void addSpan(int x, unsigned int len, int y, unsigned char coverage)
Definition: qrasterizer.cpp:89
QScanConverter scanConverter
FillRule
Definition: qnamespace.h:1485
QT_FT_SpanFunc ProcessSpans
#define COORD_OFFSET
Definition: qrasterizer.cpp:66
int left() const
Returns the x-coordinate of the rectangle&#39;s left edge.
Definition: qrect.h:240
static qreal qSafeDivide(qreal x, qreal y)
static bool q26Dot6Compare(qreal p1, qreal p2)
void qScanConvert(QScanConverter &d, T allVertical)
bool clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding)
long ASN1_INTEGER_get ASN1_INTEGER * a
int bottom() const
Returns the y-coordinate of the rectangle&#39;s bottom edge.
Definition: qrect.h:249
QDataBuffer< Line * > m_active
Q_DECL_CONSTEXPR T qAbs(const T &t)
Definition: qglobal.h:1201
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
QT_FT_Span m_spans[SPAN_BUFFER_SIZE]
const QPainterPath::Element & elementAt(int i) const
Returns the element at the given index in the painter path.
Definition: qpainterpath.h:402
Q_CORE_EXPORT QTextStream & right(QTextStream &s)
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
static bool topOrder(const QScanConverter::Line &a, const QScanConverter::Line &b)
qreal x() const
Returns the x-coordinate of this point.
Definition: qpoint.h:282
void setAntialiased(bool antialiased)
#define Q16Dot16Multiply(x, y)
Definition: qrasterizer.cpp:60
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
qreal & rx()
Returns a reference to the x coordinate of this point.
Definition: qpoint.h:302
The QRectF class defines a rectangle in the plane using floating point precision. ...
Definition: qrect.h:511
QPoint bottomRight() const
Returns the position of the rectangle&#39;s bottom-right corner.
Definition: qrect.h:291
#define CHUNK_SIZE
#define Q16Dot16ToFloat(i)
Definition: qrasterizer.cpp:54
QT_FT_Vector * points
#define QT_PREPEND_NAMESPACE(name)
This macro qualifies identifier with the full namespace.
Definition: qglobal.h:87
void rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap=false)
static const char * data(const QByteArray &arr)
static void split(QT_FT_Vector *b)
static QT_FT_Vector PointToVector(const QPointF &p)
Definition: qrasterizer.cpp:68
void flushSpans()
#define SPAN_BUFFER_SIZE
Definition: qrasterizer.cpp:63
#define Q16Dot16FastMultiply(x, y)
Definition: qrasterizer.cpp:61
void qSort(RandomAccessIterator start, RandomAccessIterator end)
Definition: qalgorithms.h:177
void qSwap(T &value1, T &value2)
Definition: qglobal.h:2181
static qreal qFloorF(qreal v)
#define FloatToQ16Dot16(i)
Definition: qrasterizer.cpp:55
static int perp(bool vertical, const QSize &size)
static bool xOrder(const QScanConverter::Line *a, const QScanConverter::Line *b)
bool isEmpty() const
Returns true if the rectangle is empty, otherwise returns false.
Definition: qrect.h:234
#define IntToQ16Dot16(i)
Definition: qrasterizer.cpp:56
static QPointF snapTo26Dot6Grid(const QPointF &p)
int top() const
Returns the y-coordinate of the rectangle&#39;s top edge.
Definition: qrect.h:243
void mergeLine(QT_FT_Vector a, QT_FT_Vector b)
#define Q16Dot16ToInt(i)
Definition: qrasterizer.cpp:57
int right() const
Returns the x-coordinate of the rectangle&#39;s right edge.
Definition: qrect.h:246
QRectF controlPointRect() const
Returns the rectangle containing all the points and control points in this path.
int Q16Dot16
Definition: qrasterizer.cpp:53
The QPoint class defines a point in the plane using integer precision.
Definition: qpoint.h:53
ProcessSpans blend
Q_DECL_CONSTEXPR const T & qBound(const T &min, const T &val, const T &max)
Definition: qglobal.h:1219
QRect m_clipRect
The QRect class defines a rectangle in the plane using integer precision.
Definition: qrect.h:58
QFactoryLoader * l
qreal & ry()
Returns a reference to the y coordinate of this point.
Definition: qpoint.h:307
QSpanBuffer(ProcessSpans blend, void *data, const QRect &clipRect)
Definition: qrasterizer.cpp:76
qreal y() const
Returns the y-coordinate of this point.
Definition: qpoint.h:287
ProcessSpans m_blend
QT_FT_BEGIN_HEADER typedef signed int QT_FT_Pos
Q16Dot16 m_leftFP
qreal top() const
Returns the y-coordinate of the rectangle&#39;s top edge.
Definition: qrect.h:526
void emitNode(const Intersection *node)
void setClipRect(const QRect &clipRect)
int elementCount() const
Returns the number of path elements in the painter path.
Definition: qpainterpath.h:397
qreal bottom() const
Returns the y-coordinate of the rectangle&#39;s bottom edge.
Definition: qrect.h:528
void allocate(int size)
static const KeyPair *const end
Intersection * m_intersections
qint64 qlonglong
Definition: qglobal.h:951
Q_CORE_EXPORT QTextStream & left(QTextStream &s)
bool operator()() const
#define COORD_ROUNDING
Definition: qrasterizer.cpp:65
void emitSpans(int chunk)
QRasterizerPrivate * d
Definition: qrasterizer_p.h:90
#define INT_MAX
Q16Dot16 m_rightFP
QPoint topLeft() const
Returns the position of the rectangle&#39;s top-left corner.
Definition: qrect.h:288