src/openvg/qpaintengine_vg.cpp
changeset 7 f7bc934e204c
parent 3 41300fa6a67c
--- a/src/openvg/qpaintengine_vg.cpp	Tue Feb 02 00:43:10 2010 +0200
+++ b/src/openvg/qpaintengine_vg.cpp	Wed Mar 31 11:06:36 2010 +0300
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 ** All rights reserved.
 ** Contact: Nokia Corporation (qt-info@nokia.com)
 **
@@ -188,6 +188,7 @@
 
     bool maskValid;         // True if vgMask() contains valid data.
     bool maskIsSet;         // True if mask would be fully set if it was valid.
+    bool scissorMask;       // True if scissor is used in place of the mask.
     bool rawVG;             // True if processing a raw VG escape.
 
     QRect maskRect;         // Rectangle version of mask if it is simple.
@@ -200,6 +201,7 @@
 
     QRegion scissorRegion;  // Currently active scissor region.
     bool scissorActive;     // True if scissor region is active.
+    bool scissorDirty;      // True if scissor is dirty after native painting.
 
     QPaintEngine::DirtyFlags dirty;
 
@@ -354,9 +356,11 @@
 
     maskValid = false;
     maskIsSet = false;
+    scissorMask = false;
     rawVG = false;
 
     scissorActive = false;
+    scissorDirty = false;
 
     dirty = 0;
 
@@ -512,14 +516,11 @@
                         0.0f, -1.0f, 0.0f,
                         0.5f, devh + 0.5f, 1.0f);
 
-    // The image transform is always the full transformation,
-    // because it can be projective.
-    imageTransform = transform * viewport;
-
-    // Determine if the transformation is projective.
-    bool projective = (imageTransform.m13() != 0.0f ||
-                       imageTransform.m23() != 0.0f ||
-                       imageTransform.m33() != 1.0f);
+    // Compute the path transform and determine if it is projective.
+    pathTransform = transform * viewport;
+    bool projective = (pathTransform.m13() != 0.0f ||
+                       pathTransform.m23() != 0.0f ||
+                       pathTransform.m33() != 1.0f);
     if (projective) {
         // The engine cannot do projective path transforms for us,
         // so we will have to convert the co-ordinates ourselves.
@@ -527,11 +528,19 @@
         pathTransform = viewport;
         simpleTransform = false;
     } else {
-        pathTransform = imageTransform;
         simpleTransform = true;
     }
     pathTransformSet = false;
 
+    // The image transform is always the full transformation,
+    // because it can be projective.  It also does not need the
+    // (0.5, -0.5) translation because vgDrawImage() implicitly
+    // adds 0.5 to each co-ordinate.
+    QTransform viewport2(1.0f, 0.0f, 0.0f,
+                         0.0f, -1.0f, 0.0f,
+                         0.0f, devh + 1, 1.0f);
+    imageTransform = transform * viewport2;
+
     // Calculate the scaling factor to use for turning cosmetic pens
     // into ordinary non-cosmetic pens.
     qt_scaleForTransform(transform, &penScale);
@@ -984,6 +993,9 @@
     return dest;
 }
 
+// defined in qpixmapdata_vg.cpp.
+const uchar *qt_vg_imageBits(const QImage& image);
+
 static VGImage toVGImage
     (const QImage & image, Qt::ImageConversionFlags flags = Qt::AutoColor)
 {
@@ -1017,7 +1029,7 @@
         break;
     }
 
-    const uchar *pixels = img.bits();
+    const uchar *pixels = qt_vg_imageBits(img);
 
     VGImage vgImg = QVGImagePool::instance()->createPermanentImage
         (format, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
@@ -1061,7 +1073,7 @@
         break;
     }
 
-    const uchar *pixels = img.bits() + bpp * sr.x() +
+    const uchar *pixels = qt_vg_imageBits(img) + bpp * sr.x() +
                           img.bytesPerLine() * sr.y();
 
     VGImage vgImg = QVGImagePool::instance()->createPermanentImage
@@ -1083,7 +1095,7 @@
     painter.drawImage(0, 0, image);
     painter.end();
 
