Improved path filling performance in the raster paint engine.
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>
Mon, 12 Dec 2011 11:57:08 +0000 (12:57 +0100)
committerQt by Nokia <qt-info@nokia.com>
Thu, 15 Dec 2011 23:51:47 +0000 (00:51 +0100)
Convert bezier curves to polylines before rasterizing with gray
raster.

Change-Id: I353debd4338f2a3ce2fa1cfa1bff9dd2e36f05ab
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
src/gui/painting/qbezier.cpp
src/gui/painting/qbezier_p.h
src/gui/painting/qoutlinemapper.cpp
src/gui/painting/qoutlinemapper_p.h

index 9d204f9..bdba3f2 100644 (file)
@@ -220,6 +220,38 @@ void QBezier::addToPolygon(QPolygonF *polygon, qreal bezier_flattening_threshold
     }
 }
 
+void QBezier::addToPolygon(QDataBuffer<QPointF> &polygon, qreal bezier_flattening_threshold) const
+{
+    QBezier beziers[32];
+    beziers[0] = *this;
+    QBezier *b = beziers;
+
+    while (b >= beziers) {
+        // check if we can pop the top bezier curve from the stack
+        qreal y4y1 = b->y4 - b->y1;
+        qreal x4x1 = b->x4 - b->x1;
+        qreal l = qAbs(x4x1) + qAbs(y4y1);
+        qreal d;
+        if (l > 1.) {
+            d = qAbs( (x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2) )
+                + qAbs( (x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3) );
+        } else {
+            d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+                qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+            l = 1.;
+        }
+        if (d < bezier_flattening_threshold*l || b == beziers + 31) {
+            // good enough, we pop it off and add the endpoint
+            polygon.add(QPointF(b->x4, b->y4));
+            --b;
+        } else {
+            // split, second half of the polygon goes lower into the stack
+            b->split(b+1, b);
+            ++b;
+        }
+    }
+}
+
 QRectF QBezier::bounds() const
 {
     qreal xmin = x1;
index f1f7eb1..e8594ff 100644 (file)
@@ -60,6 +60,7 @@
 #include "QtCore/qlist.h"
 #include "QtCore/qpair.h"
 #include "QtGui/qtransform.h"
+#include <private/qdatabuffer_p.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -81,6 +82,7 @@ public:
 
     QPolygonF toPolygon(qreal bezier_flattening_threshold = 0.5) const;
     void addToPolygon(QPolygonF *p, qreal bezier_flattening_threshold = 0.5) const;
+    void addToPolygon(QDataBuffer<QPointF> &polygon, qreal bezier_flattening_threshold) const;
 
     QRectF bounds() const;
     qreal length(qreal error = 0.01) const;
index 8b607b2..1aa7759 100644 (file)
@@ -42,6 +42,7 @@
 #include "qoutlinemapper_p.h"
 #include <private/qpainterpath_p.h>
 #include "qmath.h"
+#include <private/qbezier_p.h>
 
 #include <stdlib.h>
 
@@ -74,6 +75,19 @@ static const QRectF boundingRect(const QPointF *points, int pointCount)
     return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
 }
 
