Trim the copy of the dst made for dst-reading effects using the clip and dev-bounds of draw.

Adds dev bounds to circles and rects drawn by GrDrawTarget base class (GrIODB already provides rect bounds).

Author: bsalomon@google.com

Reviewed By: robertphilips@google.com,jvanverth@google.com,robertphillips@google.com

Review URL: https://chromiumcodereview.appspot.com/13222004

git-svn-id: http://skia.googlecode.com/svn/trunk@8453 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkTLazy.h b/include/core/SkTLazy.h
index 2fe05f1..0daf98c 100644
--- a/include/core/SkTLazy.h
+++ b/include/core/SkTLazy.h
@@ -81,10 +81,16 @@
     bool isValid() const { return NULL != fPtr; }
 
     /**
-     *  Returns either NULL, or a copy of the object that was passed to
-     *  set() or the constructor.
+     * Returns the object. This version should only be called when the caller
+     * knows that the object has been initialized.
      */
     T* get() const { SkASSERT(this->isValid()); return fPtr; }
+    
+    /**
+     * Like above but doesn't assert if object isn't initialized (in which case
+     * NULL is returned).
+     */
+    T* getMaybeNull() const { return fPtr; }
 
 private:
     friend void* operator new<T>(size_t, SkTLazy* lazy);
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 002c4ed..d16efa8 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -507,6 +507,18 @@
             }
         }
     }
+    
+    /**
+     * Checks whether any of the effects will read the dst pixel color.
+     */
+    bool willEffectReadDst() const {
+        for (int s = 0; s < kNumStages; ++s) {
+            if (this->isStageEnabled(s) && (*this->getStage(s).getEffect())->willReadDst()) {
+                return true;
+            }
+        }
+        return false;
+    }
 
     /// @}
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 4d4ba49..bc7d39e 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -407,15 +407,7 @@
 }
 
 bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) {
-    bool willReadDst = false;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        const GrEffectRef* effect = this->drawState()->getStage(s).getEffect();
-        if (NULL != effect && (*effect)->willReadDst()) {
-            willReadDst = true;
-            break;
-        }
-    }
-    if (!willReadDst) {
+    if (!this->getDrawState().willEffectReadDst()) {
         return true;
     }
     GrRenderTarget* rt = this->drawState()->getRenderTarget();
@@ -426,8 +418,24 @@
         GrPrintf("Reading Dst of non-texture render target is not currently supported.\n");
         return false;
     }
-    // TODO: Consider bounds of draw and bounds of clip
 
+    const GrClipData* clip = this->getClip();
+    GrIRect copyRect;
+    clip->getConservativeBounds(this->getDrawState().getRenderTarget(), &copyRect);
+    SkIRect drawIBounds;
+    if (info->getDevIBounds(&drawIBounds)) {
+        if (!copyRect.intersect(drawIBounds)) {
+#if GR_DEBUG
+            GrPrintf("Missed an early reject. Bailing on draw from setupDstReadIfNecessary.\n");
+#endif
+            return false;
+        }
+    } else {
+#if GR_DEBUG
+        //GrPrintf("No dev bounds when dst copy is made.\n");
+#endif
+    }
+    
     GrDrawTarget::AutoGeometryAndStatePush agasp(this, kReset_ASRInit);
 
     // The draw will resolve dst if it has MSAA. Two things to consider in the future:
@@ -437,8 +445,8 @@
     // copy of it.
     GrTextureDesc desc;
     desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
-    desc.fWidth = rt->width();
-    desc.fHeight = rt->height();
+    desc.fWidth = copyRect.width();
+    desc.fHeight = copyRect.height();
     desc.fSampleCnt = 0;
     desc.fConfig = rt->config();
 
@@ -454,11 +462,14 @@
     SkMatrix matrix;
     matrix.setIDiv(rt->width(), rt->height());
     this->drawState()->createTextureEffect(kTextureStage, rt->asTexture(), matrix);
-    SkRect copyRect = SkRect::MakeWH(SkIntToScalar(desc.fWidth),
-                                     SkIntToScalar(desc.fHeight));
-    this->drawRect(copyRect, NULL, &copyRect, NULL);
+    
+    SkRect srcRect = SkRect::MakeFromIRect(copyRect);
+    SkRect dstRect = SkRect::MakeWH(SkIntToScalar(copyRect.width()),
+                                    SkIntToScalar(copyRect.height()));
+    this->drawRect(dstRect, NULL, &srcRect, NULL);
+    
     info->fDstCopy.setTexture(ast.texture());
-    info->fDstCopy.setOffset(0, 0);
+    info->fDstCopy.setOffset(copyRect.fLeft, copyRect.fTop);
     return true;
 }
 
@@ -645,8 +656,13 @@
             localMatrix->mapPointsWithStride(coords, vsize, 4);
         }
     }
+    SkTLazy<SkRect> bounds;
+    if (this->getDrawState().willEffectReadDst()) {
+        bounds.init();
+        this->getDrawState().getViewMatrix().mapRect(bounds.get(), rect);
+    }
 
-    this->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
+    this->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4, bounds.getMaybeNull());
 }
 
 void GrDrawTarget::clipWillBeSet(const GrClipData* clipData) {
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 737ce86..df3f31b 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -654,6 +654,15 @@
             fDevBounds = &fDevBoundsStorage;
         }
         const SkRect* getDevBounds() const { return fDevBounds; }
+        
+        bool getDevIBounds(SkIRect* bounds) const {
+            if (NULL != fDevBounds) {
+                fDevBounds->roundOut(bounds);
+                return true;
+            } else {
+                return false;
+            }
+        }
 
         // NULL if no copy of the dst is needed for the draw.
         const GrDeviceCoordTexture* getDstCopy() const {
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index 2d25d54..7da89bd 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -136,24 +136,21 @@
         verts[i].fInnerRadius = innerRadius - 0.5f;
     }
 
-    SkScalar L = -outerRadius;
-    SkScalar R = +outerRadius;
-    SkScalar T = -outerRadius;
-    SkScalar B = +outerRadius;
-
     // We've extended the outer radius out half a pixel to antialias.
     // Expand the drawn rect here so all the pixels will be captured.
-    L += center.fX - SK_ScalarHalf;
-    R += center.fX + SK_ScalarHalf;
-    T += center.fY - SK_ScalarHalf;
-    B += center.fY + SK_ScalarHalf;
+    SkRect bounds = SkRect::MakeLTRB(
+        center.fX - outerRadius - SK_ScalarHalf,
+        center.fY - outerRadius - SK_ScalarHalf,
+        center.fX + outerRadius + SK_ScalarHalf,
+        center.fY + outerRadius + SK_ScalarHalf
+    );
 
-    verts[0].fPos = SkPoint::Make(L, T);
-    verts[1].fPos = SkPoint::Make(R, T);
-    verts[2].fPos = SkPoint::Make(L, B);
-    verts[3].fPos = SkPoint::Make(R, B);
+    verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
+    verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
+    verts[2].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
+    verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
 
-    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
+    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
 }
 
 void GrOvalRenderer::drawEllipse(GrDrawTarget* target,