src/opengl/gl2paintengineex/qtriangulatingstroker.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtOpenGL module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qtriangulatingstroker_p.h"
       
    43 #include <qmath.h>
       
    44 
       
    45 
       
    46 #define CURVE_FLATNESS Q_PI / 8
       
    47 
       
    48 
       
    49 
       
    50 
       
    51 void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur,
       
    52                                                bool implicitClose, bool endsAtStart)
       
    53 {
       
    54     if (endsAtStart) {
       
    55         join(start + 2);
       
    56     } else if (implicitClose) {
       
    57         join(start);
       
    58         lineTo(start);
       
    59         join(start+2);
       
    60     } else {
       
    61         endCap(cur);
       
    62     }
       
    63 }
       
    64 
       
    65 
       
    66 void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
       
    67 {
       
    68     const qreal *pts = path.points();
       
    69     const QPainterPath::ElementType *types = path.elements();
       
    70     int count = path.elementCount();
       
    71     if (count < 2)
       
    72         return;
       
    73 
       
    74     float realWidth = qpen_widthf(pen);
       
    75     if (realWidth == 0)
       
    76         realWidth = 1;
       
    77 
       
    78     m_width = realWidth / 2;
       
    79 
       
    80     bool cosmetic = pen.isCosmetic();
       
    81     if (cosmetic) {
       
    82         m_width = m_width * m_inv_scale;
       
    83     }
       
    84 
       
    85     m_join_style = qpen_joinStyle(pen);
       
    86     m_cap_style = qpen_capStyle(pen);
       
    87     m_vertices.reset();
       
    88     m_miter_limit = pen.miterLimit() * qpen_widthf(pen);
       
    89 
       
    90     // The curvyness is based on the notion that I originally wanted
       
    91     // roughly one line segment pr 4 pixels. This may seem little, but
       
    92     // because we sample at constantly incrementing B(t) E [0<t<1], we
       
    93     // will get longer segments where the curvature is small and smaller
       
    94     // segments when the curvature is high.
       
    95     //
       
    96     // To get a rough idea of the length of each curve, I pretend that
       
    97     // the curve is a 90 degree arc, whose radius is
       
    98     // qMax(curveBounds.width, curveBounds.height). Based on this
       
    99     // logic we can estimate the length of the outline edges based on
       
   100     // the radius + a pen width and adjusting for scale factors
       
   101     // depending on if the pen is cosmetic or not.
       
   102     //
       
   103     // The curvyness value of PI/14 was based on,
       
   104     // arcLength=2*PI*r/4=PI/2 and splitting length into somewhere
       
   105     // between 3 and 8 where 5 seemed to be give pretty good results
       
   106     // hence: Q_PI/14. Lower divisors will give more detail at the
       
   107     // direct cost of performance.
       
   108 
       
   109     // simplfy pens that are thin in device size (2px wide or less)
       
   110     if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) {
       
   111         if (m_cap_style == Qt::RoundCap)
       
   112             m_cap_style = Qt::SquareCap;
       
   113         if (m_join_style == Qt::RoundJoin)
       
   114             m_join_style = Qt::MiterJoin;
       
   115         m_curvyness_add = 0.5;
       
   116         m_curvyness_mul = CURVE_FLATNESS;
       
   117         m_roundness = 1;
       
   118     } else if (cosmetic) {
       
   119         m_curvyness_add = realWidth / 2;
       
   120         m_curvyness_mul = CURVE_FLATNESS;
       
   121         m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS);
       
   122     } else {
       
   123         m_curvyness_add = m_width;
       
   124         m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
       
   125         m_roundness = qMax<int>(4, realWidth * m_curvyness_mul);
       
   126     }
       
   127 
       
   128     // Over this level of segmentation, there doesn't seem to be any
       
   129     // benefit, even for huge penWidth
       
   130     if (m_roundness > 24)
       
   131         m_roundness = 24;
       
   132 
       
   133     m_sin_theta = qSin(Q_PI / m_roundness); // ### Use qFastSin
       
   134     m_cos_theta = qCos(Q_PI / m_roundness);
       
   135 
       
   136     const qreal *endPts = pts + (count<<1);
       
   137     const qreal *startPts;
       
   138 
       
   139     Qt::PenCapStyle cap = m_cap_style;
       
   140 
       
   141     if (!types) {
       
   142         startPts = pts;
       
   143 
       
   144         bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1);
       
   145 
       
   146         Qt::PenCapStyle cap = m_cap_style;
       
   147         if (endsAtStart || path.hasImplicitClose())
       
   148             m_cap_style = Qt::FlatCap;
       
   149         moveTo(pts);
       
   150         m_cap_style = cap;
       
   151         pts += 2;
       
   152         lineTo(pts);
       
   153         pts += 2;
       
   154         while (pts < endPts) {
       
   155             join(pts);
       
   156             lineTo(pts);
       
   157             pts += 2;
       
   158         }
       
   159 
       
   160         endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
       
   161 
       
   162     } else {
       
   163         bool endsAtStart;
       
   164         while (pts < endPts) {
       
   165             switch (*types) {
       
   166             case QPainterPath::MoveToElement: {
       
   167                 if (pts != path.points())
       
   168                     endCapOrJoinClosed(startPts, pts, path.hasImplicitClose(), endsAtStart);
       
   169 
       
   170                 startPts = pts;
       
   171                 int end = (endPts - pts) / 2;
       
   172                 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) {
       
   174                     ++i;
       
   175                 }
       
   176                 endsAtStart = startPts[0] == pts[i*2 - 2] && startPts[1] == pts[i*2 - 1];
       
   177                 if (endsAtStart || path.hasImplicitClose())
       
   178                     m_cap_style = Qt::FlatCap;
       
   179 
       
   180                 moveTo(pts);
       
   181                 m_cap_style = cap;
       
   182                 pts+=2;
       
   183                 ++types;
       
   184                 break; }
       
   185             case QPainterPath::LineToElement:
       
   186                 if (*(types - 1) != QPainterPath::MoveToElement)
       
   187                     join(pts);
       
   188                 lineTo(pts);
       
   189                 pts+=2;
       
   190                 ++types;
       
   191                 break;
       
   192             case QPainterPath::CurveToElement:
       
   193                 if (*(types - 1) != QPainterPath::MoveToElement)
       
   194                     join(pts);
       
   195                 cubicTo(pts);
       
   196                 pts+=6;
       
   197                 types+=3;
       
   198                 break;
       
   199             default:
       
   200                 Q_ASSERT(false);
       
   201                 break;
       
   202             }
       
   203         }
       
   204 
       
   205         endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
       
   206     }
       
   207 }
       
   208 
       
   209 void QTriangulatingStroker::cubicTo(const qreal *pts)
       
   210 {
       
   211     const QPointF *p = (const QPointF *) pts;
       
   212     QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);
       
   213 
       
   214     QRectF bounds = bezier.bounds();
       
   215     float rad = qMax(bounds.width(), bounds.height());
       
   216     int threshold = qMin<float>(64, (rad + m_curvyness_add) * m_curvyness_mul);
       
   217     if (threshold < 4)
       
   218         threshold = 4;
       
   219     qreal threshold_minus_1 = threshold - 1;
       
   220     float vx, vy;
       
   221 
       
   222     float cx = m_cx, cy = m_cy;
       
   223     float x, y;
       
   224 
       
   225     for (int i=1; i<threshold; ++i) {
       
   226         qreal t = qreal(i) / threshold_minus_1;
       
   227         QPointF p = bezier.pointAt(t);
       
   228         x = p.x();
       
   229         y = p.y();
       
   230 
       
   231         normalVector(cx, cy, x, y, &vx, &vy);
       
   232 
       
   233         emitLineSegment(x, y, vx, vy);
       
   234 
       
   235         cx = x;
       
   236         cy = y;
       
   237     }
       
   238 
       
   239     m_cx = cx;
       
   240     m_cy = cy;
       
   241 
       
   242     m_nvx = vx;
       
   243     m_nvy = vy;
       
   244 }
       
   245 
       
   246 
       
   247 
       
   248 static void qdashprocessor_moveTo(qreal x, qreal y, void *data)
       
   249 {
       
   250     ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
       
   251 }
       
   252 
       
   253 static void qdashprocessor_lineTo(qreal x, qreal y, void *data)
       
   254 {
       
   255     ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y);
       
   256 }
       
   257 
       
   258 static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *)
       
   259 {
       
   260     Q_ASSERT(0); // The dasher should not produce curves...
       
   261 }
       
   262 
       
   263 QDashedStrokeProcessor::QDashedStrokeProcessor()
       
   264     : m_dash_stroker(0), m_inv_scale(1)
       
   265 {
       
   266     m_dash_stroker.setMoveToHook(qdashprocessor_moveTo);
       
   267     m_dash_stroker.setLineToHook(qdashprocessor_lineTo);
       
   268     m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo);
       
   269 }
       
   270 
       
   271 void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen)
       
   272 {
       
   273 
       
   274     const qreal *pts = path.points();
       
   275     const QPainterPath::ElementType *types = path.elements();
       
   276     int count = path.elementCount();
       
   277 
       
   278     m_points.reset();
       
   279     m_types.reset();
       
   280 
       
   281     qreal width = pen.width();
       
   282     if (width == 0)
       
   283         width = 1;
       
   284 
       
   285     m_dash_stroker.setDashPattern(pen.dashPattern());
       
   286     m_dash_stroker.setStrokeWidth(width);
       
   287     m_dash_stroker.setMiterLimit(pen.miterLimit());
       
   288     qreal curvyness = sqrt(width) * m_inv_scale / 8;
       
   289 
       
   290     if (count < 2)
       
   291         return;
       
   292 
       
   293     const qreal *endPts = pts + (count<<1);
       
   294 
       
   295     m_dash_stroker.begin(this);
       
   296 
       
   297     if (!types) {
       
   298         m_dash_stroker.moveTo(pts[0], pts[1]);
       
   299         pts += 2;
       
   300         while (pts < endPts) {
       
   301             m_dash_stroker.lineTo(pts[0], pts[1]);
       
   302             pts += 2;
       
   303         }
       
   304     } else {
       
   305         while (pts < endPts) {
       
   306             switch (*types) {
       
   307             case QPainterPath::MoveToElement:
       
   308                 m_dash_stroker.moveTo(pts[0], pts[1]);
       
   309                 pts += 2;
       
   310                 ++types;
       
   311                 break;
       
   312             case QPainterPath::LineToElement:
       
   313                 m_dash_stroker.lineTo(pts[0], pts[1]);
       
   314                 pts += 2;
       
   315                 ++types;
       
   316                 break;
       
   317             case QPainterPath::CurveToElement: {
       
   318                 QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1),
       
   319                                                 *(((const QPointF *) pts)),
       
   320                                                 *(((const QPointF *) pts) + 1),
       
   321                                                 *(((const QPointF *) pts) + 2));
       
   322                 QRectF bounds = b.bounds();
       
   323                 int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * curvyness);
       
   324                 if (threshold < 4)
       
   325                     threshold = 4;
       
   326                 qreal threshold_minus_1 = threshold - 1;
       
   327                 for (int i=0; i<threshold; ++i) {
       
   328                     QPointF pt = b.pointAt(i / threshold_minus_1);
       
   329                     m_dash_stroker.lineTo(pt.x(), pt.y());
       
   330                 }
       
   331                 pts += 6;
       
   332                 types += 3;
       
   333                 break; }
       
   334             default: break;
       
   335             }
       
   336         }
       
   337     }
       
   338 
       
   339     m_dash_stroker.end();
       
   340 }