Index: src/gpu/GrTextureParamsAdjuster.cpp |
diff --git a/src/gpu/GrTextureParamsAdjuster.cpp b/src/gpu/GrTextureParamsAdjuster.cpp |
index 579cf96ef36d30823a70b491cc0015e6813be116..b98a126971de0836d666226a184179d50588632c 100644 |
--- a/src/gpu/GrTextureParamsAdjuster.cpp |
+++ b/src/gpu/GrTextureParamsAdjuster.cpp |
@@ -19,6 +19,7 @@ |
#include "SkCanvas.h" |
#include "SkGr.h" |
#include "SkGrPriv.h" |
+#include "effects/GrBicubicEffect.h" |
#include "effects/GrTextureDomain.h" |
typedef GrTextureProducer::CopyParams CopyParams; |
@@ -131,7 +132,7 @@ GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrTextureParams& par |
GrTexture* texture = this->originalTexture(); |
GrContext* context = texture->getContext(); |
CopyParams copyParams; |
- const SkIRect* contentArea = this->contentArea(); |
+ const SkIRect* contentArea = this->contentAreaOrNull(); |
if (contentArea && GrTextureParams::kMipMap_FilterMode == params.filterMode()) { |
// If we generate a MIP chain for texture it will read pixel values from outside the content |
@@ -171,6 +172,211 @@ GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrTextureParams& par |
return result; |
} |
+enum DomainMode { |
+ kNoDomain_DomainMode, |
+ kDomain_DomainMode, |
+ kTightCopy_DomainMode |
+}; |
+ |
+/** Determines whether a texture domain is necessary and if so what domain to use. There are two |
+ * rectangles to consider: |
+ * - The first is the content area specified by the texture adjuster. We can *never* allow |
+ * filtering to cause bleed of pixels outside this rectangle. |
+ * - The second rectangle is the constraint rectangle, which is known to be contained by the |
+ * content area. The filterConstraint specifies whether we are allowed to bleed across this |
+ * rect. |
+ * |
+ * We want to avoid using a domain if possible. We consider the above rectangles, the filter type, |
+ * and whether the coords generated by the draw would all fall within the constraint rect. If the |
+ * latter is true we only need to consider whether the filter would extend beyond the rects. |
+ */ |
+static DomainMode determine_domain_mode( |
+ const SkRect& constraintRect, |
+ GrTextureAdjuster::FilterConstraint filterConstraint, |
+ bool coordsLimitedToConstraintRect, |
+ int texW, int texH, |
+ const SkIRect* textureContentArea, |
+ const GrTextureParams::FilterMode* filterModeOrNullForBicubic, |
+ SkRect* domainRect) { |
+ |
+ SkASSERT(SkRect::MakeIWH(texW, texH).contains(constraintRect)); |
+ // We only expect a content area rect if there is some non-content area. |
+ SkASSERT(!textureContentArea || |
+ (!textureContentArea->contains(SkIRect::MakeWH(texW, texH)) && |
+ SkRect::Make(*textureContentArea).contains(constraintRect))); |
+ |
+ SkRect textureBounds = SkRect::MakeIWH(texW, texH); |
+ // If the src rectangle contains the whole texture then no need for a domain. |
+ if (constraintRect.contains(textureBounds)) { |
+ return kNoDomain_DomainMode; |
+ } |
+ |
+ bool restrictFilterToRect = (filterConstraint == GrTextureAdjuster::kYes_FilterConstraint); |
+ |
+ // If we can filter outside the constraint rect, and there is no non-content area of the |
+ // texture, and we aren't going to generate sample coords outside the constraint rect then we |
+ // don't need a domain. |
+ if (!restrictFilterToRect && !textureContentArea && coordsLimitedToConstraintRect) { |
+ return kNoDomain_DomainMode; |
+ } |
+ |
+ // Get the domain inset based on sampling mode (or bail if mipped) |
+ SkScalar filterHalfWidth = 0.f; |
+ if (filterModeOrNullForBicubic) { |
+ switch (*filterModeOrNullForBicubic) { |
+ case GrTextureParams::kNone_FilterMode: |
+ if (coordsLimitedToConstraintRect) { |
+ return kNoDomain_DomainMode; |
+ } else { |
+ filterHalfWidth = 0.f; |
+ } |
+ break; |
+ case GrTextureParams::kBilerp_FilterMode: |
+ filterHalfWidth = .5f; |
+ break; |
+ case GrTextureParams::kMipMap_FilterMode: |
+ // No domain can save use here. |
+ return kTightCopy_DomainMode; |
+ } |
+ } else { |
+ // bicubic does nearest filtering internally. |
+ filterHalfWidth = 1.5f; |
+ } |
+ |
+ // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center |
+ // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps |
+ |
+ static const SkScalar kDomainInset = 0.5f; |
+ // Figure out the limits of pixels we're allowed to sample from. |
+ // Unless we know the amount of outset and the texture matrix we have to conservatively enforce |
+ // the domain. |
+ if (restrictFilterToRect) { |
+ domainRect->fLeft = constraintRect.fLeft + kDomainInset; |
+ domainRect->fTop = constraintRect.fTop + kDomainInset; |
+ domainRect->fRight = constraintRect.fRight - kDomainInset; |
+ domainRect->fBottom = constraintRect.fBottom - kDomainInset; |
+ } else if (textureContentArea) { |
+ // If we got here then: there is a textureContentArea, the coords are limited to the |
+ // constraint rect, and we're allowed to filter across the constraint rect boundary. So |
+ // we check whether the filter would reach across the edge of the content area. |
+ // We will only set the sides that are required. |
+ |
+ domainRect->setLargest(); |
+ if (coordsLimitedToConstraintRect) { |
+ // We may be able to use the fact that the texture coords are limited to the constraint |
+ // rect in order to avoid having to add a domain. |
+ bool needContentAreaConstraint = false; |
+ if (textureContentArea->fLeft > 0 && |
+ textureContentArea->fLeft + filterHalfWidth > constraintRect.fLeft) { |
+ domainRect->fLeft = textureContentArea->fLeft + kDomainInset; |
+ needContentAreaConstraint = true; |
+ } |
+ if (textureContentArea->fTop > 0 && |
+ textureContentArea->fTop + filterHalfWidth > constraintRect.fTop) { |
+ domainRect->fTop = textureContentArea->fTop + kDomainInset; |
+ needContentAreaConstraint = true; |
+ } |
+ if (textureContentArea->fRight < texW && |
+ textureContentArea->fRight - filterHalfWidth < constraintRect.fRight) { |
+ domainRect->fRight = textureContentArea->fRight - kDomainInset; |
+ needContentAreaConstraint = true; |
+ } |
+ if (textureContentArea->fBottom < texH && |
+ textureContentArea->fBottom - filterHalfWidth < constraintRect.fBottom) { |
+ domainRect->fBottom = textureContentArea->fBottom - kDomainInset; |
+ needContentAreaConstraint = true; |
+ } |
+ if (!needContentAreaConstraint) { |
+ return kNoDomain_DomainMode; |
+ } |
+ } else { |
+ // Our sample coords for the texture are allowed to be outside the constraintRect so we |
+ // don't consider it when computing the domain. |
+ if (textureContentArea->fLeft != 0) { |
+ domainRect->fLeft = textureContentArea->fLeft + kDomainInset; |
+ } |
+ if (textureContentArea->fTop != 0) { |
+ domainRect->fTop = textureContentArea->fTop + kDomainInset; |
+ } |
+ if (textureContentArea->fRight != texW) { |
+ domainRect->fRight = textureContentArea->fRight - kDomainInset; |
+ } |
+ if (textureContentArea->fBottom != texH) { |
+ domainRect->fBottom = textureContentArea->fBottom - kDomainInset; |
+ } |
+ } |
+ } else { |
+ return kNoDomain_DomainMode; |
+ } |
+ |
+ if (domainRect->fLeft > domainRect->fRight) { |
+ domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight); |
+ } |
+ if (domainRect->fTop > domainRect->fBottom) { |
+ domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom); |
+ } |
+ domainRect->fLeft /= texW; |
+ domainRect->fTop /= texH; |
+ domainRect->fRight /= texW; |
+ domainRect->fBottom /= texH; |
+ return kDomain_DomainMode; |
+} |
+ |
+const GrFragmentProcessor* GrTextureAdjuster::createFragmentProcessor( |
+ const SkMatrix& textureMatrix, |
+ const SkRect& constraintRect, |
+ FilterConstraint filterConstraint, |
+ bool coordsLimitedToConstraintRect, |
+ const GrTextureParams::FilterMode* filterOrNullForBicubic) { |
+ |
+ const SkIRect* contentArea = this->contentAreaOrNull(); |
+ |
+ SkRect domain; |
+ GrTexture* texture = this->originalTexture(); |
+ DomainMode domainMode = |
+ determine_domain_mode(constraintRect, filterConstraint, coordsLimitedToConstraintRect, |
+ texture->width(), texture->height(), |
+ contentArea, filterOrNullForBicubic, |
+ &domain); |
+ if (kTightCopy_DomainMode == domainMode) { |
+ // TODO: Copy the texture and adjust the texture matrix (both parts need to consider |
+ // non-int constraint rect) |
+ // For now: treat as bilerp and ignore what goes on above level 0. |
+ |
+ // We only expect MIP maps to require a tight copy. |
+ SkASSERT(filterOrNullForBicubic && |
+ GrTextureParams::kMipMap_FilterMode == *filterOrNullForBicubic); |
+ static const GrTextureParams::FilterMode kBilerp = GrTextureParams::kBilerp_FilterMode; |
+ domainMode = |
+ determine_domain_mode(constraintRect, filterConstraint, coordsLimitedToConstraintRect, |
+ texture->width(), texture->height(), |
+ contentArea, &kBilerp, &domain); |
+ SkASSERT(kTightCopy_DomainMode != domainMode); |
+ filterOrNullForBicubic = &kBilerp; |
+ } |
+ SkASSERT(kNoDomain_DomainMode == domainMode || |
+ (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom)); |
+ if (filterOrNullForBicubic) { |
+ if (kDomain_DomainMode == domainMode) { |
+ SkASSERT(*filterOrNullForBicubic != GrTextureParams::kMipMap_FilterMode); |
+ return GrTextureDomainEffect::Create(texture, textureMatrix, domain, |
+ GrTextureDomain::kClamp_Mode, |
+ *filterOrNullForBicubic); |
+ } else { |
+ GrTextureParams params(SkShader::kClamp_TileMode, *filterOrNullForBicubic); |
+ return GrSimpleTextureEffect::Create(texture, textureMatrix, params); |
+ } |
+ } else { |
+ if (kDomain_DomainMode == domainMode) { |
+ return GrBicubicEffect::Create(texture, textureMatrix, domain); |
+ } else { |
+ static const SkShader::TileMode kClampClamp[] = |
+ { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode }; |
+ return GrBicubicEffect::Create(texture, textureMatrix, kClampClamp); |
+ } |
+ } |
+} |
+ |
////////////////////////////////////////////////////////////////////////////// |
GrTexture* GrTextureMaker::refTextureForParams(GrContext* ctx, const GrTextureParams& params) { |