-    const uchar *pixels = img.bits();
+    const uchar *pixels = qt_vg_imageBits(img);
 
     VGImage vgImg = QVGImagePool::instance()->createPermanentImage
         (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
@@ -1105,7 +1117,7 @@
     painter.drawImage(QPoint(0, 0), image, sr);
     painter.end();
 
-    const uchar *pixels = img.bits();
+    const uchar *pixels = qt_vg_imageBits(img);
 
     VGImage vgImg = QVGImagePool::instance()->createPermanentImage
         (VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
@@ -1537,7 +1549,28 @@
 static inline bool clipTransformIsSimple(const QTransform& transform)
 {
     QTransform::TransformationType type = transform.type();
-    return (type == QTransform::TxNone || type == QTransform::TxTranslate);
+    if (type == QTransform::TxNone || type == QTransform::TxTranslate)
+        return true;
+    if (type == QTransform::TxRotate) {
+        // Check for 0, 90, 180, and 270 degree rotations.
+        // (0 might happen after 4 rotations of 90 degrees).
+        qreal m11 = transform.m11();
+        qreal m12 = transform.m12();
+        qreal m21 = transform.m21();
+        qreal m22 = transform.m22();
+        if (m11 == 0.0f && m22 == 0.0f) {
+            if (m12 == 1.0f && m21 == -1.0f)
+                return true;    // 90 degrees.
+            else if (m12 == -1.0f && m21 == 1.0f)
+                return true;    // 270 degrees.
+        } else if (m12 == 0.0f && m21 == 0.0f) {
+            if (m11 == -1.0f && m22 == -1.0f)
+                return true;    // 180 degrees.
+            else if (m11 == 1.0f && m22 == 1.0f)
+                return true;    // 0 degrees.
+        }
+    }
+    return false;
 }
 
 #if defined(QVG_SCISSOR_CLIP)
@@ -1659,12 +1692,12 @@
     if (op == Qt::NoClip) {
         d->maskValid = false;
         d->maskIsSet = true;
+        d->scissorMask = false;
         d->maskRect = QRect();
         vgSeti(VG_MASKING, VG_FALSE);
         return;
     }
 
-#if defined(QVG_NO_RENDER_TO_MASK)
     // We don't have vgRenderToMask(), so handle simple QRectF's only.
     if (path.shape() == QVectorPath::RectangleHint &&
             path.elementCount() == 4 && clipTransformIsSimple(d->transform)) {
@@ -1674,8 +1707,10 @@
         QRectF rect(points[0], points[1], points[2] - points[0],
                     points[5] - points[1]);
         clip(rect.toRect(), op);
+        return;
     }
-#else
+
+#if !defined(QVG_NO_RENDER_TO_MASK)
     QPaintDevice *pdev = paintDevice();
     int width = pdev->width();
     int height = pdev->height();
@@ -1706,6 +1741,7 @@
     vgSeti(VG_MASKING, VG_TRUE);
     d->maskValid = true;
     d->maskIsSet = false;
+    d->scissorMask = false;
 #endif
 }
 
@@ -1726,6 +1762,7 @@
         {
             d->maskValid = false;
             d->maskIsSet = true;
+            d->scissorMask = false;
             d->maskRect = QRect();
             vgSeti(VG_MASKING, VG_FALSE);
         }
@@ -1741,6 +1778,7 @@
                     vgSeti(VG_MASKING, VG_FALSE);
                 d->maskValid = false;
                 d->maskIsSet = true;
+                d->scissorMask = false;
                 d->maskRect = QRect();
             } else {
                 // Special case: if the intersection of the system
@@ -1758,6 +1796,7 @@
                     if (clip.rectCount() != 1) {
                         d->maskValid = false;
                         d->maskIsSet = false;
+                        d->scissorMask = false;
                         d->maskRect = QRect();
                         d->modifyMask(this, VG_FILL_MASK, r);
                         break;
@@ -1766,6 +1805,7 @@
                 }
                 d->maskValid = false;
                 d->maskIsSet = false;
+                d->scissorMask = true;
                 d->maskRect = clipRect;
                 vgSeti(VG_MASKING, VG_FALSE);
                 updateScissor();
@@ -1776,13 +1816,30 @@
         case Qt::IntersectClip:
         {
             QRect r = d->transform.mapRect(rect);
-            if (d->maskIsSet && isDefaultClipRect(r)) {
+            if (!d->maskValid) {
+                // Mask has not been used yet, so intersect with
+                // the previous scissor-based region in maskRect.
+                if (d->scissorMask)
+                    r = r.intersect(d->maskRect);
+                if (isDefaultClipRect(r)) {
+                    // The clip is the full window, so turn off clipping.
+                    d->maskIsSet = true;
+                    d->maskRect = QRect();
+                } else {
+                    // Activate the scissor on a smaller maskRect.
+                    d->maskIsSet = false;
+                    d->maskRect = r;
+                }
+                d->scissorMask = true;
+                updateScissor();
+            } else if (d->maskIsSet && isDefaultClipRect(r)) {
                 // Intersecting a full-window clip with a full-window
                 // region is the same as turning off clipping.
                 if (d->maskValid)
                     vgSeti(VG_MASKING, VG_FALSE);
                 d->maskValid = false;
                 d->maskIsSet = true;
+                d->scissorMask = false;
                 d->maskRect = QRect();
             } else {
                 d->modifyMask(this, VG_INTERSECT_MASK, r);
@@ -1824,6 +1881,7 @@
         {
             d->maskValid = false;
             d->maskIsSet = true;
+            d->scissorMask = false;
             d->maskRect = QRect();
             vgSeti(VG_MASKING, VG_FALSE);
         }
@@ -1839,6 +1897,7 @@
                     vgSeti(VG_MASKING, VG_FALSE);
                 d->maskValid = false;
                 d->maskIsSet = true;
+                d->scissorMask = false;
                 d->maskRect = QRect();
             } else {
                 // Special case: if the intersection of the system
@@ -1852,12 +1911,14 @@
                 if (clip.rectCount() == 1) {
                     d->maskValid = false;
                     d->maskIsSet = false;
+                    d->scissorMask = true;
                     d->maskRect = clip.boundingRect();
                     vgSeti(VG_MASKING, VG_FALSE);
                     updateScissor();
                 } else {
                     d->maskValid = false;
                     d->maskIsSet = false;
+                    d->scissorMask = false;
                     d->maskRect = QRect();
                     d->modifyMask(this, VG_FILL_MASK, r);
                 }
@@ -1882,6 +1943,7 @@
                     vgSeti(VG_MASKING, VG_FALSE);
                 d->maskValid = false;
                 d->maskIsSet = true;
+                d->scissorMask = false;
                 d->maskRect = QRect();
             } else {
                 d->modifyMask(this, VG_INTERSECT_MASK, r);
@@ -1960,6 +2022,7 @@
     if (op == Qt::NoClip) {
         d->maskValid = false;
         d->maskIsSet = true;
+        d->scissorMask = false;
         d->maskRect = QRect();
         vgSeti(VG_MASKING, VG_FALSE);
         return;
@@ -1995,6 +2058,7 @@
     vgSeti(VG_MASKING, VG_TRUE);
     d->maskValid = true;
     d->maskIsSet = false;
+    d->scissorMask = false;
 #else
     QPaintEngineEx::clip(path, op);
 #endif
@@ -2003,6 +2067,7 @@
 void QVGPaintEnginePrivate::ensureMask
         (QVGPaintEngine *engine, int width, int height)
 {
+    scissorMask = false;
     if (maskIsSet) {
         vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, width, height);
         maskRect = QRect();
@@ -2038,6 +2103,7 @@
     vgSeti(VG_MASKING, VG_TRUE);
     maskValid = true;
     maskIsSet = false;
+    scissorMask = false;
 }
 
 void QVGPaintEnginePrivate::modifyMask
@@ -2059,6 +2125,7 @@
     vgSeti(VG_MASKING, VG_TRUE);
     maskValid = true;
     maskIsSet = false;
+    scissorMask = false;
 }
 
 #endif // !QVG_SCISSOR_CLIP
@@ -2083,6 +2150,7 @@
             // so there is no point doing any scissoring.
             vgSeti(VG_SCISSORING, VG_FALSE);
             d->scissorActive = false;
+            d->scissorDirty = false;
             return;
         }
     } else
@@ -2090,7 +2158,7 @@
     {
 #if !defined(QVG_SCISSOR_CLIP)
         // Combine the system clip with the simple mask rectangle.
-        if (!d->maskRect.isNull()) {
+        if (d->scissorMask) {
             if (region.isEmpty())
                 region = d->maskRect;
             else
@@ -2100,6 +2168,7 @@
                 // so there is no point doing any scissoring.
                 vgSeti(VG_SCISSORING, VG_FALSE);
                 d->scissorActive = false;
+                d->scissorDirty = false;
                 return;
             }
         } else
@@ -2109,11 +2178,12 @@
         if (region.isEmpty()) {
             vgSeti(VG_SCISSORING, VG_FALSE);
             d->scissorActive = false;
+            d->scissorDirty = false;
             return;
         }
     }
 
-    if (d->scissorActive && region == d->scissorRegion)
+    if (d->scissorActive && region == d->scissorRegion && !d->scissorDirty)
         return;
 
     QVector<QRect> rects = region.rects();
@@ -2131,6 +2201,7 @@
 
     vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data());
     vgSeti(VG_SCISSORING, VG_TRUE);
+    d->scissorDirty = false;
     d->scissorActive = true;
     d->scissorRegion = region;
 }
