Qt 4.8
qcosmeticstroker.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 "qcosmeticstroker_p.h"
43 #include "private/qpainterpath_p.h"
44 #include <qdebug.h>
45 #include <math.h>
46 
48 
49 #if 0
50 inline QString capString(int caps)
51 {
52  QString str;
53  if (caps & QCosmeticStroker::CapBegin) {
54  str += "CapBegin ";
55  }
56  if (caps & QCosmeticStroker::CapEnd) {
57  str += "CapEnd ";
58  }
59  return str;
60 }
61 #endif
62 
63 #define toF26Dot6(x) ((int)((x)*64.))
64 
65 static inline uint sourceOver(uint d, uint color)
66 {
67  return color + BYTE_MUL(d, qAlpha(~color));
68 }
69 
70 inline static int F16Dot16FixedDiv(int x, int y)
71 {
72  if (qAbs(x) > 0x7fff)
73  return (((qlonglong)x) << 16) / y;
74  return (x << 16) / y;
75 }
76 
77 typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage);
78 
79 namespace {
80 
81 struct Dasher {
82  QCosmeticStroker *stroker;
83  int *pattern;
84  int offset;
85  int dashIndex;
86  int dashOn;
87 
88  Dasher(QCosmeticStroker *s, bool reverse, int start, int stop)
89  : stroker(s)
90  {
91  int delta = stop - start;
92  if (reverse) {
93  pattern = stroker->reversePattern;
94  offset = stroker->patternLength - stroker->patternOffset - delta - ((start & 63) - 32);
95  dashOn = 0;
96  } else {
97  pattern = stroker->pattern;
98  offset = stroker->patternOffset - ((start & 63) - 32);
99  dashOn = 1;
100  }
101  offset %= stroker->patternLength;
102  if (offset < 0)
103  offset += stroker->patternLength;
104 
105  dashIndex = 0;
106  while (offset>= pattern[dashIndex])
107  ++dashIndex;
108 
109 // qDebug() << " dasher" << offset/64. << reverse << dashIndex;
110  stroker->patternOffset += delta;
111  stroker->patternOffset %= stroker->patternLength;
112  }
113 
114  bool on() const {
115  return (dashIndex + dashOn) & 1;
116  }
117  void adjust() {
118  offset += 64;
119  if (offset >= pattern[dashIndex]) {
120  ++dashIndex;
121  dashIndex %= stroker->patternSize;
122  }
123  offset %= stroker->patternLength;
124 // qDebug() << "dasher.adjust" << offset/64. << dashIndex;
125  }
126 };
127 
128 struct NoDasher {
129  NoDasher(QCosmeticStroker *, bool, int, int) {}
130  bool on() const { return true; }
131  void adjust(int = 0) {}
132 };
133 
134 };
135 
136 /*
137  * The return value is the result of the clipLine() call performed at the start
138  * of each of the two functions, aka "false" means completely outside the devices
139  * rect.
140  */
141 template<DrawPixel drawPixel, class Dasher>
142 static bool drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
143 template<DrawPixel drawPixel, class Dasher>
144 static bool drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
145 
146 inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage)
147 {
148  const QRect &cl = stroker->clip;
149  if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
150  return;
151 
152  int lastx = stroker->spans[stroker->current_span-1].x + stroker->spans[stroker->current_span-1].len ;
153  int lasty = stroker->spans[stroker->current_span-1].y;
154 
155  if (stroker->current_span == QCosmeticStroker::NSPANS || y < lasty || (y == lasty && x < lastx)) {
156  stroker->blend(stroker->current_span, stroker->spans, &stroker->state->penData);
157  stroker->current_span = 0;
158  }
159 
160  stroker->spans[stroker->current_span].x = ushort(x);
161  stroker->spans[stroker->current_span].len = 1;
162  stroker->spans[stroker->current_span].y = y;
163  stroker->spans[stroker->current_span].coverage = coverage*stroker->opacity >> 8;
164  ++stroker->current_span;
165 }
166 
167 inline void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage)
168 {
169  const QRect &cl = stroker->clip;
170  if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
171  return;
172 
173  int offset = x + stroker->ppl*y;
174  uint c = BYTE_MUL(stroker->color, coverage);
175  stroker->pixels[offset] = sourceOver(stroker->pixels[offset], c);
176 }
177 
178 inline void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int)
179 {
180  const QRect &cl = stroker->clip;
181  if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
182  return;
183 
184  int offset = x + stroker->ppl*y;
185  stroker->pixels[offset] = sourceOver(stroker->pixels[offset], stroker->color);
186 }
187 
189  Aliased = 0,
191  Solid = 0,
192  Dashed = 2,
195 };
196 
197 static StrokeLine strokeLine(int strokeSelection)
198 {
199  StrokeLine stroke;
200 
201  switch (strokeSelection) {
203  stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, NoDasher>;
204  break;
205  case Aliased|Solid|FastDraw:
206  stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, NoDasher>;
207  break;
209  stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, Dasher>;
210  break;
211  case Aliased|Dashed|FastDraw:
212  stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, Dasher>;
213  break;
215  stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, NoDasher>;
216  break;
218  stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, NoDasher>;
219  break;
221  stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, Dasher>;
222  break;
224  stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, Dasher>;
225  break;
226  default:
227  Q_ASSERT(false);
228  stroke = 0;
229  }
230  return stroke;
231 }
232 
234 {
235  blend = state->penData.blend;
236  if (state->clip && state->clip->enabled && state->clip->hasRectClip && !state->clip->clipRect.isEmpty()) {
237  clip &= state->clip->clipRect;
238  blend = state->penData.unclipped_blend;
239  }
240 
241  int strokeSelection = 0;
242  if (blend == state->penData.unclipped_blend
243  && state->penData.type == QSpanData::Solid
244  && (state->penData.rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
245  || state->penData.rasterBuffer->format == QImage::Format_RGB32)
246  && state->compositionMode() == QPainter::CompositionMode_SourceOver)
247  strokeSelection |= FastDraw;
248 
249  if (state->renderHints & QPainter::Antialiasing)
250  strokeSelection |= AntiAliased;
251 
252  const QVector<qreal> &penPattern = state->lastPen.dashPattern();
253  if (penPattern.isEmpty()) {
254  Q_ASSERT(!pattern && !reversePattern);
255  pattern = 0;
256  reversePattern = 0;
257  patternLength = 0;
258  patternSize = 0;
259  } else {
260  pattern = (int *)malloc(penPattern.size()*sizeof(int));
261  reversePattern = (int *)malloc(penPattern.size()*sizeof(int));
262  patternSize = penPattern.size();
263 
264  patternLength = 0;
265  for (int i = 0; i < patternSize; ++i) {
266  patternLength += (int) qMax(1. , penPattern.at(i)*64.);
267  pattern[i] = patternLength;
268  }
269  patternLength = 0;
270  for (int i = 0; i < patternSize; ++i) {
271  patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.);
272  reversePattern[i] = patternLength;
273  }
274  strokeSelection |= Dashed;
275 // qDebug() << "setup: size=" << patternSize << "length=" << patternLength/64.;
276  }
277 
278  stroke = strokeLine(strokeSelection);
279 
280  qreal width = state->lastPen.widthF();
281  if (width == 0)
282  opacity = 256;
283  else if (state->lastPen.isCosmetic())
284  opacity = (int) 256*width;
285  else
286  opacity = (int) 256*width*state->txscale;
287  opacity = qBound(0, opacity, 256);
288 
289  drawCaps = state->lastPen.capStyle() != Qt::FlatCap;
290 
291  if (strokeSelection & FastDraw) {
292  color = INTERPOLATE_PIXEL_256(state->penData.solid.color, opacity, 0, 0);
293  QRasterBuffer *buffer = state->penData.rasterBuffer;
294  pixels = (uint *)buffer->buffer();
295  ppl = buffer->bytesPerLine()>>2;
296  }
297 
298  // line drawing produces different results with different clips, so
299  // we need to clip consistently when painting to the same device
300 
301  // setup FP clip bounds
302  xmin = deviceRect.left() - 1;
303  xmax = deviceRect.right() + 2;
304  ymin = deviceRect.top() - 1;
305  ymax = deviceRect.bottom() + 2;
306 
307  lastPixel.x = -1;
308  lastPixel.y = -1;
309 }
310 
311 // returns true if the whole line gets clipped away
313 {
314  // basic/rough clipping is done in floating point coordinates to avoid
315  // integer overflow problems.
316  if (x1 < xmin) {
317  if (x2 <= xmin)
318  goto clipped;
319  y1 += (y2 - y1)/(x2 - x1) * (xmin - x1);
320  x1 = xmin;
321  } else if (x1 > xmax) {
322  if (x2 >= xmax)
323  goto clipped;
324  y1 += (y2 - y1)/(x2 - x1) * (xmax - x1);
325  x1 = xmax;
326  }
327  if (x2 < xmin) {
328  lastPixel.x = -1;
329  y2 += (y2 - y1)/(x2 - x1) * (xmin - x2);
330  x2 = xmin;
331  } else if (x2 > xmax) {
332  lastPixel.x = -1;
333  y2 += (y2 - y1)/(x2 - x1) * (xmax - x2);
334  x2 = xmax;
335  }
336 
337  if (y1 < ymin) {
338  if (y2 <= ymin)
339  goto clipped;
340  x1 += (x2 - x1)/(y2 - y1) * (ymin - y1);
341  y1 = ymin;
342  } else if (y1 > ymax) {
343  if (y2 >= ymax)
344  goto clipped;
345  x1 += (x2 - x1)/(y2 - y1) * (ymax - y1);
346  y1 = ymax;
347  }
348  if (y2 < ymin) {
349  lastPixel.x = -1;
350  x2 += (x2 - x1)/(y2 - y1) * (ymin - y2);
351  y2 = ymin;
352  } else if (y2 > ymax) {
353  lastPixel.x = -1;
354  x2 += (x2 - x1)/(y2 - y1) * (ymax - y2);
355  y2 = ymax;
356  }
357 
358  return false;
359 
360  clipped:
361  lastPixel.x = -1;
362  return true;
363 }
364 
365 
366 void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2)
367 {
368  if (p1 == p2) {
369  drawPoints(&p1, 1);
370  return;
371  }
372 
373  QPointF start = p1 * state->matrix;
374  QPointF end = p2 * state->matrix;
375 
376  patternOffset = state->lastPen.dashOffset()*64;
377  lastPixel.x = -1;
378 
379  stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0);
380 
381  blend(current_span, spans, &state->penData);
382  current_span = 0;
383 }
384 
385 void QCosmeticStroker::drawPoints(const QPoint *points, int num)
386 {
387  const QPoint *end = points + num;
388  while (points < end) {
389  QPointF p = QPointF(*points) * state->matrix;
390  drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
391  ++points;
392  }
393 
394  blend(current_span, spans, &state->penData);
395  current_span = 0;
396 }
397 
398 void QCosmeticStroker::drawPoints(const QPointF *points, int num)
399 {
400  const QPointF *end = points + num;
401  while (points < end) {
402  QPointF p = (*points) * state->matrix;
403  drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
404  ++points;
405  }
406 
407  blend(current_span, spans, &state->penData);
408  current_span = 0;
409 }
410 
412 {
413  // this is basically the same code as used in the aliased stroke method,
414  // but it only determines the direction and last point of a line
415  //
416  // This is being used to have proper dropout control for closed contours
417  // by calculating the direction and last pixel of the last segment in the contour.
418  // the info is then used to perform dropout control when drawing the first line segment
419  // of the contour
420  lastPixel.x = -1;
421  lastPixel.y = -1;
422 
423  if (clipLine(rx1, ry1, rx2, ry2))
424  return;
425 
426  const int half = 31;
427  int x1 = toF26Dot6(rx1) + half;
428  int y1 = toF26Dot6(ry1) + half;
429  int x2 = toF26Dot6(rx2) + half;
430  int y2 = toF26Dot6(ry2) + half;
431 
432  int dx = qAbs(x2 - x1);
433  int dy = qAbs(y2 - y1);
434 
435  if (dx < dy) {
436  // vertical
437  bool swapped = false;
438  if (y1 > y2) {
439  swapped = true;
440  qSwap(y1, y2);
441  qSwap(x1, x2);
442  }
443  int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
444  int x = x1 << 10;
445 
446  int y = (y1 + 32) >> 6;
447  int ys = (y2 + 32) >> 6;
448 
449  if (y != ys) {
450  x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6;
451 
452  if (swapped) {
453  lastPixel.x = x >> 16;
454  lastPixel.y = y;
456  } else {
457  lastPixel.x = (x + (ys - y - 1)*xinc) >> 16;
458  lastPixel.y = ys - 1;
460  }
461  lastAxisAligned = qAbs(xinc) < (1 << 14);
462  }
463  } else {
464  // horizontal
465  if (!dx)
466  return;
467 
468  bool swapped = false;
469  if (x1 > x2) {
470  swapped = true;
471  qSwap(x1, x2);
472  qSwap(y1, y2);
473  }
474  int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
475  int y = y1 << 10;
476 
477  int x = (x1 + 32) >> 6;
478  int xs = (x2 + 32) >> 6;
479 
480  if (x != xs) {
481  y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6;
482 
483  if (swapped) {
484  lastPixel.x = x;
485  lastPixel.y = y >> 16;
487  } else {
488  lastPixel.x = xs - 1;
489  lastPixel.y = (y + (xs - x - 1)*yinc) >> 16;
491  }
492  lastAxisAligned = qAbs(yinc) < (1 << 14);
493  }
494  }
495 // qDebug() << " moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir;
496 }
497 
499  const qreal *points, bool *closed)
500 {
501  const QPainterPath::ElementType *start = t;
502  ++t;
503 
504  // find out if the subpath is closed
505  while (t < end) {
506  if (*t == QPainterPath::MoveToElement)
507  break;
508  ++t;
509  }
510 
511  int offset = t - start - 1;
512 // qDebug() << "subpath" << offset << points[0] << points[1] << points[2*offset] << points[2*offset+1];
513  *closed = (points[0] == points[2*offset] && points[1] == points[2*offset + 1]);
514 
515  return t;
516 }
517 
519 {
520 // qDebug() << ">>>> drawpath" << path.convertToPainterPath()
521 // << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose();
522  if (path.isEmpty())
523  return;
524 
525  const qreal *points = path.points();
526  const QPainterPath::ElementType *type = path.elements();
527 
528  if (type) {
529  const QPainterPath::ElementType *end = type + path.elementCount();
530 
531  while (type < end) {
532  Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement);
533 
534  QPointF p = QPointF(points[0], points[1]) * state->matrix;
535  patternOffset = state->lastPen.dashOffset()*64;
536  lastPixel.x = -1;
537  lastPixel.y = -1;
538 
539  bool closed;
540  const QPainterPath::ElementType *e = subPath(type, end, points, &closed);
541  if (closed) {
542  const qreal *p = points + 2*(e-type);
543  QPointF p1 = QPointF(p[-4], p[-3]) * state->matrix;
544  QPointF p2 = QPointF(p[-2], p[-1]) * state->matrix;
545  calculateLastPoint(p1.x(), p1.y(), p2.x(), p2.y());
546  }
547  int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
548 // qDebug() << "closed =" << closed << capString(caps);
549 
550  points += 2;
551  ++type;
552 
553  while (type < e) {
554  QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
555  switch (*type) {
557  Q_ASSERT(!"Logic error");
558  break;
559 
561  if (!closed && drawCaps && type == e - 1)
562  caps |= CapEnd;
563  stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
564  p = p2;
565  points += 2;
566  ++type;
567  break;
568 
570  if (!closed && drawCaps && type == e - 3)
571  caps |= CapEnd;
572  QPointF p3 = QPointF(points[2], points[3]) * state->matrix;
573  QPointF p4 = QPointF(points[4], points[5]) * state->matrix;
574  renderCubic(p, p2, p3, p4, caps);
575  p = p4;
576  type += 3;
577  points += 6;
578  break;
579  }
581  Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
582  break;
583  }
584  caps = NoCaps;
585  }
586  }
587  } else { // !type, simple polygon
588  QPointF p = QPointF(points[0], points[1]) * state->matrix;
589  QPointF movedTo = p;
590  patternOffset = state->lastPen.dashOffset()*64;
591  lastPixel.x = -1;
592  lastPixel.y = -1;
593 
594  const qreal *begin = points;
595  const qreal *end = points + 2*path.elementCount();
596  // handle closed path case
597  bool closed = path.hasImplicitClose() || (points[0] == end[-2] && points[1] == end[-1]);
598  int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
599  if (closed) {
600  QPointF p2 = QPointF(end[-2], end[-1]) * state->matrix;
601  calculateLastPoint(p2.x(), p2.y(), p.x(), p.y());
602  }
603 
604  bool fastPenAliased = (state->flags.fast_pen && !state->flags.antialiased);
605  points += 2;
606  while (points < end) {
607  QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
608 
609  if (!closed && drawCaps && points == end - 2)
610  caps |= CapEnd;
611 
612  QCosmeticStroker::Point last = this->lastPixel;
613  bool unclipped = stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
614 
615  /* fix for gaps in polylines with fastpen and aliased in a sequence
616  of points with small distances: if current point p2 has been dropped
617  out, keep last non dropped point p.
618 
619  However, if the line was completely outside the devicerect, we
620  still need to update p to avoid drawing the line after this one from
621  a bad starting position.
622  */
623  if (fastPenAliased && unclipped) {
624  if (last.x != lastPixel.x || last.y != lastPixel.y
625  || points == begin + 2 || points == end - 2) {
626  p = p2;
627  }
628  } else {
629  p = p2;
630  }
631  points += 2;
632  caps = NoCaps;
633  }
634  if (path.hasImplicitClose())
635  stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps);
636  }
637 
638 
639  blend(current_span, spans, &state->penData);
640  current_span = 0;
641 }
642 
643 void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps)
644 {
645 // qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps);
646  const int maxSubDivisions = 6;
647  PointF points[3*maxSubDivisions + 4];
648 
649  points[3].x = p1.x();
650  points[3].y = p1.y();
651  points[2].x = p2.x();
652  points[2].y = p2.y();
653  points[1].x = p3.x();
654  points[1].y = p3.y();
655  points[0].x = p4.x();
656  points[0].y = p4.y();
657 
658  PointF *p = points;
659  int level = maxSubDivisions;
660 
661  renderCubicSubdivision(p, level, caps);
662 }
663 
665 {
666  const qreal half = .5;
667  qreal a, b, c, d;
668 
669  points[6].x = points[3].x;
670  c = points[1].x;
671  d = points[2].x;
672  points[1].x = a = ( points[0].x + c ) * half;
673  points[5].x = b = ( points[3].x + d ) * half;
674  c = ( c + d ) * half;
675  points[2].x = a = ( a + c ) * half;
676  points[4].x = b = ( b + c ) * half;
677  points[3].x = ( a + b ) * half;
678 
679  points[6].y = points[3].y;
680  c = points[1].y;
681  d = points[2].y;
682  points[1].y = a = ( points[0].y + c ) * half;
683  points[5].y = b = ( points[3].y + d ) * half;
684  c = ( c + d ) * half;
685  points[2].y = a = ( a + c ) * half;
686  points[4].y = b = ( b + c ) * half;
687  points[3].y = ( a + b ) * half;
688 }
689 
691 {
692  if (level) {
693  qreal dx = points[3].x - points[0].x;
694  qreal dy = points[3].y - points[0].y;
695  qreal len = ((qreal).25) * (qAbs(dx) + qAbs(dy));
696 
697  if (qAbs(dx * (points[0].y - points[2].y) - dy * (points[0].x - points[2].x)) >= len ||
698  qAbs(dx * (points[0].y - points[1].y) - dy * (points[0].x - points[1].x)) >= len) {
699  splitCubic(points);
700 
701  --level;
702  renderCubicSubdivision(points + 3, level, caps & CapBegin);
703  renderCubicSubdivision(points, level, caps & CapEnd);
704  return;
705  }
706  }
707 
708  stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps);
709 }
710 
711 static inline int swapCaps(int caps)
712 {
713  return ((caps & QCosmeticStroker::CapBegin) << 1) |
714  ((caps & QCosmeticStroker::CapEnd) >> 1);
715 }
716 
717 // adjust line by half a pixel
718 static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc)
719 {
720  if (caps & QCosmeticStroker::CapBegin) {
721  x1 -= 32;
722  y -= yinc >> 1;
723  }
724  if (caps & QCosmeticStroker::CapEnd) {
725  x2 += 32;
726  }
727 }
728 
729 /*
730  The hard part about this is dropout control and avoiding douple drawing of points when
731  the drawing shifts from horizontal to vertical or back.
732  */
733 template<DrawPixel drawPixel, class Dasher>
734 static bool drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
735 {
736  if (stroker->clipLine(rx1, ry1, rx2, ry2))
737  return false;
738 
739  static const int half = 31;
740  int x1 = toF26Dot6(rx1) + half;
741  int y1 = toF26Dot6(ry1) + half;
742  int x2 = toF26Dot6(rx2) + half;
743  int y2 = toF26Dot6(ry2) + half;
744 
745  int dx = qAbs(x2 - x1);
746  int dy = qAbs(y2 - y1);
747 
748  QCosmeticStroker::Point last = stroker->lastPixel;
749 
750 // qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.;
751 
752  if (dx < dy) {
753  // vertical
755 
756  bool swapped = false;
757  if (y1 > y2) {
758  swapped = true;
759  qSwap(y1, y2);
760  qSwap(x1, x2);
761  caps = swapCaps(caps);
763  }
764  int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
765  int x = x1 << 10;
766 
767  if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir)
769 
770  capAdjust(caps, y1, y2, x, xinc);
771 
772  int y = (y1 + 32) >> 6;
773  int ys = (y2 + 32) >> 6;
774 
775  if (y != ys) {
776  x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6;
777 
778  // calculate first and last pixel and perform dropout control
780  first.x = x >> 16;
781  first.y = y;
782  last.x = (x + (ys - y - 1)*xinc) >> 16;
783  last.y = ys - 1;
784  if (swapped)
785  qSwap(first, last);
786 
787  bool axisAligned = qAbs(xinc) < (1 << 14);
788  if (stroker->lastPixel.x >= 0) {
789  if (first.x == stroker->lastPixel.x &&
790  first.y == stroker->lastPixel.y) {
791  // remove duplicated pixel
792  if (swapped) {
793  --ys;
794  } else {
795  ++y;
796  x += xinc;
797  }
798  } else if (stroker->lastDir != dir &&
799  (((axisAligned && stroker->lastAxisAligned) &&
800  stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
801  (qAbs(stroker->lastPixel.x - first.x) > 1 ||
802  qAbs(stroker->lastPixel.y - first.y) > 1))) {
803  // have a missing pixel, insert it
804  if (swapped) {
805  ++ys;
806  } else {
807  --y;
808  x -= xinc;
809  }
810  }
811  }
812  stroker->lastDir = dir;
813  stroker->lastAxisAligned = axisAligned;
814 
815  Dasher dasher(stroker, swapped, y << 6, ys << 6);
816 
817  do {
818  if (dasher.on())
819  drawPixel(stroker, x >> 16, y, 255);
820  dasher.adjust();
821  x += xinc;
822  } while (++y < ys);
823  }
824  } else {
825  // horizontal
826  if (!dx)
827  return true;
828 
830 
831  bool swapped = false;
832  if (x1 > x2) {
833  swapped = true;
834  qSwap(x1, x2);
835  qSwap(y1, y2);
836  caps = swapCaps(caps);
838  }
839  int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
840  int y = y1 << 10;
841 
842  if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir)
844 
845  capAdjust(caps, x1, x2, y, yinc);
846 
847  int x = (x1 + 32) >> 6;
848  int xs = (x2 + 32) >> 6;
849 
850  if (x != xs) {
851  y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6;
852 
853  // calculate first and last pixel to perform dropout control
855  first.x = x;
856  first.y = y >> 16;
857  last.x = xs - 1;
858  last.y = (y + (xs - x - 1)*yinc) >> 16;
859  if (swapped)
860  qSwap(first, last);
861 
862  bool axisAligned = qAbs(yinc) < (1 << 14);
863  if (stroker->lastPixel.x >= 0) {
864  if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) {
865  // remove duplicated pixel
866  if (swapped) {
867  --xs;
868  } else {
869  ++x;
870  y += yinc;
871  }
872  } else if (stroker->lastDir != dir &&
873  (((axisAligned && stroker->lastAxisAligned) &&
874  stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
875  (qAbs(stroker->lastPixel.x - first.x) > 1 ||
876  qAbs(stroker->lastPixel.y - first.y) > 1))) {
877  // have a missing pixel, insert it
878  if (swapped) {
879  ++xs;
880  } else {
881  --x;
882  y -= yinc;
883  }
884  }
885  }
886  stroker->lastDir = dir;
887  stroker->lastAxisAligned = axisAligned;
888 
889  Dasher dasher(stroker, swapped, x << 6, xs << 6);
890 
891  do {
892  if (dasher.on())
893  drawPixel(stroker, x, y >> 16, 255);
894  dasher.adjust();
895  y += yinc;
896  } while (++x < xs);
897  }
898  }
899  stroker->lastPixel = last;
900  return true;
901 }
902 
903 
904 template<DrawPixel drawPixel, class Dasher>
905 static bool drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
906 {
907  if (stroker->clipLine(rx1, ry1, rx2, ry2))
908  return false;
909 
910  int x1 = toF26Dot6(rx1);
911  int y1 = toF26Dot6(ry1);
912  int x2 = toF26Dot6(rx2);
913  int y2 = toF26Dot6(ry2);
914 
915  int dx = x2 - x1;
916  int dy = y2 - y1;
917 
918  if (qAbs(dx) < qAbs(dy)) {
919  // vertical
920 
921  int xinc = F16Dot16FixedDiv(dx, dy);
922 
923  bool swapped = false;
924  if (y1 > y2) {
925  qSwap(y1, y2);
926  qSwap(x1, x2);
927  swapped = true;
928  caps = swapCaps(caps);
929  }
930 
931  int x = (x1 - 32) << 10;
932  x -= ( ((y1 & 63) - 32) * xinc ) >> 6;
933 
934  capAdjust(caps, y1, y2, x, xinc);
935 
936  Dasher dasher(stroker, swapped, y1, y2);
937 
938  int y = y1 >> 6;
939  int ys = y2 >> 6;
940 
941  int alphaStart, alphaEnd;
942  if (y == ys) {
943  alphaStart = y2 - y1;
944  Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
945  alphaEnd = 0;
946  } else {
947  alphaStart = 64 - (y1 & 63);
948  alphaEnd = (y2 & 63);
949  }
950 // qDebug() << "vertical" << x1/64. << y1/64. << x2/64. << y2/64.;
951 // qDebug() << " x=" << x << "dx=" << dx << "xi=" << (x>>16) << "xsi=" << ((x+(ys-y)*dx)>>16) << "y=" << y << "ys=" << ys;
952 
953  // draw first pixel
954  if (dasher.on()) {
955  uint alpha = (quint8)(x >> 8);
956  drawPixel(stroker, x>>16, y, (255-alpha) * alphaStart >> 6);
957  drawPixel(stroker, (x>>16) + 1, y, alpha * alphaStart >> 6);
958  }
959  dasher.adjust();
960  x += xinc;
961  ++y;
962  if (y < ys) {
963  do {
964  if (dasher.on()) {
965  uint alpha = (quint8)(x >> 8);
966  drawPixel(stroker, x>>16, y, (255-alpha));
967  drawPixel(stroker, (x>>16) + 1, y, alpha);
968  }
969  dasher.adjust();
970  x += xinc;
971  } while (++y < ys);
972  }
973  // draw last pixel
974  if (alphaEnd && dasher.on()) {
975  uint alpha = (quint8)(x >> 8);
976  drawPixel(stroker, x>>16, y, (255-alpha) * alphaEnd >> 6);
977  drawPixel(stroker, (x>>16) + 1, y, alpha * alphaEnd >> 6);
978  }
979  } else {
980  // horizontal
981  if (!dx)
982  return true;
983 
984  int yinc = F16Dot16FixedDiv(dy, dx);
985 
986  bool swapped = false;
987  if (x1 > x2) {
988  qSwap(x1, x2);
989  qSwap(y1, y2);
990  swapped = true;
991  caps = swapCaps(caps);
992  }
993 
994  int y = (y1 - 32) << 10;
995  y -= ( ((x1 & 63) - 32) * yinc ) >> 6;
996 
997  capAdjust(caps, x1, x2, y, yinc);
998 
999  Dasher dasher(stroker, swapped, x1, x2);
1000 
1001  int x = x1 >> 6;
1002  int xs = x2 >> 6;
1003 
1004 // qDebug() << "horizontal" << x1/64. << y1/64. << x2/64. << y2/64.;
1005 // qDebug() << " y=" << y << "dy=" << dy << "x=" << x << "xs=" << xs << "yi=" << (y>>16) << "ysi=" << ((y+(xs-x)*dy)>>16);
1006  int alphaStart, alphaEnd;
1007  if (x == xs) {
1008  alphaStart = x2 - x1;
1009  Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
1010  alphaEnd = 0;
1011  } else {
1012  alphaStart = 64 - (x1 & 63);
1013  alphaEnd = (x2 & 63);
1014  }
1015 
1016  // draw first pixel
1017  if (dasher.on()) {
1018  uint alpha = (quint8)(y >> 8);
1019  drawPixel(stroker, x, y>>16, (255-alpha) * alphaStart >> 6);
1020  drawPixel(stroker, x, (y>>16) + 1, alpha * alphaStart >> 6);
1021  }
1022  dasher.adjust();
1023  y += yinc;
1024  ++x;
1025  // draw line
1026  if (x < xs) {
1027  do {
1028  if (dasher.on()) {
1029  uint alpha = (quint8)(y >> 8);
1030  drawPixel(stroker, x, y>>16, (255-alpha));
1031  drawPixel(stroker, x, (y>>16) + 1, alpha);
1032  }
1033  dasher.adjust();
1034  y += yinc;
1035  } while (++x < xs);
1036  }
1037  // draw last pixel
1038  if (alphaEnd && dasher.on()) {
1039  uint alpha = (quint8)(y >> 8);
1040  drawPixel(stroker, x, y>>16, (255-alpha) * alphaEnd >> 6);
1041  drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6);
1042  }
1043  }
1044  return true;
1045 }
1046 
double d
Definition: qnumeric_p.h:62
ElementType
This enum describes the types of elements used to connect vertices in subpaths.
Definition: qpainterpath.h:70
static StrokeLine strokeLine(int strokeSelection)
int type
Definition: qmetatype.cpp:239
double qreal
Definition: qglobal.h:1193
unsigned char c[8]
Definition: qnumeric_p.h:62
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
unsigned char coverage
static bool drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps)
int elementCount() const
bool isEmpty() const
#define toF26Dot6(x)
void drawLine(const QPointF &p1, const QPointF &p2)
unsigned short len
static C reverse(const C &l)
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:214
QT_FT_Span spans[NSPANS]
static bool clipLine(QLineF *line, const QRect &rect)
long ASN1_INTEGER_get ASN1_INTEGER * a
void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage)
unsigned char quint8
Definition: qglobal.h:934
int bottom() const
Returns the y-coordinate of the rectangle&#39;s bottom edge.
Definition: qrect.h:249
The QString class provides a Unicode character string.
Definition: qstring.h:83
int bytesPerLine() const
Q_DECL_CONSTEXPR T qAbs(const T &t)
Definition: qglobal.h:1201
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
static void splitCubic(QCosmeticStroker::PointF *points)
static int F16Dot16FixedDiv(int x, int y)
const QPainterPath::ElementType * elements() const
static void capAdjust(int caps, int &x1, int &x2, int &y, int yinc)
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
qreal x() const
Returns the x-coordinate of this point.
Definition: qpoint.h:282
int qAlpha(QRgb rgba)
Returns the alpha component of the ARGB quadruplet rgba.
Definition: qrgb.h:66
Q_STATIC_INLINE_FUNCTION uint BYTE_MUL(uint x, uint a)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
StrokeSelection
void renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps)
Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b)
#define QT_PREPEND_NAMESPACE(name)
This macro qualifies identifier with the full namespace.
Definition: qglobal.h:87
void(* DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage)
bool(* StrokeLine)(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps)
unsigned int uint
Definition: qglobal.h:996
static uint sourceOver(uint d, uint color)
void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int)
void qSwap(T &value1, T &value2)
Definition: qglobal.h:2181
const T & at(int i) const
Returns the item at index position i in the vector.
Definition: qvector.h:350
void drawPoints(const QPoint *points, int num)
static bool drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps)
int right() const
Returns the x-coordinate of the rectangle&#39;s right edge.
Definition: qrect.h:246
static int swapCaps(int caps)
int y() const
Returns the y-coordinate of the rectangle&#39;s top edge.
Definition: qrect.h:255
unsigned short ushort
Definition: qglobal.h:995
int x() const
Returns the x-coordinate of the rectangle&#39;s left edge.
Definition: qrect.h:252
The QPoint class defines a point in the plane using integer precision.
Definition: qpoint.h:53
Q_DECL_CONSTEXPR const T & qBound(const T &min, const T &val, const T &max)
Definition: qglobal.h:1219
const qreal * points() const
The QRect class defines a rectangle in the plane using integer precision.
Definition: qrect.h:58
bool hasImplicitClose() const
static void drawPoints(const T *points, int n, const QTransform &transform, IDirectFBSurface *surface)
qreal y() const
Returns the y-coordinate of this point.
Definition: qpoint.h:287
void calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2)
uchar * buffer() const
bool isEmpty() const
Returns true if the vector has size 0; otherwise returns false.
Definition: qvector.h:139
void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage)
bool clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2)
static const KeyPair *const end
qint64 qlonglong
Definition: qglobal.h:951
void drawPath(const QVectorPath &path)
int size() const
Returns the number of items in the vector.
Definition: qvector.h:137
QRasterPaintEngineState * state
void renderCubicSubdivision(PointF *points, int level, int caps)
static const QPainterPath::ElementType * subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end, const qreal *points, bool *closed)
Q_DECL_CONSTEXPR int qRound(qreal d)
Definition: qglobal.h:1203