Add support for reading the dst pixel value in an effect. Use in a new effect for the kDarken xfer mode.

The current implementation is to always make a copy of the entire dst before the draw.
It will only succeed if the RT is also a texture.
Obviously, there is lots of room for improvement.
Review URL: https://codereview.chromium.org/13314002

git-svn-id: http://skia.googlecode.com/svn/trunk@8449 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 64576af..4d4ba49 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -9,6 +9,7 @@
 
 
 #include "GrDrawTarget.h"
+#include "GrContext.h"
 #include "GrDrawTargetCaps.h"
 #include "GrRenderTarget.h"
 #include "GrTexture.h"
@@ -38,6 +39,9 @@
     } else {
         fDevBounds = NULL;
     }
+
+    fDstCopy = di.fDstCopy;
+
     return *this;
 }
 
@@ -402,6 +406,62 @@
     return true;
 }
 
+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) {
+        return true;
+    }
+    GrRenderTarget* rt = this->drawState()->getRenderTarget();
+    // If the dst is not a texture then we don't currently have a way of copying the
+    // texture. TODO: make copying RT->Tex (or Surface->Surface) a GrDrawTarget operation that can
+    // be built on top of GL/D3D APIs.
+    if (NULL == rt->asTexture()) {
+        GrPrintf("Reading Dst of non-texture render target is not currently supported.\n");
+        return false;
+    }
+    // TODO: Consider bounds of draw and bounds of clip
+
+    GrDrawTarget::AutoGeometryAndStatePush agasp(this, kReset_ASRInit);
+
+    // The draw will resolve dst if it has MSAA. Two things to consider in the future:
+    // 1) to make the dst values be pre-resolve we'd need to be able to copy to MSAA
+    // texture and sample it correctly in the shader. 2) If 1 isn't available then we
+    // should just resolve and use the resolved texture directly rather than making a
+    // copy of it.
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = rt->width();
+    desc.fHeight = rt->height();
+    desc.fSampleCnt = 0;
+    desc.fConfig = rt->config();
+
+    GrAutoScratchTexture ast(fContext, desc, GrContext::kApprox_ScratchTexMatch);
+
+    if (NULL == ast.texture()) {
+        GrPrintf("Failed to create temporary copy of destination texture.\n");
+        return false;
+    }
+    this->drawState()->disableState(GrDrawState::kClip_StateBit);
+    this->drawState()->setRenderTarget(ast.texture()->asRenderTarget());
+    static const int kTextureStage = 0;
+    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);
+    info->fDstCopy.setTexture(ast.texture());
+    info->fDstCopy.setOffset(0, 0);
+    return true;
+}
+
 void GrDrawTarget::drawIndexed(GrPrimitiveType type,
                                int startVertex,
                                int startIndex,
@@ -423,6 +483,10 @@
         if (NULL != devBounds) {
             info.setDevBounds(*devBounds);
         }
+        // TODO: We should continue with incorrect blending.
+        if (!this->setupDstReadIfNecessary(&info)) {
+            return;
+        }
         this->onDraw(info);
     }
 }
@@ -446,6 +510,10 @@
         if (NULL != devBounds) {
             info.setDevBounds(*devBounds);
         }
+        // TODO: We should continue with incorrect blending.
+        if (!this->setupDstReadIfNecessary(&info)) {
+            return;
+        }
         this->onDraw(info);
     }
 }
@@ -508,6 +576,10 @@
     if (NULL != devBounds) {
         info.setDevBounds(*devBounds);
     }
+    // TODO: We should continue with incorrect blending.
+    if (!this->setupDstReadIfNecessary(&info)) {
+        return;
+    }
 
     while (instanceCount) {
         info.fInstanceCount = GrMin(instanceCount, maxInstancesPerDraw);