diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index fff79220ff..c71abbf273 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -55,6 +55,8 @@ '<(skia_src_path)/gpu/GrAtlas.cpp', '<(skia_src_path)/gpu/GrAtlas.h', '<(skia_src_path)/gpu/GrBinHashKey.h', + '<(skia_src_path)/gpu/GrBlend.cpp', + '<(skia_src_path)/gpu/GrBlend.h', '<(skia_src_path)/gpu/GrBufferAllocPool.cpp', '<(skia_src_path)/gpu/GrBufferAllocPool.h', '<(skia_src_path)/gpu/GrCacheID.cpp', diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h index f148e390b5..71e6b9737b 100644 --- a/include/core/SkMatrix.h +++ b/include/core/SkMatrix.h @@ -470,6 +470,18 @@ public: return this->mapRect(rect, *rect); } + /** Apply this matrix to the src rectangle, and write the four transformed + points into dst. The points written to dst will be the original top-left, top-right, + bottom-right, and bottom-left points transformed by the matrix. + @param dst Where the transformed quad is written. + @param rect The original rectangle to be transformed. + */ + void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const { + // This could potentially be faster if we only transformed each x and y of the rect once. + rect.toQuad(dst); + this->mapPoints(dst, 4); + } + /** Return the mean radius of a circle after it has been mapped by this matrix. NOTE: in perspective this value assumes the circle has its center at the origin. diff --git a/include/core/SkRect.h b/include/core/SkRect.h index 9f3b59a38f..d0eaac40e4 100644 --- a/include/core/SkRect.h +++ b/include/core/SkRect.h @@ -466,8 +466,9 @@ struct SK_API SkRect { return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); } - /** return the 4 points that enclose the rectangle - */ + /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right, + bottom-left). TODO: Consider adding param to control whether quad is CW or CCW. + */ void toQuad(SkPoint quad[4]) const; /** Set this rectangle to the empty rectangle (0,0,0,0) diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h index 9e326f0495..59343e70ec 100644 --- a/include/gpu/GrPaint.h +++ b/include/gpu/GrPaint.h @@ -174,7 +174,28 @@ public: this->resetColorFilter(); } + /** + * Determines whether the drawing with this paint is opaque with respect to both color blending + * and fractional coverage. It does not consider whether AA has been enabled on the paint or + * not. Depending upon whether multisampling or coverage-based AA is in use, AA may make the + * result only apply to the interior of primitives. + * + */ + bool isOpaque() const; + + /** + * Returns true if isOpaque would return true and the paint represents a solid constant color + * draw. If the result is true, constantColor will be updated to contain the constant color. + */ + bool isOpaqueAndConstantColor(GrColor* constantColor) const; + private: + + /** + * Helper for isOpaque and isOpaqueAndConstantColor. + */ + bool getOpaqueAndKnownColor(GrColor* solidColor, uint32_t* solidColorKnownComponents) const; + /** * Called when the source coord system from which geometry is rendered changes. It ensures that * the local coordinates seen by effects remains unchanged. oldToNew gives the transformation diff --git a/include/gpu/GrSurface.h b/include/gpu/GrSurface.h index 52a56659ab..eeb63ad21b 100644 --- a/include/gpu/GrSurface.h +++ b/include/gpu/GrSurface.h @@ -11,6 +11,7 @@ #include "GrTypes.h" #include "GrResource.h" +#include "SkRect.h" class GrTexture; class GrRenderTarget; @@ -33,6 +34,12 @@ public: */ int height() const { return fDesc.fHeight; } + /** + * Helper that gets the width and height of the surface as a bounding rectangle. + */ + void getBoundsRect(SkRect* rect) const { rect->setWH(SkIntToScalar(this->width()), + SkIntToScalar(this->height())); } + GrSurfaceOrigin origin() const { GrAssert(kTopLeft_GrSurfaceOrigin == fDesc.fOrigin || kBottomLeft_GrSurfaceOrigin == fDesc.fOrigin); return fDesc.fOrigin; diff --git a/src/gpu/GrBlend.cpp b/src/gpu/GrBlend.cpp new file mode 100644 index 0000000000..c0621a9b3f --- /dev/null +++ b/src/gpu/GrBlend.cpp @@ -0,0 +1,154 @@ + +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrBlend.h" + +static inline GrBlendCoeff swap_coeff_src_dst(GrBlendCoeff coeff) { + switch (coeff) { + case kDC_GrBlendCoeff: + return kSC_GrBlendCoeff; + case kIDC_GrBlendCoeff: + return kISC_GrBlendCoeff; + case kDA_GrBlendCoeff: + return kSA_GrBlendCoeff; + case kIDA_GrBlendCoeff: + return kISA_GrBlendCoeff; + case kSC_GrBlendCoeff: + return kDC_GrBlendCoeff; + case kISC_GrBlendCoeff: + return kIDC_GrBlendCoeff; + case kSA_GrBlendCoeff: + return kDA_GrBlendCoeff; + case kISA_GrBlendCoeff: + return kIDA_GrBlendCoeff; + default: + return coeff; + } +} + +static inline unsigned saturated_add(unsigned a, unsigned b) { + SkASSERT(a <= 255); + SkASSERT(b <= 255); + unsigned sum = a + b; + if (sum > 255) { + sum = 255; + } + return sum; +} + +static GrColor add_colors(GrColor src, GrColor dst) { + unsigned r = saturated_add(GrColorUnpackR(src), GrColorUnpackR(dst)); + unsigned g = saturated_add(GrColorUnpackG(src), GrColorUnpackG(dst)); + unsigned b = saturated_add(GrColorUnpackB(src), GrColorUnpackB(dst)); + unsigned a = saturated_add(GrColorUnpackA(src), GrColorUnpackA(dst)); + return GrColorPackRGBA(r, g, b, a); +} + +static inline bool valid_color(uint32_t compFlags) { + return (kRGBA_GrColorComponentFlags & compFlags) == kRGBA_GrColorComponentFlags; +} + +static GrColor simplify_blend_term(GrBlendCoeff* srcCoeff, + GrColor srcColor, uint32_t srcCompFlags, + GrColor dstColor, uint32_t dstCompFlags, + GrColor constantColor) { + + GrAssert(!GrBlendCoeffRefsSrc(*srcCoeff)); + GrAssert(NULL != srcCoeff); + + // Check whether srcCoeff can be reduced to kOne or kZero based on known color inputs. + // We could pick out the coeff r,g,b,a values here and use them to compute the blend term color, + // if possible, below but that is not implemented now. + switch (*srcCoeff) { + case kIDC_GrBlendCoeff: + dstColor = ~dstColor; // fallthrough + case kDC_GrBlendCoeff: + if (valid_color(dstCompFlags)) { + if (0xffffffff == dstColor) { + *srcCoeff = kOne_GrBlendCoeff; + } else if (0 == dstColor) { + *srcCoeff = kZero_GrBlendCoeff; + } + } + break; + + case kIDA_GrBlendCoeff: + dstColor = ~dstColor; // fallthrough + case kDA_GrBlendCoeff: + if (kA_GrColorComponentFlag & dstCompFlags) { + if (0xff == GrColorUnpackA(dstColor)) { + *srcCoeff = kOne_GrBlendCoeff; + } else if (0 == GrColorUnpackA(dstColor)) { + *srcCoeff = kZero_GrBlendCoeff; + } + } + break; + + case kIConstC_GrBlendCoeff: + constantColor = ~constantColor; // fallthrough + case kConstC_GrBlendCoeff: + if (0xffffffff == constantColor) { + *srcCoeff = kOne_GrBlendCoeff; + } else if (0 == constantColor) { + *srcCoeff = kZero_GrBlendCoeff; + } + break; + + case kIConstA_GrBlendCoeff: + constantColor = ~constantColor; // fallthrough + case kConstA_GrBlendCoeff: + if (0xff == GrColorUnpackA(constantColor)) { + *srcCoeff = kOne_GrBlendCoeff; + } else if (0 == GrColorUnpackA(constantColor)) { + *srcCoeff = kZero_GrBlendCoeff; + } + break; + + default: + break; + } + // We may have invalidated these above and shouldn't read them again. + GR_DEBUGCODE(dstColor = constantColor = GrColor_ILLEGAL;) + + if (kZero_GrBlendCoeff == *srcCoeff || (valid_color(srcCompFlags) && 0 == srcColor)) { + *srcCoeff = kZero_GrBlendCoeff; + return 0; + } + + if (kOne_GrBlendCoeff == *srcCoeff && valid_color(srcCompFlags)) { + return srcColor; + } else { + return GrColor_ILLEGAL; + } +} + +GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff, + GrBlendCoeff* dstCoeff, + GrColor srcColor, uint32_t srcCompFlags, + GrColor dstColor, uint32_t dstCompFlags, + GrColor constantColor) { + GrColor srcTermColor = simplify_blend_term(srcCoeff, + srcColor, srcCompFlags, + dstColor, dstCompFlags, + constantColor); + + // We call the same function to simplify the dst blend coeff. We trick it out by swapping the + // src and dst. + GrBlendCoeff spoofedCoeff = swap_coeff_src_dst(*dstCoeff); + GrColor dstTermColor = simplify_blend_term(&spoofedCoeff, + dstColor, dstCompFlags, + srcColor, srcCompFlags, + constantColor); + *dstCoeff = swap_coeff_src_dst(spoofedCoeff); + + if (GrColor_ILLEGAL != srcTermColor && GrColor_ILLEGAL != dstTermColor) { + return add_colors(srcTermColor, dstTermColor); + } else { + return GrColor_ILLEGAL; + } +} diff --git a/src/gpu/GrBlend.h b/src/gpu/GrBlend.h new file mode 100644 index 0000000000..e70d945777 --- /dev/null +++ b/src/gpu/GrBlend.h @@ -0,0 +1,45 @@ + +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTypes.h" +#include "GrColor.h" + +#ifndef GrBlend_DEFINED +#define GrBlend_DEFINED + +static inline bool GrBlendCoeffRefsSrc(GrBlendCoeff coeff) { + switch (coeff) { + case kSC_GrBlendCoeff: + case kISC_GrBlendCoeff: + case kSA_GrBlendCoeff: + case kISA_GrBlendCoeff: + return true; + default: + return false; + } +} + +static inline bool GrBlendCoeffRefsDst(GrBlendCoeff coeff) { + switch (coeff) { + case kDC_GrBlendCoeff: + case kIDC_GrBlendCoeff: + case kDA_GrBlendCoeff: + case kIDA_GrBlendCoeff: + return true; + default: + return false; + } +} + +GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff, + GrBlendCoeff* dstCoeff, + GrColor srcColor, uint32_t srcCompFlags, + GrColor dstColor, uint32_t dstCompFlags, + GrColor constantColor); + +#endif diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 20c633dc67..4d5551e494 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -681,9 +681,8 @@ static bool isIRect(const SkRect& r) { static bool apply_aa_to_rect(GrDrawTarget* target, const SkRect& rect, SkScalar strokeWidth, - const SkMatrix* matrix, - SkMatrix* combinedMatrix, - SkRect* devRect, + const SkMatrix& combinedMatrix, + SkRect* devBoundRect, bool* useVertexCoverage) { // we use a simple coverage ramp to do aa on axis-aligned rects // we check if the rect will be axis-aligned, and the rect won't land on @@ -716,52 +715,32 @@ static bool apply_aa_to_rect(GrDrawTarget* target, #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) if (strokeWidth >= 0) { #endif - if (!drawState.getViewMatrix().preservesAxisAlignment()) { + if (!combinedMatrix.preservesAxisAlignment()) { return false; } - if (NULL != matrix && !matrix->preservesAxisAlignment()) { - return false; - } #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) } else { - if (!drawState.getViewMatrix().preservesAxisAlignment() && - !drawState.getViewMatrix().preservesRightAngles()) { - return false; - } - - if (NULL != matrix && !matrix->preservesRightAngles()) { + if (!combinedMatrix.preservesRightAngles()) { return false; } } #endif - *combinedMatrix = drawState.getViewMatrix(); - if (NULL != matrix) { - combinedMatrix->preConcat(*matrix); - -#if GR_DEBUG -#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) - if (strokeWidth >= 0) { -#endif - GrAssert(combinedMatrix->preservesAxisAlignment()); -#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) - } else { - GrAssert(combinedMatrix->preservesRightAngles()); - } -#endif -#endif - } - - combinedMatrix->mapRect(devRect, rect); + combinedMatrix.mapRect(devBoundRect, rect); if (strokeWidth < 0) { - return !isIRect(*devRect); + return !isIRect(*devBoundRect); } else { return true; } } +static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) { + return point.fX >= rect.fLeft && point.fX <= rect.fRight && + point.fY >= rect.fTop && point.fY <= rect.fBottom; +} + void GrContext::drawRect(const GrPaint& paint, const SkRect& rect, SkScalar width, @@ -771,13 +750,51 @@ void GrContext::drawRect(const GrPaint& paint, AutoRestoreEffects are; GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are); - SkRect devRect; - SkMatrix combinedMatrix; + SkMatrix combinedMatrix = target->drawState()->getViewMatrix(); + if (NULL != matrix) { + combinedMatrix.preConcat(*matrix); + } + + // Check if this is a full RT draw and can be replaced with a clear. We don't bother checking + // cases where the RT is fully inside a stroke. + if (width < 0) { + SkRect rtRect; + target->getDrawState().getRenderTarget()->getBoundsRect(&rtRect); + SkRect clipSpaceRTRect = rtRect; + bool checkClip = false; + if (NULL != this->getClip()) { + checkClip = true; + clipSpaceRTRect.offset(SkIntToScalar(this->getClip()->fOrigin.fX), + SkIntToScalar(this->getClip()->fOrigin.fY)); + } + // Does the clip contain the entire RT? + if (!checkClip || target->getClip()->fClipStack->quickContains(clipSpaceRTRect)) { + SkMatrix invM; + if (!combinedMatrix.invert(&invM)) { + return; + } + // Does the rect bound the RT? + SkPoint srcSpaceRTQuad[4]; + invM.mapRectToQuad(srcSpaceRTQuad, rtRect); + if (rect_contains_inclusive(rect, srcSpaceRTQuad[0]) && + rect_contains_inclusive(rect, srcSpaceRTQuad[1]) && + rect_contains_inclusive(rect, srcSpaceRTQuad[2]) && + rect_contains_inclusive(rect, srcSpaceRTQuad[3])) { + // Will it blend? + GrColor clearColor; + if (paint.isOpaqueAndConstantColor(&clearColor)) { + target->clear(NULL, clearColor); + return; + } + } + } + } + + SkRect devBoundRect; bool useVertexCoverage; bool needAA = paint.isAntiAlias() && !target->getDrawState().getRenderTarget()->isMultisampled(); - bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix, - &combinedMatrix, &devRect, + bool doAA = needAA && apply_aa_to_rect(target, rect, width, combinedMatrix, &devBoundRect, &useVertexCoverage); if (doAA) { GrDrawState::AutoViewMatrixRestore avmr; @@ -786,12 +803,12 @@ void GrContext::drawRect(const GrPaint& paint, } if (width >= 0) { fAARectRenderer->strokeAARect(this->getGpu(), target, - rect, combinedMatrix, devRect, + rect, combinedMatrix, devBoundRect, width, useVertexCoverage); } else { // filled AA rect fAARectRenderer->fillAARect(this->getGpu(), target, - rect, combinedMatrix, devRect, + rect, combinedMatrix, devBoundRect, useVertexCoverage); } return; diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h index c006e6c5dd..785be77f58 100644 --- a/src/gpu/GrDrawState.h +++ b/src/gpu/GrDrawState.h @@ -9,6 +9,7 @@ #define GrDrawState_DEFINED #include "GrBackendEffectFactory.h" +#include "GrBlend.h" #include "GrColor.h" #include "GrEffectStage.h" #include "GrPaint.h" @@ -469,27 +470,11 @@ public: fCommon.fSrcBlend = srcCoeff; fCommon.fDstBlend = dstCoeff; #if GR_DEBUG - switch (dstCoeff) { - case kDC_GrBlendCoeff: - case kIDC_GrBlendCoeff: - case kDA_GrBlendCoeff: - case kIDA_GrBlendCoeff: - GrPrintf("Unexpected dst blend coeff. Won't work correctly with" - "coverage stages.\n"); - break; - default: - break; + if (GrBlendCoeffRefsDst(dstCoeff)) { + GrPrintf("Unexpected dst blend coeff. Won't work correctly with coverage stages.\n"); } - switch (srcCoeff) { - case kSC_GrBlendCoeff: - case kISC_GrBlendCoeff: - case kSA_GrBlendCoeff: - case kISA_GrBlendCoeff: - GrPrintf("Unexpected src blend coeff. Won't work correctly with" - "coverage stages.\n"); - break; - default: - break; + if (GrBlendCoeffRefsSrc(srcCoeff)) { + GrPrintf("Unexpected src blend coeff. Won't work correctly with coverage stages.\n"); } #endif } diff --git a/src/gpu/GrPaint.cpp b/src/gpu/GrPaint.cpp index d67f2e8707..898a32ff70 100644 --- a/src/gpu/GrPaint.cpp +++ b/src/gpu/GrPaint.cpp @@ -8,6 +8,7 @@ #include "GrPaint.h" +#include "GrBlend.h" #include "effects/GrSimpleTextureEffect.h" void GrPaint::addColorTextureEffect(GrTexture* texture, const SkMatrix& matrix) { @@ -33,3 +34,96 @@ void GrPaint::addCoverageTextureEffect(GrTexture* texture, GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params); this->addCoverageEffect(effect)->unref(); } + +bool GrPaint::isOpaque() const { + return this->getOpaqueAndKnownColor(NULL, NULL); +} + +bool GrPaint::isOpaqueAndConstantColor(GrColor* color) const { + GrColor tempColor; + uint32_t colorComps; + if (this->getOpaqueAndKnownColor(&tempColor, &colorComps)) { + if (kRGBA_GrColorComponentFlags == colorComps) { + *color = tempColor; + return true; + } + } + return false; +} + +bool GrPaint::getOpaqueAndKnownColor(GrColor* solidColor, + uint32_t* solidColorKnownComponents) const { + + // TODO: Share this implementation with GrDrawState + + // Since fColorFilterXfermode is going away soon, we aren't attempting to handle anything but + // the default setting. + if (SkXfermode::kDst_Mode != fColorFilterXfermode) { + return false; + } + + GrColor coverage = GrColorPackRGBA(fCoverage, fCoverage, fCoverage, fCoverage); + uint32_t coverageComps = kRGBA_GrColorComponentFlags; + int count = fCoverageStages.count(); + for (int i = 0; i < count; ++i) { + (*fCoverageStages[i].getEffect())->getConstantColorComponents(&coverage, &coverageComps); + } + if (kRGBA_GrColorComponentFlags != coverageComps || 0xffffffff != coverage) { + return false; + } + + GrColor color = fColor; + uint32_t colorComps = kRGBA_GrColorComponentFlags; + count = fColorStages.count(); + for (int i = 0; i < count; ++i) { + (*fColorStages[i].getEffect())->getConstantColorComponents(&color, &colorComps); + } + + GrAssert((NULL == solidColor) == (NULL == solidColorKnownComponents)); + + GrBlendCoeff srcCoeff = fSrcBlendCoeff; + GrBlendCoeff dstCoeff = fDstBlendCoeff; + GrSimplifyBlend(&srcCoeff, &dstCoeff, color, colorComps, 0, 0, 0); + + bool opaque = kZero_GrBlendCoeff == dstCoeff && !GrBlendCoeffRefsDst(srcCoeff); + if (NULL != solidColor) { + if (opaque) { + switch (srcCoeff) { + case kZero_GrBlendCoeff: + *solidColor = 0; + *solidColorKnownComponents = kRGBA_GrColorComponentFlags; + break; + + case kOne_GrBlendCoeff: + *solidColor = color; + *solidColorKnownComponents = colorComps; + break; + + // The src coeff should never refer to the src and if it refers to dst then opaque + // should have been false. + case kSC_GrBlendCoeff: + case kISC_GrBlendCoeff: + case kDC_GrBlendCoeff: + case kIDC_GrBlendCoeff: + case kSA_GrBlendCoeff: + case kISA_GrBlendCoeff: + case kDA_GrBlendCoeff: + case kIDA_GrBlendCoeff: + default: + GrCrash("srcCoeff should not refer to src or dst."); + break; + + // TODO: update this once GrPaint actually has a const color. + case kConstC_GrBlendCoeff: + case kIConstC_GrBlendCoeff: + case kConstA_GrBlendCoeff: + case kIConstA_GrBlendCoeff: + *solidColorKnownComponents = 0; + break; + } + } else { + solidColorKnownComponents = 0; + } + } + return opaque; +}