Add GrDrawTarget::copySurface.
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 3 Apr 2013 14:56:40 +0000 (14:56 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 3 Apr 2013 14:56:40 +0000 (14:56 +0000)
Review URL: https://codereview.chromium.org/13428004

git-svn-id: http://skia.googlecode.com/svn/trunk@8510 2bbb7eff-a529-9590-31e7-b0007b416f81

src/gpu/GrDrawTarget.cpp
src/gpu/GrDrawTarget.h

index df6965d..6edb109 100644 (file)
@@ -412,8 +412,7 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) {
     }
     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.
+    // texture. TODO: API-specific impl of onCopySurface that can handle more cases.
     if (NULL == rt->asTexture()) {
         GrPrintf("Reading Dst of non-texture render target is not currently supported.\n");
         return false;
@@ -436,8 +435,6 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) {
 #endif
     }
 
-    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
@@ -456,21 +453,14 @@ bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) {
         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 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(copyRect.fLeft, copyRect.fTop);
-    return true;
+    SkIPoint dstPoint = {0, 0};
+    if (this->copySurface(ast.texture(), rt, copyRect, dstPoint)) {
+        info->fDstCopy.setTexture(ast.texture());
+        info->fDstCopy.setOffset(copyRect.fLeft, copyRect.fTop);
+        return true;
+    } else {
+        return false;
+    }
 }
 
 void GrDrawTarget::drawIndexed(GrPrimitiveType type,
@@ -761,6 +751,100 @@ GrDrawTarget::AutoClipRestore::AutoClipRestore(GrDrawTarget* target, const SkIRe
     target->setClip(&fReplacementClip);
 }
 
+bool GrDrawTarget::copySurface(GrSurface* dst,
+                               GrSurface* src,
+                               const SkIRect& srcRect,
+                               const SkIPoint& dstPoint) {
+    SkIRect clippedSrcRect(srcRect);
+    SkIPoint clippedDstPoint(dstPoint);
+    
+    // clip the left edge to src and dst bounds, adjusting dstPoint if neceessary
+    if (clippedSrcRect.fLeft < 0) {
+        clippedDstPoint.fX -= clippedSrcRect.fLeft;
+        clippedSrcRect.fLeft = 0;
+    }
+    if (clippedDstPoint.fX < 0) {
+        clippedSrcRect.fLeft -= clippedDstPoint.fX;
+        clippedDstPoint.fX = 0;
+    }
+
+    // clip the top edge to src and dst bounds, adjusting dstPoint if neceessary
+    if (clippedSrcRect.fTop < 0) {
+        clippedDstPoint.fY -= clippedSrcRect.fTop;
+        clippedSrcRect.fTop = 0;
+    }
+    if (clippedDstPoint.fY < 0) {
+        clippedSrcRect.fTop -= clippedDstPoint.fY;
+        clippedDstPoint.fY = 0;
+    }
+    
+    // clip the right edge to the src and dst bounds.
+    if (clippedSrcRect.fRight > src->width()) {
+        clippedSrcRect.fRight = src->width();
+    }
+    if (clippedDstPoint.fX + clippedSrcRect.width() > dst->width()) {
+        clippedSrcRect.fRight = clippedSrcRect.fLeft + dst->width() - clippedDstPoint.fX;
+    }
+
+    // clip the bottom edge to the src and dst bounds.
+    if (clippedSrcRect.fBottom > src->height()) {
+        clippedSrcRect.fBottom = src->height();
+    }
+    if (clippedDstPoint.fY + clippedSrcRect.height() > dst->height()) {
+        clippedSrcRect.fBottom = clippedSrcRect.fTop + dst->height() - clippedDstPoint.fY;
+    }
+  
+    // The above clipping steps may have inverted the rect if it didn't intersect either the src or
+    // dst bounds.
+    if (clippedSrcRect.isEmpty()) {
+        return true;
+    }
+
+    bool result = this->onCopySurface(dst, src, clippedSrcRect, clippedDstPoint);
+    GrAssert(result == this->canCopySurface(dst, src, clippedSrcRect, clippedDstPoint));
+    return result;
+}
+
+bool GrDrawTarget::canCopySurface(GrSurface* dst,
+                                  GrSurface* src,
+                                  const SkIRect& srcRect,
+                                  const SkIPoint& dstPoint) {
+    // Check that the read/write rects are contained within the src/dst bounds.
+    GrAssert(!srcRect.isEmpty());
+    GrAssert(SkIRect::MakeWH(src->width(), src->height()).contains(srcRect));
+    GrAssert(dstPoint.fX >= 0 && dstPoint.fY >= 0);
+    GrAssert(dstPoint.fX + srcRect.width() <= dst->width() &&
+             dstPoint.fY + srcRect.height() <= dst->height());
+
+    return NULL != dst->asRenderTarget() && NULL != src->asTexture();
+}
+
+bool GrDrawTarget::onCopySurface(GrSurface* dst,
+                                 GrSurface* src,
+                                 const SkIRect& srcRect,
+                                 const SkIPoint& dstPoint) {
+    if (!GrDrawTarget::canCopySurface(dst, src, srcRect, dstPoint)) {
+        return false;
+    }
+
+    GrRenderTarget* rt = dst->asRenderTarget();
+    GrTexture* tex = src->asTexture();
+
+    GrDrawTarget::AutoStateRestore asr(this, kReset_ASRInit);
+    this->drawState()->setRenderTarget(rt);
+    SkMatrix matrix;
+    matrix.setTranslate(SkIntToScalar(srcRect.fLeft - dstPoint.fX),
+                        SkIntToScalar(srcRect.fTop - dstPoint.fY));
+    matrix.postIDiv(tex->width(), tex->height());
+    this->drawState()->createTextureEffect(0, tex, matrix);
+    SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX,
+                                        dstPoint.fY,
+                                        srcRect.width(),
+                                        srcRect.height());
+    this->drawSimpleRect(dstRect);
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SK_DEFINE_INST_COUNT(GrDrawTargetCaps)
index fc4cd05..ffde27e 100644 (file)
@@ -139,8 +139,8 @@ public:
      *    data. The target provides ptrs to hold the vertex and/or index data.
      *
      *    The data is writable up until the next drawIndexed, drawNonIndexed,
-     *    drawIndexedInstances, or pushGeometrySource. At this point the data is
-     *    frozen and the ptrs are no longer valid.
+     *    drawIndexedInstances, drawRect, copySurface, or pushGeometrySource. At
+     *    this point the data is frozen and the ptrs are no longer valid.
      *
      *    Where the space is allocated and how it is uploaded to the GPU is
      *    subclass-dependent.
@@ -169,9 +169,9 @@ public:
      * source is reset and likewise for indexCount.
      *
      * The pointers to the space allocated for vertices and indices remain valid
-     * until a drawIndexed, drawNonIndexed, drawIndexedInstances, or push/
-     * popGeomtrySource is called. At that point logically a snapshot of the
-     * data is made and the pointers are invalid.
+     * until a drawIndexed, drawNonIndexed, drawIndexedInstances, drawRect,
+     * copySurface, or push/popGeomtrySource is called. At that point logically a
+     * snapshot of the data is made and the pointers are invalid.
      *
      * @param vertexCount  the number of vertices to reserve space for. Can be
      *                     0. Vertex size is queried from the current GrDrawState.
@@ -406,6 +406,22 @@ public:
     virtual void clear(const GrIRect* rect,
                        GrColor color,
                        GrRenderTarget* renderTarget = NULL) = 0;
+    
+    /**
+     * Copies a pixel rectangle from one surface to another. This call may finalize
+     * reserved vertex/index data (as though a draw call was made). The src pixels
+     * copied are specified by srcRect. They are copied to a rect of the same
+     * size in dst with top left at dstPoint. If the src rect is clipped by the
+     * src bounds then  pixel values in the dst rect corresponding to area clipped
+     * by the src rect are not overwritten. This method can fail and return false
+     * depending on the type of surface, configs, etc, and the backend-specific
+     * limitations. If rect is clipped out entirely by the src or dst bounds then
+     * true is returned since there is no actual copy necessary to succeed.
+     */
+    bool copySurface(GrSurface* dst,
+                     GrSurface* src,
+                     const SkIRect& srcRect,
+                     const SkIPoint& dstPoint);
 
     /**
      * Release any resources that are cached but not currently in use. This
@@ -616,6 +632,25 @@ protected:
         }
     }
 
+    // This method is called by copySurface  The srcRect is guaranteed to be entirely within the
+    // src bounds. Likewise, the dst rect implied by dstPoint and srcRect's width and height falls
+    // entirely within the dst. The default implementation will draw a rect from the src to the
+    // dst if the src is a texture and the dst is a render target and fail otherwise.
+    virtual bool onCopySurface(GrSurface* dst,
+                               GrSurface* src,
+                               const SkIRect& srcRect,
+                               const SkIPoint& dstPoint);
+
+    // Called to determine whether an onCopySurface call would succeed or not. This is useful for
+    // proxy subclasses to test whether the copy would succeed without executing it yet. Derived
+    // classes must keep this consistent with their implementation of onCopySurface(). The inputs
+    // are the same as onCopySurface(), i.e. srcRect and dstPoint are clipped to be inside the src
+    // and dst bounds.
+    virtual bool canCopySurface(GrSurface* dst,
+                                GrSurface* src,
+                                const SkIRect& srcRect,
+                                const SkIPoint& dstPoint);
+
     GrContext* getContext() { return fContext; }
     const GrContext* getContext() const { return fContext; }