Add GrDrawTarget::copySurface.
Review URL: https://codereview.chromium.org/13428004
git-svn-id: http://skia.googlecode.com/svn/trunk@8510 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index df6965d..6edb109 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -412,8 +412,7 @@
}
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 @@
#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 @@
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 @@
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)