diff options
author | 2013-04-03 14:56:40 +0000 | |
---|---|---|
committer | 2013-04-03 14:56:40 +0000 | |
commit | e4617bf6d45cdde07f89e341ebf5c485916bf0b2 (patch) | |
tree | bc80004002ca6d32afd391d9fbb5c00334084320 | |
parent | c37589d78ef31a36ddd227aa96bc22f0bf918fac (diff) |
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
-rw-r--r-- | src/gpu/GrDrawTarget.cpp | 122 | ||||
-rw-r--r-- | src/gpu/GrDrawTarget.h | 45 |
2 files changed, 143 insertions, 24 deletions
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index df6965d921..6edb109bc6 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -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) diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index fc4cd05446..ffde27e4b6 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -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; } |