src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
2:56cd8111b7f7 3:41300fa6a67c
    40 ****************************************************************************/
    40 ****************************************************************************/
    41 
    41 
    42 #include "qtriangulatingstroker_p.h"
    42 #include "qtriangulatingstroker_p.h"
    43 #include <qmath.h>
    43 #include <qmath.h>
    44 
    44 
       
    45 QT_BEGIN_NAMESPACE
    45 
    46 
    46 #define CURVE_FLATNESS Q_PI / 8
    47 #define CURVE_FLATNESS Q_PI / 8
    47 
    48 
    48 
    49 
    49 
    50 
    58         lineTo(start);
    59         lineTo(start);
    59         join(start+2);
    60         join(start+2);
    60     } else {
    61     } else {
    61         endCap(cur);
    62         endCap(cur);
    62     }
    63     }
       
    64     int count = m_vertices.size();
       
    65 
       
    66     // Copy the (x, y) values because QDataBuffer::add(const float& t)
       
    67     // may resize the buffer, which will leave t pointing at the
       
    68     // previous buffer's memory region if we don't copy first.
       
    69     float x = m_vertices.at(count-2);
       
    70     float y = m_vertices.at(count-1);
       
    71     m_vertices.add(x);
       
    72     m_vertices.add(y);
    63 }
    73 }
    64 
    74 
    65 
    75 
    66 void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
    76 void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
    67 {
    77 {
   111         if (m_cap_style == Qt::RoundCap)
   121         if (m_cap_style == Qt::RoundCap)
   112             m_cap_style = Qt::SquareCap;
   122             m_cap_style = Qt::SquareCap;
   113         if (m_join_style == Qt::RoundJoin)
   123         if (m_join_style == Qt::RoundJoin)
   114             m_join_style = Qt::MiterJoin;
   124             m_join_style = Qt::MiterJoin;
   115         m_curvyness_add = 0.5;
   125         m_curvyness_add = 0.5;
   116         m_curvyness_mul = CURVE_FLATNESS;
   126         m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
   117         m_roundness = 1;
   127         m_roundness = 1;
   118     } else if (cosmetic) {
   128     } else if (cosmetic) {
   119         m_curvyness_add = realWidth / 2;
   129         m_curvyness_add = realWidth / 2;
   120         m_curvyness_mul = CURVE_FLATNESS;
   130         m_curvyness_mul = CURVE_FLATNESS;
   121         m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS);
   131         m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS);
   128     // Over this level of segmentation, there doesn't seem to be any
   138     // Over this level of segmentation, there doesn't seem to be any
   129     // benefit, even for huge penWidth
   139     // benefit, even for huge penWidth
   130     if (m_roundness > 24)
   140     if (m_roundness > 24)
   131         m_roundness = 24;
   141         m_roundness = 24;
   132 
   142 
   133     m_sin_theta = qSin(Q_PI / m_roundness); // ### Use qFastSin
   143     m_sin_theta = qFastSin(Q_PI / m_roundness);
   134     m_cos_theta = qCos(Q_PI / m_roundness);
   144     m_cos_theta = qFastCos(Q_PI / m_roundness);
   135 
   145 
   136     const qreal *endPts = pts + (count<<1);
   146     const qreal *endPts = pts + (count<<1);
   137     const qreal *startPts;
   147     const qreal *startPts;
   138 
   148 
   139     Qt::PenCapStyle cap = m_cap_style;
   149     Qt::PenCapStyle cap = m_cap_style;
   141     if (!types) {
   151     if (!types) {
   142         startPts = pts;
   152         startPts = pts;
   143 
   153 
   144         bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1);
   154         bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1);
   145 
   155 
   146         Qt::PenCapStyle cap = m_cap_style;
       
   147         if (endsAtStart || path.hasImplicitClose())
   156         if (endsAtStart || path.hasImplicitClose())
   148             m_cap_style = Qt::FlatCap;
   157             m_cap_style = Qt::FlatCap;
   149         moveTo(pts);
   158         moveTo(pts);
   150         m_cap_style = cap;
   159         m_cap_style = cap;
   151         pts += 2;
   160         pts += 2;
   163         bool endsAtStart;
   172         bool endsAtStart;
   164         while (pts < endPts) {
   173         while (pts < endPts) {
   165             switch (*types) {
   174             switch (*types) {
   166             case QPainterPath::MoveToElement: {
   175             case QPainterPath::MoveToElement: {
   167                 if (pts != path.points())
   176                 if (pts != path.points())
   168                     endCapOrJoinClosed(startPts, pts, path.hasImplicitClose(), endsAtStart);
   177                     endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
   169 
   178 
   170                 startPts = pts;
   179                 startPts = pts;
   171                 int end = (endPts - pts) / 2;
   180                 int end = (endPts - pts) / 2;
   172                 int i = 2; // Start looking to ahead since we never have two moveto's in a row
   181                 int i = 2; // Start looking to ahead since we never have two moveto's in a row
   173                 while (i<end && types[i] != QPainterPath::MoveToElement) {
   182                 while (i<end && types[i] != QPainterPath::MoveToElement) {
   204 
   213 
   205         endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
   214         endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
   206     }
   215     }
   207 }
   216 }
   208 
   217 
       
   218 void QTriangulatingStroker::moveTo(const qreal *pts)
       
   219 {
       
   220     m_cx = pts[0];
       
   221     m_cy = pts[1];
       
   222 
       
   223     float x2 = pts[2];
       
   224     float y2 = pts[3];
       
   225     normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy);
       
   226 
       
   227 
       
   228     // To acheive jumps we insert zero-area tringles. This is done by
       
   229     // adding two identical points in both the end of previous strip
       
   230     // and beginning of next strip
       
   231     bool invisibleJump = m_vertices.size();
       
   232 
       
   233     switch (m_cap_style) {
       
   234     case Qt::FlatCap:
       
   235         if (invisibleJump) {
       
   236             m_vertices.add(m_cx + m_nvx);
       
   237             m_vertices.add(m_cy + m_nvy);
       
   238         }
       
   239         break;
       
   240     case Qt::SquareCap: {
       
   241         float sx = m_cx - m_nvy;
       
   242         float sy = m_cy + m_nvx;
       
   243         if (invisibleJump) {
       
   244             m_vertices.add(sx + m_nvx);
       
   245             m_vertices.add(sy + m_nvy);
       
   246         }
       
   247         emitLineSegment(sx, sy, m_nvx, m_nvy);
       
   248         break; }
       
   249     case Qt::RoundCap: {
       
   250         QVarLengthArray<float> points;
       
   251         arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points);
       
   252         m_vertices.resize(m_vertices.size() + points.size() + 2 * int(invisibleJump));
       
   253         int count = m_vertices.size();
       
   254         int front = 0;
       
   255         int end = points.size() / 2;
       
   256         while (front != end) {
       
   257             m_vertices.at(--count) = points[2 * end - 1];
       
   258             m_vertices.at(--count) = points[2 * end - 2];
       
   259             --end;
       
   260             if (front == end)
       
   261                 break;
       
   262             m_vertices.at(--count) = points[2 * front + 1];
       
   263             m_vertices.at(--count) = points[2 * front + 0];
       
   264             ++front;
       
   265         }
       
   266 
       
   267         if (invisibleJump) {
       
   268             m_vertices.at(count - 1) = m_vertices.at(count + 1);
       
   269             m_vertices.at(count - 2) = m_vertices.at(count + 0);
       
   270         }
       
   271         break; }
       
   272     default: break; // ssssh gcc...
       
   273     }
       
   274     emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
       
   275 }
       
   276 
   209 void QTriangulatingStroker::cubicTo(const qreal *pts)
   277 void QTriangulatingStroker::cubicTo(const qreal *pts)
   210 {
   278 {
   211     const QPointF *p = (const QPointF *) pts;
   279     const QPointF *p = (const QPointF *) pts;
   212     QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);
   280     QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);
   213 
   281 
   241 
   309 
   242     m_nvx = vx;
   310     m_nvx = vx;
   243     m_nvy = vy;
   311     m_nvy = vy;
   244 }
   312 }
   245 
   313 
   246 
   314 void QTriangulatingStroker::join(const qreal *pts)
       
   315 {
       
   316     // Creates a join to the next segment (m_cx, m_cy) -> (pts[0], pts[1])
       
   317     normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy);
       
   318 
       
   319     switch (m_join_style) {
       
   320     case Qt::BevelJoin:
       
   321         break;
       
   322     case Qt::SvgMiterJoin:
       
   323     case Qt::MiterJoin: {
       
   324         // Find out on which side the join should be.
       
   325         int count = m_vertices.size();
       
   326         float prevNvx = m_vertices.at(count - 2) - m_cx;
       
   327         float prevNvy = m_vertices.at(count - 1) - m_cy;
       
   328         float xprod = prevNvx * m_nvy - prevNvy * m_nvx;
       
   329         float px, py, qx, qy;
       
   330 
       
   331         // If the segments are parallel, use bevel join.
       
   332         if (qFuzzyIsNull(xprod))
       
   333             break;
       
   334 
       
   335         // Find the corners of the previous and next segment to join.
       
   336         if (xprod < 0) {
       
   337             px = m_vertices.at(count - 2);
       
   338             py = m_vertices.at(count - 1);
       
   339             qx = m_cx - m_nvx;
       
   340             qy = m_cy - m_nvy;
       
   341         } else {
       
   342             px = m_vertices.at(count - 4);
       
   343             py = m_vertices.at(count - 3);
       
   344             qx = m_cx + m_nvx;
       
   345             qy = m_cy + m_nvy;
       
   346         }
       
   347 
       
   348         // Find intersection point.
       
   349         float pu = px * prevNvx + py * prevNvy;
       
   350         float qv = qx * m_nvx + qy * m_nvy;
       
   351         float ix = (m_nvy * pu - prevNvy * qv) / xprod;
       
   352         float iy = (prevNvx * qv - m_nvx * pu) / xprod;
       
   353 
       
   354         // Check that the distance to the intersection point is less than the miter limit.
       
   355         if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) {
       
   356             m_vertices.add(ix);
       
   357             m_vertices.add(iy);
       
   358             m_vertices.add(ix);
       
   359             m_vertices.add(iy);
       
   360         }
       
   361         // else
       
   362         // Do a plain bevel join if the miter limit is exceeded or if
       
   363         // the lines are parallel. This is not what the raster
       
   364         // engine's stroker does, but it is both faster and similar to
       
   365         // what some other graphics API's do.
       
   366 
       
   367         break; }
       
   368     case Qt::RoundJoin: {
       
   369         QVarLengthArray<float> points;
       
   370         int count = m_vertices.size();
       
   371         float prevNvx = m_vertices.at(count - 2) - m_cx;
       
   372         float prevNvy = m_vertices.at(count - 1) - m_cy;
       
   373         if (m_nvx * prevNvy - m_nvy * prevNvx < 0) {
       
   374             arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points);
       
   375             for (int i = points.size() / 2; i > 0; --i)
       
   376                 emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]);
       
   377         } else {
       
   378             arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points);
       
   379             for (int i = 0; i < points.size() / 2; ++i)
       
   380                 emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]);
       
   381         }
       
   382         break; }
       
   383     default: break; // gcc warn--
       
   384     }
       
   385 
       
   386     emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
       
   387 }
       
   388 
       
   389 void QTriangulatingStroker::endCap(const qreal *)
       
   390 {
       
   391     switch (m_cap_style) {
       
   392     case Qt::FlatCap:
       
   393         break;
       
   394     case Qt::SquareCap:
       
   395         emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy);
       
   396         break;
       
   397     case Qt::RoundCap: {
       
   398         QVarLengthArray<float> points;
       
   399         int count = m_vertices.size();
       
   400         arcPoints(m_cx, m_cy, m_vertices.at(count - 2), m_vertices.at(count - 1), m_vertices.at(count - 4), m_vertices.at(count - 3), points);
       
   401         int front = 0;
       
   402         int end = points.size() / 2;
       
   403         while (front != end) {
       
   404             m_vertices.add(points[2 * end - 2]);
       
   405             m_vertices.add(points[2 * end - 1]);
       
   406             --end;
       
   407             if (front == end)
       
   408                 break;
       
   409             m_vertices.add(points[2 * front + 0]);
       
   410             m_vertices.add(points[2 * front + 1]);
       
   411             ++front;
       
   412         }
       
   413         break; }
       
   414     default: break; // to shut gcc up...
       
   415     }
       
   416 }
       
   417 
       
   418 void QTriangulatingStroker::arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray<float> &points)
       
   419 {
       
   420     float dx1 = fromX - cx;
       
   421     float dy1 = fromY - cy;
       
   422     float dx2 = toX - cx;
       
   423     float dy2 = toY - cy;
       
   424 
       
   425     // while more than 180 degrees left:
       
   426     while (dx1 * dy2 - dx2 * dy1 < 0) {
       
   427         float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
       
   428         float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
       
   429         dx1 = tmpx;
       
   430         dy1 = tmpy;
       
   431         points.append(cx + dx1);
       
   432         points.append(cy + dy1);
       
   433     }
       
   434 
       
   435     // while more than 90 degrees left:
       
   436     while (dx1 * dx2 + dy1 * dy2 < 0) {
       
   437         float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
       
   438         float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
       
   439         dx1 = tmpx;
       
   440         dy1 = tmpy;
       
   441         points.append(cx + dx1);
       
   442         points.append(cy + dy1);
       
   443     }
       
   444 
       
   445     // while more than 0 degrees left:
       
   446     while (dx1 * dy2 - dx2 * dy1 > 0) {
       
   447         float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
       
   448         float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
       
   449         dx1 = tmpx;
       
   450         dy1 = tmpy;
       
   451         points.append(cx + dx1);
       
   452         points.append(cy + dy1);
       
   453     }
       
   454 
       
   455     // remove last point which was rotated beyond [toX, toY].
       
   456     if (!points.isEmpty())
       
   457         points.resize(points.size() - 2);
       
   458 }
   247 
   459 
   248 static void qdashprocessor_moveTo(qreal x, qreal y, void *data)
   460 static void qdashprocessor_moveTo(qreal x, qreal y, void *data)
   249 {
   461 {
   250     ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
   462     ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
   251 }
   463 }
   276     int count = path.elementCount();
   488     int count = path.elementCount();
   277 
   489 
   278     m_points.reset();
   490     m_points.reset();
   279     m_types.reset();
   491     m_types.reset();
   280 
   492 
   281     qreal width = pen.width();
   493     qreal width = qpen_widthf(pen);
   282     if (width == 0)
   494     if (width == 0)
   283         width = 1;
   495         width = 1;
   284 
   496 
   285     m_dash_stroker.setDashPattern(pen.dashPattern());
   497     m_dash_stroker.setDashPattern(pen.dashPattern());
   286     m_dash_stroker.setStrokeWidth(width);
   498     m_dash_stroker.setStrokeWidth(pen.isCosmetic() ? width * m_inv_scale : width);
   287     m_dash_stroker.setMiterLimit(pen.miterLimit());
   499     m_dash_stroker.setMiterLimit(pen.miterLimit());
   288     qreal curvyness = sqrt(width) * m_inv_scale / 8;
   500     qreal curvyness = sqrt(width) * m_inv_scale / 8;
   289 
   501 
   290     if (count < 2)
   502     if (count < 2)
   291         return;
   503         return;
   336         }
   548         }
   337     }
   549     }
   338 
   550 
   339     m_dash_stroker.end();
   551     m_dash_stroker.end();
   340 }
   552 }
       
   553 
       
   554 QT_END_NAMESPACE
       
   555