Fix some issues in gpu device with perspective. Add a gm that would have caught them.

Review URL: http://codereview.appspot.com/4994048/



git-svn-id: http://skia.googlecode.com/svn/trunk@2256 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/pathfillpersp.cpp b/gm/pathfillpersp.cpp
new file mode 100644
index 0000000..00326fd
--- /dev/null
+++ b/gm/pathfillpersp.cpp
@@ -0,0 +1,123 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+
+namespace skiagm {
+
+class FillTypePerspGM : public GM {
+    SkPath fPath;
+public:
+    FillTypePerspGM() {
+        const SkScalar radius = SkIntToScalar(45);
+        fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
+        fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
+    }
+    
+protected:
+    virtual SkString onShortName() {
+        return SkString("filltypespersp");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(835, 840);
+    }
+
+    void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft,
+                  SkScalar scale, const SkPaint& paint) {
+
+        const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) };
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+        canvas->clipRect(r);
+        canvas->drawColor(SK_ColorWHITE);
+        fPath.setFillType(ft);
+        canvas->translate(r.centerX(), r.centerY());
+        canvas->scale(scale, scale);
+        canvas->translate(-r.centerX(), -r.centerY());
+        canvas->drawPath(fPath, paint);
+        canvas->restore();
+    }
+
+    void showFour(SkCanvas* canvas, SkScalar scale, bool aa) {
+
+        SkPaint paint;
+        SkPoint center = SkPoint::Make(SkIntToScalar(100), SkIntToScalar(100));
+        SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN};
+        SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
+        SkShader* s = SkGradientShader::CreateRadial(center,
+                                                     SkIntToScalar(100),
+                                                     colors,
+                                                     pos,
+                                                     SK_ARRAY_COUNT(colors),
+                                                     SkShader::kClamp_TileMode);
+        paint.setShader(s)->unref();
+        paint.setAntiAlias(aa);
+
+        showPath(canvas,   0,   0, SkPath::kWinding_FillType,
+                 scale, paint);
+        showPath(canvas, 200,   0, SkPath::kEvenOdd_FillType,
+                 scale, paint);
+        showPath(canvas,  00, 200, SkPath::kInverseWinding_FillType,
+                 scale, paint);
+        showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType,
+                 scale, paint);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        // do perspective drawPaint as the background;
+        SkPaint bkgnrd;
+        SkPoint center = SkPoint::Make(SkIntToScalar(100), SkIntToScalar(100));
+        SkColor colors[] = {SK_ColorBLACK, SK_ColorGREEN, SK_ColorYELLOW, SK_ColorWHITE};
+        SkScalar pos[] = {0, SK_ScalarHalf / 2, 3 * SK_ScalarHalf / 2, SK_Scalar1};
+        SkShader* s = SkGradientShader::CreateRadial(center,
+                                                     SkIntToScalar(1000),
+                                                     colors,
+                                                     pos,
+                                                     SK_ARRAY_COUNT(colors),
+                                                     SkShader::kClamp_TileMode);
+        bkgnrd.setShader(s)->unref();
+        canvas->save();
+            canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
+            SkMatrix mat;
+            mat.reset();
+            mat.setPerspY(SK_Scalar1 / 300);
+            canvas->concat(mat);
+            canvas->drawPaint(bkgnrd);
+        canvas->restore();
+
+        // draw the paths in perspective
+        SkMatrix persp;
+        persp.reset();
+        persp.setPerspX(-SK_Scalar1 / 300);
+        canvas->concat(persp);
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        const SkScalar scale = SkIntToScalar(5)/4;
+
+        showFour(canvas, SK_Scalar1, false);
+        canvas->translate(SkIntToScalar(450), 0);
+        showFour(canvas, scale, paint);
+
+        canvas->translate(SkIntToScalar(-450), SkIntToScalar(450));
+        showFour(canvas, SK_Scalar1, true);
+        canvas->translate(SkIntToScalar(450), 0);
+        showFour(canvas, scale, paint);
+    }
+    
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new FillTypeGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 8eb8c63..6de3169 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -18,6 +18,7 @@
 #include "GrResourceCache.h"
 #include "GrStencilBuffer.h"
 #include "GrTextStrike.h"
+#include "SkTLazy.h"
 #include "SkTrace.h"
 
 // Using MSAA seems to be slower for some yet unknown reason.
@@ -592,22 +593,37 @@
               GrIntToScalar(getRenderTarget()->height()));
     GrAutoMatrix am;
     GrMatrix inverse;
+    SkTLazy<GrPaint> tmpPaint;
+    const GrPaint* p = &paint;
     // We attempt to map r by the inverse matrix and draw that. mapRect will
     // map the four corners and bound them with a new rect. This will not
     // produce a correct result for some perspective matrices.
