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
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index a193848..3158a03 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -537,7 +537,7 @@
* @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 @@
* @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 @@
* @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 @@
};
/**
- * 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 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);
- }
- 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 (NULL != fContext) {
- fContext->setMatrix(fMatrix);
+ fContext = NULL;
}
}
@@ -770,9 +819,12 @@
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 9f9403e..3c29662 100644
--- a/include/gpu/GrPaint.h
+++ b/include/gpu/GrPaint.h
@@ -189,9 +189,8 @@
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 @@
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 a1ad8ec..7933e27 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -196,7 +196,10 @@
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 e00d94a..64d22be 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -442,14 +442,19 @@
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 8997280..ee3a4e1 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -603,12 +603,10 @@
}
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 @@
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 042e030..6846721 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrTextContext.cpp
@@ -206,12 +206,13 @@
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 d94a8dd..50cc12b 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 @@
// 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::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 @@
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::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 @@
SkAutoTUnref<GrTexture> 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 @@
// 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 @@
srcRect, sigma, sigma));
if (!isNormalBlur) {
+ context->setIdentityMatrix();
GrPaint paint;
paint.reset();
paint.colorSampler(0)->matrix()->setIDiv(pathTexture->width(),
@@ -900,10 +899,11 @@
}
}
- 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 @@
}
// 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 @@
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 @@
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 @@
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 @@
}
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 @@
return;
}
- GrContext::AutoMatrix avm(fContext, GrMatrix::I());
-
GrSamplerState* sampler = grPaint.colorSampler(kBitmapTextureIdx);
GrTexture* texture;
@@ -1584,7 +1580,7 @@
}
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<SkGpuDevice*>(device);
if (dev->fNeedClear) {
@@ -1592,7 +1588,8 @@
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 @@
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 @@
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 @@
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 @@
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::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);