From 3cbaa2d4da8bc39a99bf3afaaab43cc6dc481723 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Fri, 12 Oct 2012 14:51:52 +0000 Subject: [PATCH] Beef up GrContext::AutoMatrix to handle doing GrPaint matrix adjustments. R=robertphillips@google.com Review URL: https://codereview.appspot.com/6656047 git-svn-id: http://skia.googlecode.com/svn/trunk@5918 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/gpu/GrContext.h | 136 ++++++++++++++++-------- include/gpu/GrPaint.h | 21 +++- src/effects/SkBlendImageFilter.cpp | 5 +- src/effects/SkMorphologyImageFilter.cpp | 7 +- src/gpu/GrContext.cpp | 12 +-- src/gpu/GrTextContext.cpp | 7 +- src/gpu/SkGpuDevice.cpp | 86 +++++++-------- 7 files changed, 173 insertions(+), 101 deletions(-) diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index a193848f8..3158a03a1 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -537,7 +537,7 @@ public: * @param height height of rectangle to write in pixels. * @param config the pixel config of the source buffer * @param buffer memory to read the rectangle from. - * @param rowBytes number of bytes bewtween consecutive rows. Zero means rows are tightly + * @param rowBytes number of bytes between consecutive rows. Zero means rows are tightly * packed. * @param pixelOpsFlags see PixelOpsFlags enum above. */ @@ -556,7 +556,7 @@ public: * @param height height of rectangle to read in pixels. * @param config the pixel config of the destination buffer * @param buffer memory to read the rectangle into. - * @param rowBytes number of bytes bewtween consecutive rows. Zero means rows are tightly + * @param rowBytes number of bytes between consecutive rows. Zero means rows are tightly * packed. * @param pixelOpsFlags see PixelOpsFlags enum above. * @@ -578,7 +578,7 @@ public: * @param height height of rectangle to write in pixels. * @param config the pixel config of the source buffer * @param buffer memory to read pixels from - * @param rowBytes number of bytes bewtween consecutive rows. Zero + * @param rowBytes number of bytes between consecutive rows. Zero * means rows are tightly packed. * @param pixelOpsFlags see PixelOpsFlags enum above. */ @@ -669,55 +669,104 @@ public: }; /** - * Save/restore the view-matrix in the context. + * Save/restore the view-matrix in the context. It can optionally adjust a paint to account + * for a coordinate system change. Here is an example of how the paint param can be used: + * + * A GrPaint is setup with custom stages. The stages will have access to the pre-matrix source + * geometry positions when the draw is executed. Later on a decision is made to transform the + * geometry to device space on the CPU. The custom stages now need to know that the space in + * which the geometry will be specified has changed. + * + * Note that when restore is called (or in the destructor) the context's matrix will be + * restored. However, the paint will not be restored. The caller must make a copy of the + * paint if necessary. Hint: use SkTCopyOnFirstWrite if the AutoMatrix is conditionally + * initialized. */ class AutoMatrix : GrNoncopyable { public: - enum InitialMatrix { - kPreserve_InitialMatrix, - kIdentity_InitialMatrix, - }; - AutoMatrix() : fContext(NULL) {} - AutoMatrix(GrContext* ctx, InitialMatrix initialState) : fContext(ctx) { - fMatrix = ctx->getMatrix(); - switch (initialState) { - case kPreserve_InitialMatrix: - break; - case kIdentity_InitialMatrix: - ctx->setMatrix(GrMatrix::I()); - break; - default: - GrCrash("Unexpected initial matrix state"); + ~AutoMatrix() { this->restore(); } + + /** + * Initializes by pre-concat'ing the context's current matrix with the preConcat param. + */ + void setPreConcat(GrContext* context, const GrMatrix& preConcat, GrPaint* paint = NULL) { + GrAssert(NULL != context); + + this->restore(); + + fContext = context; + fMatrix = context->getMatrix(); + this->preConcat(preConcat, paint); + } + + /** + * Sets the context's matrix to identity. Returns false if the inverse matrix is required to + * update a paint but the matrix cannot be inverted. + */ + bool setIdentity(GrContext* context, GrPaint* paint = NULL) { + GrAssert(NULL != context); + + this->restore(); + + if (NULL != paint) { + if (!paint->preConcatSamplerMatricesWithInverse(context->getMatrix())) { + return false; + } } + fMatrix = context->getMatrix(); + fContext = context; + context->setIdentityMatrix(); + return true; } - AutoMatrix(GrContext* ctx, const GrMatrix& matrix) : fContext(ctx) { - fMatrix = ctx->getMatrix(); - ctx->setMatrix(matrix); + /** + * Replaces the context's matrix with a new matrix. Returns false if the inverse matrix is + * required to update a paint but the matrix cannot be inverted. + */ + bool set(GrContext* context, const GrMatrix& newMatrix, GrPaint* paint = NULL) { + if (NULL != paint) { + if (!this->setIdentity(context, paint)) { + return false; + } + this->preConcat(newMatrix, paint); + } else { + this->restore(); + fContext = context; + fMatrix = context->getMatrix(); + context->setMatrix(newMatrix); + } + return true; } - void set(GrContext* ctx) { - if (NULL != fContext) { - fContext->setMatrix(fMatrix); - } - fMatrix = ctx->getMatrix(); - fContext = ctx; - } - - void set(GrContext* ctx, const GrMatrix& matrix) { - if (NULL != fContext) { - fContext->setMatrix(fMatrix); - } - fMatrix = ctx->getMatrix(); - ctx->setMatrix(matrix); - fContext = ctx; - } - - ~AutoMatrix() { + /** + * If this has been initialized then the context's matrix will be further updated by + * pre-concat'ing the preConcat param. The matrix that will be restored remains unchanged. + * The paint is assumed to be relative to the context's matrix at the time this call is + * made, not the matrix at the time AutoMatrix was first initialized. In other words, this + * performs an incremental update of the paint. + */ + void preConcat(const GrMatrix& preConcat, GrPaint* paint = NULL) { + if (NULL != paint) { + paint->preConcatSamplerMatrices(preConcat); + } + fContext->concatMatrix(preConcat); + } + + /** + * Returns false if never initialized or the inverse matrix was required to update a paint + * but the matrix could not be inverted. + */ + bool succeeded() const { return NULL != fContext; } + + /** + * If this has been initialized then the context's original matrix is restored. + */ + void restore() { if (NULL != fContext) { fContext->setMatrix(fMatrix); + fContext = NULL; } } @@ -770,9 +819,12 @@ public: public: AutoWideOpenIdentityDraw(GrContext* ctx, GrRenderTarget* rt) : fAutoClip(ctx, AutoClip::kWideOpen_InitialClip) - , fAutoRT(ctx, rt) - , fAutoMatrix(ctx, AutoMatrix::kIdentity_InitialMatrix) { + , fAutoRT(ctx, rt) { + fAutoMatrix.setIdentity(ctx); + // should never fail with no paint param. + GrAssert(fAutoMatrix.succeeded()); } + private: AutoClip fAutoClip; AutoRenderTarget fAutoRT; diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h index 9f9403e9e..3c2966243 100644 --- a/include/gpu/GrPaint.h +++ b/include/gpu/GrPaint.h @@ -189,9 +189,8 @@ public: bool hasStage() const { return this->hasColorStage() || this->hasCoverageStage(); } /** - * Preconcats the matrix of all samplers in the mask with the inverse of a matrix. If the - * matrix inverse cannot be computed (and there is at least one enabled stage) then false is - * returned. + * Preconcats the matrix of all enabled stages with the inverse of a matrix. If the matrix + * inverse cannot be computed (and there is at least one enabled stage) then false is returned. */ bool preConcatSamplerMatricesWithInverse(const GrMatrix& matrix) { GrMatrix inv; @@ -219,6 +218,22 @@ public: return true; } + /** + * Preconcats the matrix of all stages with a matrix. + */ + void preConcatSamplerMatrices(const GrMatrix& matrix) { + for (int i = 0; i < kMaxColorStages; ++i) { + if (this->isColorStageEnabled(i)) { + fColorSamplers[i].preConcatMatrix(matrix); + } + } + for (int i = 0; i < kMaxCoverageStages; ++i) { + if (this->isCoverageStageEnabled(i)) { + fCoverageSamplers[i].preConcatMatrix(matrix); + } + } + } + GrPaint& operator=(const GrPaint& paint) { fSrcBlendCoeff = paint.fSrcBlendCoeff; fDstBlendCoeff = paint.fDstBlendCoeff; diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp index a1ad8ec1b..7933e2799 100644 --- a/src/effects/SkBlendImageFilter.cpp +++ b/src/effects/SkBlendImageFilter.cpp @@ -196,7 +196,10 @@ GrTexture* SkBlendImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, co GrAutoScratchTexture ast(context, desc); GrTexture* dst = ast.detach(); - GrContext::AutoMatrix avm(context, GrMatrix::I()); + + GrContext::AutoMatrix am; + am.setIdentity(context); + GrContext::AutoRenderTarget art(context, dst->asRenderTarget()); GrContext::AutoClip ac(context, rect); diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp index e00d94a75..64d22be56 100644 --- a/src/effects/SkMorphologyImageFilter.cpp +++ b/src/effects/SkMorphologyImageFilter.cpp @@ -442,14 +442,19 @@ GrTexture* apply_morphology(GrTexture* srcTexture, SkISize radius) { GrContext* context = srcTexture->getContext(); srcTexture->ref(); - GrContext::AutoMatrix avm(context, GrMatrix::I()); + + GrContext::AutoMatrix am; + am.setIdentity(context); + GrContext::AutoClip acs(context, GrRect::MakeWH(SkIntToScalar(srcTexture->width()), SkIntToScalar(srcTexture->height()))); + GrTextureDesc desc; desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; desc.fWidth = SkScalarCeilToInt(rect.width()); desc.fHeight = SkScalarCeilToInt(rect.height()); desc.fConfig = kRGBA_8888_GrPixelConfig; + if (radius.fWidth > 0) { GrAutoScratchTexture ast(context, desc); GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget()); diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 8997280ac..ee3a4e1af 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -603,12 +603,10 @@ void GrContext::drawPaint(const GrPaint& origPaint) { } inverse.mapRect(&r); } else { - if (paint->hasStage()) { - if (!paint.writable()->preConcatSamplerMatricesWithInverse(fDrawState->getViewMatrix())) { - GrPrintf("Could not invert matrix\n"); - } + if (!am.setIdentity(this, paint.writable())) { + GrPrintf("Could not invert matrix\n"); + return; } - am.set(this, GrMatrix::I()); } // by definition this fills the entire clip, no need for AA if (paint->isAntiAlias()) { @@ -1780,7 +1778,9 @@ GrTexture* GrContext::gaussianBlur(GrTexture* srcTexture, AutoRenderTarget art(this); - AutoMatrix avm(this, GrMatrix::I()); + AutoMatrix am; + am.setIdentity(this); + SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp index 042e0307d..684672152 100644 --- a/src/gpu/GrTextContext.cpp +++ b/src/gpu/GrTextContext.cpp @@ -206,12 +206,13 @@ void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, glyph->fPath = path; } - GrContext::AutoMatrix am(fContext, GrContext::AutoMatrix::kPreserve_InitialMatrix); + GrContext::AutoMatrix am; GrMatrix translate; translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)), GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop))); - fContext->concatMatrix(translate); - fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill); + GrPaint tmpPaint(fPaint); + am.setPreConcat(fContext, translate, &tmpPaint); + fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill); return; } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index d94a8dd63..50cc12b4a 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -26,13 +26,13 @@ #if 0 extern bool (*gShouldDrawProc)(); - #define CHECK_SHOULD_DRAW(draw) \ + #define CHECK_SHOULD_DRAW(draw, forceI) \ do { \ if (gShouldDrawProc && !gShouldDrawProc()) return; \ - this->prepareDraw(draw); \ + this->prepareDraw(draw, forceI); \ } while (0) #else - #define CHECK_SHOULD_DRAW(draw) this->prepareDraw(draw) + #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI) #endif // we use the same texture slot on GrPaint for bitmaps and shaders @@ -407,14 +407,18 @@ static void check_bounds(const GrClipData& clipData, // call this every draw call, to ensure that the context reflects our state, // and not the state from some other canvas/device -void SkGpuDevice::prepareDraw(const SkDraw& draw) { +void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) { GrAssert(NULL != fClipData.fClipStack); fContext->setRenderTarget(fRenderTarget); SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack); - fContext->setMatrix(*draw.fMatrix); + if (forceIdentity) { + fContext->setIdentityMatrix(); + } else { + fContext->setMatrix(*draw.fMatrix); + } fClipData.fOrigin = this->getOrigin(); #ifdef SK_DEBUG @@ -625,7 +629,7 @@ void SkGpuDevice::clear(SkColor color) { } void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); GrPaint grPaint; SkAutoCachedTexture textures[GrPaint::kMaxColorStages]; @@ -649,7 +653,7 @@ static const GrPrimitiveType gPointMode2PrimtiveType[] = { void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); SkScalar width = paint.getStrokeWidth(); if (width < 0) { @@ -688,7 +692,7 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint& paint) { CHECK_FOR_NODRAW_ANNOTATION(paint); - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); bool doStroke = paint.getStyle() != SkPaint::kFill_Style; SkScalar width = paint.getStrokeWidth(); @@ -837,21 +841,12 @@ bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath, SkAutoTUnref blurTexture; - GrMatrix origMatrix = context->getMatrix(); - - // We pass kPreserve here. We will replace the current matrix below. - GrContext::AutoMatrix avm(context, GrContext::AutoMatrix::kPreserve_InitialMatrix); - { GrContext::AutoRenderTarget art(context, pathTexture->asRenderTarget()); GrContext::AutoClip ac(context, srcRect); context->clear(NULL, 0); - // Draw hard shadow to pathTexture with path top-left at origin 0,0. - GrMatrix translate; - translate.setTranslate(offset.fX, offset.fY); - GrPaint tempPaint; if (grp->isAntiAlias()) { tempPaint.setAntiAlias(true); @@ -860,14 +855,17 @@ bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath, // to properly blend partially covered pixels. This means the AA // code path may not be taken. So we use a dst blend coeff of ISA. We // could special case AA draws to a dst surface with known alpha=0 to - // use a zero dst coeff when dual source blending isn't available. + // use a zero dst coeff when dual source blending isn't available.f tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); } - context->setMatrix(translate); - context->drawPath(tempPaint, devPath, pathFillType); - // switch to device coord drawing when going back to the main RT. - context->setIdentityMatrix(); + GrContext::AutoMatrix am; + + // Draw hard shadow to pathTexture with path top-left at origin using tempPaint. + GrMatrix translate; + translate.setTranslate(offset.fX, offset.fY); + am.set(context, translate); + context->drawPath(tempPaint, devPath, pathFillType); // If we're doing a normal blur, we can clobber the pathTexture in the // gaussianBlur. Otherwise, we need to save it for later compositing. @@ -876,6 +874,7 @@ bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath, srcRect, sigma, sigma)); if (!isNormalBlur) { + context->setIdentityMatrix(); GrPaint paint; paint.reset(); paint.colorSampler(0)->matrix()->setIDiv(pathTexture->width(), @@ -900,10 +899,11 @@ bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath, } } - if (!grp->preConcatSamplerMatricesWithInverse(origMatrix)) { + GrContext::AutoMatrix am; + if (!am.setIdentity(context, grp)) { return false; } - + static const int MASK_IDX = GrPaint::kMaxCoverageStages - 1; // we assume the last mask index is available for use GrAssert(!grp->isCoverageStageEnabled(MASK_IDX)); @@ -943,13 +943,9 @@ bool drawWithMaskFilter(GrContext* context, const SkPath& devPath, } // we now have a device-aligned 8bit mask in dstM, ready to be drawn using - // the current clip (and identity matrix) and grpaint settings - - if (!grp->preConcatSamplerMatricesWithInverse(context->getMatrix())) { - return false; - } - - GrContext::AutoMatrix avm(context, GrMatrix::I()); + // the current clip (and identity matrix) and GrPaint settings + GrContext::AutoMatrix am; + am.setIdentity(context, grp); GrTextureDesc desc; desc.fWidth = dstM.fBounds.width(); @@ -993,7 +989,7 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) { CHECK_FOR_NODRAW_ANNOTATION(paint); - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); bool doFill = true; @@ -1202,7 +1198,7 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, const SkRect* srcRectPtr, const SkMatrix& m, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); SkRect srcRect; if (NULL == srcRectPtr) { @@ -1465,7 +1461,8 @@ void apply_custom_stage(GrContext* context, const GrRect& rect, GrCustomStage* stage) { SkASSERT(srcTexture && srcTexture->getContext() == context); - GrContext::AutoMatrix avm(context, GrMatrix::I()); + GrContext::AutoMatrix am; + am.setIdentity(context); GrContext::AutoRenderTarget art(context, dstTexture->asRenderTarget()); GrContext::AutoClip acs(context, rect); @@ -1504,8 +1501,9 @@ static GrTexture* filter_texture(SkDevice* device, GrContext* context, } void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, - int left, int top, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); + int left, int top, const SkPaint& paint) { + // drawSprite is defined to be in device coords. + CHECK_SHOULD_DRAW(draw, true); SkAutoLockPixels alp(bitmap, !bitmap.getTexture()); if (!bitmap.getTexture() && !bitmap.readyToDraw()) { @@ -1521,8 +1519,6 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, return; } - GrContext::AutoMatrix avm(fContext, GrMatrix::I()); - GrSamplerState* sampler = grPaint.colorSampler(kBitmapTextureIdx); GrTexture* texture; @@ -1584,7 +1580,7 @@ void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, } void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device, - int x, int y, const SkPaint& paint) { + int x, int y, const SkPaint& paint) { // clear of the source device must occur before CHECK_SHOULD_DRAW SkGpuDevice* dev = static_cast(device); if (dev->fNeedClear) { @@ -1592,7 +1588,8 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device, dev->clear(0x0); } - CHECK_SHOULD_DRAW(draw); + // drawDevice is defined to be in device coords. + CHECK_SHOULD_DRAW(draw, true); GrPaint grPaint; SkAutoCachedTexture colorLutTexture; @@ -1622,7 +1619,6 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device, int w = bm.width(); int h = bm.height(); - GrContext::AutoMatrix avm(fContext, GrMatrix::I()); GrRect dstRect = GrRect::MakeXYWH(GrIntToScalar(x), GrIntToScalar(y), GrIntToScalar(w), @@ -1691,7 +1687,7 @@ void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, SkXfermode* xmode, const uint16_t indices[], int indexCount, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); GrPaint grPaint; SkAutoCachedTexture textures[GrPaint::kMaxColorStages]; @@ -1800,7 +1796,7 @@ SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) { void SkGpuDevice::drawText(const SkDraw& draw, const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); if (fContext->getMatrix().hasPerspective()) { // this guy will just call our drawPath() @@ -1827,7 +1823,7 @@ void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, size_t byteLength, const SkScalar pos[], SkScalar constY, int scalarsPerPos, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); if (fContext->getMatrix().hasPerspective()) { // this guy will just call our drawPath() @@ -1855,7 +1851,7 @@ void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t len, const SkPath& path, const SkMatrix* m, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); + CHECK_SHOULD_DRAW(draw, false); SkASSERT(draw.fDevice == this); draw.drawTextOnPath((const char*)text, len, path, m, paint);