+void QOutlineMapper::curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) {
+#ifdef QT_DEBUG_CONVERT
+    printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y());
+#endif
+
+    QBezier bezier = QBezier::fromPoints(m_elements.last(), cp1, cp2, ep);
+    bezier.addToPolygon(m_elements, m_curve_threshold);
+    m_element_types.reserve(m_elements.size());
+    for (int i = m_elements.size() - m_element_types.size(); i; --i)
+        m_element_types << QPainterPath::LineToElement;
+    Q_ASSERT(m_elements.size() == m_element_types.size());
+}
+
 
 QT_FT_Outline *QOutlineMapper::convertPath(const QPainterPath &path)
 {
@@ -169,51 +183,47 @@ void QOutlineMapper::endOutline()
 {
     closeSubpath();
 
-    int element_count = m_elements.size();
-
-    if (element_count == 0) {
+    if (m_elements.isEmpty()) {
         memset(&m_outline, 0, sizeof(m_outline));
         return;
     }
 
-    QPointF *elements;
+    QPointF *elements = m_elements.data();
 
     // Transform the outline
     if (m_txop == QTransform::TxNone) {
-        elements = m_elements.data();
-    } else {
-        if (m_txop == QTransform::TxTranslate) {
-            for (int i=0; i<m_elements.size(); ++i) {
-                const QPointF &e = m_elements.at(i);
-                m_elements_dev << QPointF(e.x() + m_dx, e.y() + m_dy);
-            }
-        } else if (m_txop == QTransform::TxScale) {
-            for (int i=0; i<m_elements.size(); ++i) {
-                const QPointF &e = m_elements.at(i);
-                m_elements_dev << QPointF(m_m11 * e.x() + m_dx, m_m22 * e.y() + m_dy);
-            }
-        } else if (m_txop < QTransform::TxProject) {
-            for (int i=0; i<m_elements.size(); ++i) {
-                const QPointF &e = m_elements.at(i);
-                m_elements_dev << QPointF(m_m11 * e.x() + m_m21 * e.y() + m_dx,
-                                          m_m22 * e.y() + m_m12 * e.x() + m_dy);
-            }
-        } else {
-            const QVectorPath vp((qreal *)m_elements.data(), m_elements.size(), m_element_types.size() ? m_element_types.data() : 0);
-            QPainterPath path = vp.convertToPainterPath();
-            path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path);
-            if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
-                path.setFillRule(Qt::WindingFill);
-            uint old_txop = m_txop;
-            m_txop = QTransform::TxNone;
-            if (path.isEmpty())
-                m_valid = false;
-            else
-                convertPath(path);
-            m_txop = old_txop;
-            return;
+        // Nothing to do.
+    } else if (m_txop == QTransform::TxTranslate) {
+        for (int i = 0; i < m_elements.size(); ++i) {
+            QPointF &e = elements[i];
+            e = QPointF(e.x() + m_dx, e.y() + m_dy);
+        }
+    } else if (m_txop == QTransform::TxScale) {
+        for (int i = 0; i < m_elements.size(); ++i) {
+            QPointF &e = elements[i];
+            e = QPointF(m_m11 * e.x() + m_dx, m_m22 * e.y() + m_dy);
         }
-        elements = m_elements_dev.data();
+    } else if (m_txop < QTransform::TxProject) {
+        for (int i = 0; i < m_elements.size(); ++i) {
+            QPointF &e = elements[i];
+            e = QPointF(m_m11 * e.x() + m_m21 * e.y() + m_dx,
+                        m_m22 * e.y() + m_m12 * e.x() + m_dy);
+        }
+    } else {
+        const QVectorPath vp((qreal *)elements, m_elements.size(),
+                             m_element_types.size() ? m_element_types.data() : 0);
+        QPainterPath path = vp.convertToPainterPath();
+        path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path);
+        if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
+            path.setFillRule(Qt::WindingFill);
+        uint old_txop = m_txop;
+        m_txop = QTransform::TxNone;
+        if (path.isEmpty())
+            m_valid = false;
+        else
+            convertPath(path);
+        m_txop = old_txop;
+        return;
     }
 
     if (m_round_coords) {
@@ -223,7 +233,7 @@ void QOutlineMapper::endOutline()
                                   qFloor(elements[i].y() + aliasedCoordinateDelta));
     }
 
-    controlPointRect = boundingRect(elements, element_count);
+    controlPointRect = boundingRect(elements, m_elements.size());
 
 #ifdef QT_DEBUG_CONVERT
     printf(" - control point rect (%.2f, %.2f) %.2f x %.2f, clip=(%d,%d, %dx%d)\n",
@@ -242,9 +252,9 @@ void QOutlineMapper::endOutline()
                           || controlPointRect.height() > QT_RASTER_COORD_LIMIT));
 
     if (do_clip) {
-        clipElements(elements, elementTypes(), element_count);
+        clipElements(elements, elementTypes(), m_elements.size());
     } else {
-        convertElements(elements, elementTypes(), element_count);
+        convertElements(elements, elementTypes(), m_elements.size());
     }
 }
 
index 388858c..7bcf316 100644 (file)
@@ -73,6 +73,8 @@ const int QT_RASTER_COORD_LIMIT = 32767;
 
 //#define QT_DEBUG_CONVERT
 
+Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+
 /********************************************************************************
  * class QOutlineMapper
  *
@@ -90,11 +92,9 @@ public:
     QOutlineMapper() :
         m_element_types(0),
         m_elements(0),
-        m_elements_dev(0),
         m_points(0),
         m_tags(0),
         m_contours(0),
-        m_polygon_dev(0),
         m_in_clip_elements(false),
         m_round_coords(false)
     {
@@ -117,6 +117,10 @@ public:
         m_dx = m.dx();
         m_dy = m.dy();
         m_txop = m.type();
+
+        qreal scale;
+        qt_scaleForTransform(m, &scale);
+        m_curve_threshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale);
     }
 
     void beginOutline(Qt::FillRule fillRule)
@@ -126,7 +130,6 @@ public:
 #endif
         m_valid = true;
         m_elements.reset();
-        m_elements_dev.reset();
         m_element_types.reset();
         m_points.reset();
         m_tags.reset();
@@ -161,15 +164,7 @@ public:
         m_element_types << QPainterPath::LineToElement;
     }
 
-    inline void curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) {
-#ifdef QT_DEBUG_CONVERT
-        printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y());
-#endif
-        m_elements << cp1 << cp2 << ep;
-        m_element_types << QPainterPath::CurveToElement
-                        << QPainterPath::CurveToDataElement
-                        << QPainterPath::CurveToDataElement;
-    }
+    void curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep);
 
     inline void closeSubpath() {
         int element_count = m_elements.size();
@@ -209,14 +204,11 @@ public:
 public:
     QDataBuffer<QPainterPath::ElementType> m_element_types;
     QDataBuffer<QPointF> m_elements;
-    QDataBuffer<QPointF> m_elements_dev;
     QDataBuffer<QT_FT_Vector> m_points;
     QDataBuffer<char> m_tags;
     QDataBuffer<int> m_contours;
 
     QRect m_clip_rect;
-    QDataBuffer<QPointF> m_polygon_dev;
-
     QRectF controlPointRect; // only valid after endOutline()
 
     QT_FT_Outline m_outline;
@@ -235,6 +227,8 @@ public:
     qreal m_dx;
     qreal m_dy;
 
+    qreal m_curve_threshold;
+
     bool m_valid;
     bool m_in_clip_elements;