-    if (!this->getMatrix().hasPerspective() &&
-        fGpu->getViewInverse(&inverse)) {
+    if (!this->getMatrix().hasPerspective()) {
+        if (!fGpu->getViewInverse(&inverse)) {
+            GrPrintf("Could not invert matrix");
+            return;
+        }
         inverse.mapRect(&r);
     } else {
+        if (paint.getActiveMaskStageMask() || paint.getActiveStageMask()) {
+            if (!fGpu->getViewInverse(&inverse)) {
+                GrPrintf("Could not invert matrix");
+                return;
+            }
+            tmpPaint.set(paint);
+            tmpPaint.get()->preConcatActiveSamplerMatrices(inverse);
+            p = tmpPaint.get();
+        }
         am.set(this, GrMatrix::I());
     }
-    GrPaint tmpPaint;
-    const GrPaint* p = &paint;
     // by definition this fills the entire clip, no need for AA
     if (paint.fAntiAlias) {
-        tmpPaint = paint;
-        tmpPaint.fAntiAlias = false;
-        p = &tmpPaint;
+        if (!tmpPaint.isValid()) {
+            tmpPaint.set(paint);
+            p = tmpPaint.get();
+        }
+        GrAssert(p == tmpPaint.get());
+        tmpPaint.get()->fAntiAlias = false;
     }
     this->drawRect(*p, r);
 }
diff --git a/gpu/src/GrDefaultPathRenderer.cpp b/gpu/src/GrDefaultPathRenderer.cpp
index 278c751..fd56c07 100644
--- a/gpu/src/GrDefaultPathRenderer.cpp
+++ b/gpu/src/GrDefaultPathRenderer.cpp
@@ -509,8 +509,19 @@
                                GrIntToScalar(fTarget->getRenderTarget()->width()),
                                GrIntToScalar(fTarget->getRenderTarget()->height()));
                 GrMatrix vmi;
-                if (fTarget->getViewInverse(&vmi)) {
+                // mapRect through persp matrix may not be correct
+                if (!fTarget->getViewMatrix().hasPerspective() &&
+                    fTarget->getViewInverse(&vmi)) {
                     vmi.mapRect(&bounds);
+                } else {
+                    if (stages) {
+                        if (!fTarget->getViewInverse(&vmi)) {
+                            GrPrintf("Could not invert matrix.");
+                            return;
+                        }
+                        fTarget->preConcatSamplerMatrices(stages, vmi);
+                    }
+                    fTarget->setViewMatrix(GrMatrix::I());
                 }
             } else {
                 bounds = fPath->getBounds();
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
index 0c9f145..cd4defd 100644
--- a/gyp/gm.gyp
+++ b/gyp/gm.gyp
@@ -15,6 +15,7 @@
         '../gm/complexclip.cpp',
         '../gm/complexclip2.cpp',
         '../gm/filltypes.cpp',
+        '../gm/filltypespersp.cpp',
         '../gm/gmmain.cpp',
         '../gm/gradients.cpp',
         '../gm/lcdtext.cpp',
diff --git a/include/core/SkTLazy.h b/include/core/SkTLazy.h
index 752d43b..5747da0 100644
--- a/include/core/SkTLazy.h
+++ b/include/core/SkTLazy.h
@@ -29,14 +29,15 @@
     }
 
     SkTLazy(const SkTLazy<T>& src) : fPtr(NULL) {
-        const T* ptr = src.get();
-        if (ptr) {
-            fPtr = new (fStorage) T(*ptr);
+        if (src.isValid()) {
+            fPtr = new (fStorage) T(*src->get());
+        } else {
+            fPtr = NULL;
         }
     }
 
     ~SkTLazy() {
-        if (fPtr) {
+        if (this->isValid()) {
             fPtr->~T();
         }
     }
@@ -48,13 +49,13 @@
      *  always returned.
      */
     T* init() {
-        if (fPtr) {
+        if (this->isValid()) {
             fPtr->~T();
         }
         fPtr = new (fStorage) T;
         return fPtr;
     }
-        
+
     /**
      *  Copy src into this, and return a pointer to a copy of it. Note this
      *  will always return the same pointer, so if it is called on a lazy that
@@ -62,19 +63,25 @@
      *  contents.
      */
     T* set(const T& src) {
-        if (fPtr) {
+        if (this->isValid()) {
             *fPtr = src;
         } else {
             fPtr = new (fStorage) T(src);
         }
         return fPtr;
     }
+
+    /**
+     *  Returns true if a valid object has been initialized in the SkTLazy,
+     *  false otherwise.
+     */
+    bool isValid() const { return NULL != fPtr; }
     
     /**
      *  Returns either NULL, or a copy of the object that was passed to
      *  set() or the constructor.
      */
-    T* get() const { return fPtr; }
+    T* get() const { SkASSERT(this->isValid()); return fPtr; }
     
 private:
     T*   fPtr; // NULL or fStorage