@@ -2178,6 +2249,7 @@
         // Replay the entire clip stack to put the mask into the right state.
         d->maskValid = false;
         d->maskIsSet = true;
+        d->scissorMask = false;
         d->maskRect = QRect();
         s->clipRegion = defaultClipRegion();
         d->replayClipOperations();
@@ -2187,6 +2259,7 @@
         vgSeti(VG_MASKING, VG_FALSE);
         d->maskValid = false;
         d->maskIsSet = false;
+        d->scissorMask = false;
         d->maskRect = QRect();
     }
 #endif
@@ -2305,12 +2378,7 @@
     Q_D(QVGPaintEngine);
     QVGPainterState *s = state();
     if (!s->clipEnabled || s->clipOperation == Qt::NoClip) {
-        // The transform will either be identity or a simple translation,
-        // so do a simpler version of "r = d->transform.map(rect).toRect()".
-        QRect r = QRect(qRound(rect.x() + d->transform.dx()),
-                        qRound(rect.y() + d->transform.dy()),
-                        qRound(rect.width()),
-                        qRound(rect.height()));
+        QRect r = d->transform.mapRect(rect).toRect();
         int height = paintDevice()->height();
         if (d->clearColor != color || d->clearOpacity != s->opacity) {
             VGfloat values[4];
@@ -2337,7 +2405,7 @@
         return;
 
     // Check to see if we can use vgClear() for faster filling.
-    if (brush.style() == Qt::SolidPattern &&
+    if (brush.style() == Qt::SolidPattern && brush.isOpaque() &&
             clipTransformIsSimple(d->transform) && d->opacity == 1.0f &&
             clearRect(rect, brush.color())) {
         return;
@@ -2380,7 +2448,7 @@
     Q_D(QVGPaintEngine);
 
     // Check to see if we can use vgClear() for faster filling.
-    if (clipTransformIsSimple(d->transform) && d->opacity == 1.0f &&
+    if (clipTransformIsSimple(d->transform) && d->opacity == 1.0f && color.alpha() == 255 &&
             clearRect(rect, color)) {
         return;
     }
@@ -3166,15 +3234,15 @@
         if (!scaledImage.isNull()) {  // Not a space character
             if (scaledImage.format() == QImage::Format_Indexed8) {
                 vgImage = vgCreateImage(VG_A_8, scaledImage.width(), scaledImage.height(), VG_IMAGE_QUALITY_FASTER);
-                vgImageSubData(vgImage, scaledImage.bits(), scaledImage.bytesPerLine(), VG_A_8, 0, 0, scaledImage.width(), scaledImage.height());
+                vgImageSubData(vgImage, qt_vg_imageBits(scaledImage), scaledImage.bytesPerLine(), VG_A_8, 0, 0, scaledImage.width(), scaledImage.height());
             } else if (scaledImage.format() == QImage::Format_Mono) {
                 QImage img = scaledImage.convertToFormat(QImage::Format_Indexed8);
                 vgImage = vgCreateImage(VG_A_8, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
-                vgImageSubData(vgImage, img.bits(), img.bytesPerLine(), VG_A_8, 0, 0, img.width(), img.height());
+                vgImageSubData(vgImage, qt_vg_imageBits(img), img.bytesPerLine(), VG_A_8, 0, 0, img.width(), img.height());
             } else {
                 QImage img = scaledImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
                 vgImage = vgCreateImage(VG_sARGB_8888_PRE, img.width(), img.height(), VG_IMAGE_QUALITY_FASTER);
-                vgImageSubData(vgImage, img.bits(), img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, img.width(), img.height());
+                vgImageSubData(vgImage, qt_vg_imageBits(img), img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, img.width(), img.height());
             }
         }
         origin[0] = -metrics.x.toReal() + 0.5f;
@@ -3333,6 +3401,7 @@
     d->brushType = (VGPaintType)0;
     d->clearColor = QColor();
     d->fillPaint = d->brushPaint;
+    d->scissorDirty = true;
     restoreState(QPaintEngine::AllDirty);
     d->dirty = dirty;
     d->rawVG = false;
@@ -3388,6 +3457,7 @@
                   QPaintEngine::DirtyClipEnabled)) != 0) {
         d->maskValid = false;
         d->maskIsSet = false;
+        d->scissorMask = false;
         d->maskRect = QRect();
         clipEnabledChanged();
     }
@@ -3640,7 +3710,7 @@
         if (vgImage == VG_INVALID_HANDLE)
             return;
         vgImageSubData
-            (vgImage, img.bits() + img.bytesPerLine() * (img.height() - 1),
+            (vgImage, qt_vg_imageBits(img) + img.bytesPerLine() * (img.height() - 1),
              -(img.bytesPerLine()), VG_sARGB_8888_PRE, 0, 0,
              img.width(), img.height());
 
@@ -3666,15 +3736,17 @@
 
     vgSetiv(VG_SCISSOR_RECTS, count * 4, params.data());
     vgSeti(VG_SCISSORING, VG_TRUE);
+    d->scissorDirty = false;
     d->scissorActive = true;
     d->scissorRegion = region;
 }
 
 void QVGCompositionHelper::clearScissor()
 {
-    if (d->scissorActive) {
+    if (d->scissorActive || d->scissorDirty) {
         vgSeti(VG_SCISSORING, VG_FALSE);
         d->scissorActive = false;
+        d->scissorDirty = false;
     }
 }