diff options
author | Brian Osman <brianosman@google.com> | 2016-11-28 10:06:27 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2016-11-28 15:44:40 +0000 |
commit | e8e54580c3c91fd485187af040af118aa5153261 (patch) | |
tree | 27d67ccb3500d300ba40e52bef56c05b095999df /src/gpu | |
parent | e9f78b41c65a78400c22a1b79007b1b9e187a745 (diff) |
GrTextureProducer cleanup, phase two: Producer, Adjuster, Maker
Previously: GrTextureProducer, GrTextureAdjuster, and GrTextureMaker
were all in GrTextureParamsAdjuster.h. Now they're each in their own
header.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=5202
Change-Id: I17fa9057b11511aa4d3e15569ea1c378cfec4c80
Reviewed-on: https://skia-review.googlesource.com/5202
Reviewed-by: Robert Phillips <robertphillips@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Diffstat (limited to 'src/gpu')
-rw-r--r-- | src/gpu/GrBitmapTextureMaker.h | 2 | ||||
-rw-r--r-- | src/gpu/GrGpu.h | 2 | ||||
-rw-r--r-- | src/gpu/GrImageTextureMaker.h | 2 | ||||
-rw-r--r-- | src/gpu/GrTextureAdjuster.cpp | 167 | ||||
-rw-r--r-- | src/gpu/GrTextureAdjuster.h | 66 | ||||
-rw-r--r-- | src/gpu/GrTextureMaker.cpp | 107 | ||||
-rw-r--r-- | src/gpu/GrTextureMaker.h | 74 | ||||
-rw-r--r-- | src/gpu/GrTextureParamsAdjuster.cpp | 524 | ||||
-rw-r--r-- | src/gpu/GrTextureParamsAdjuster.h | 236 | ||||
-rw-r--r-- | src/gpu/GrTextureProducer.cpp | 252 | ||||
-rw-r--r-- | src/gpu/GrTextureProducer.h | 146 | ||||
-rw-r--r-- | src/gpu/GrTextureProvider.cpp | 1 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 1 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice_drawTexture.cpp | 4 | ||||
-rw-r--r-- | src/gpu/SkGr.cpp | 1 |
15 files changed, 819 insertions, 766 deletions
diff --git a/src/gpu/GrBitmapTextureMaker.h b/src/gpu/GrBitmapTextureMaker.h index f66f20ce56..33145439c4 100644 --- a/src/gpu/GrBitmapTextureMaker.h +++ b/src/gpu/GrBitmapTextureMaker.h @@ -8,7 +8,7 @@ #ifndef GrBitmapTextureMaker_DEFINED #define GrBitmapTextureMaker_DEFINED -#include "GrTextureParamsAdjuster.h" +#include "GrTextureMaker.h" /** This class manages the conversion of SW-backed bitmaps to GrTextures. If the input bitmap is non-volatile the texture is cached using a key created from the pixels' image id and the diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index f88423be66..80beffafd7 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -12,7 +12,7 @@ #include "GrProgramDesc.h" #include "GrSwizzle.h" #include "GrAllocator.h" -#include "GrTextureParamsAdjuster.h" +#include "GrTextureProducer.h" #include "GrTypes.h" #include "GrXferProcessor.h" #include "SkPath.h" diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h index b156fabf7a..e2f3a5f956 100644 --- a/src/gpu/GrImageTextureMaker.h +++ b/src/gpu/GrImageTextureMaker.h @@ -8,7 +8,7 @@ #ifndef GrImageTextureMaker_DEFINED #define GrImageTextureMaker_DEFINED -#include "GrTextureParamsAdjuster.h" +#include "GrTextureMaker.h" #include "SkImage.h" class SkImageCacherator; diff --git a/src/gpu/GrTextureAdjuster.cpp b/src/gpu/GrTextureAdjuster.cpp new file mode 100644 index 0000000000..24d050a1ed --- /dev/null +++ b/src/gpu/GrTextureAdjuster.cpp @@ -0,0 +1,167 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTextureAdjuster.h" + +#include "GrContext.h" +#include "GrGpu.h" +#include "GrGpuResourcePriv.h" +#include "GrTexture.h" +#include "SkGrPriv.h" + +GrTextureAdjuster::GrTextureAdjuster(GrTexture* original, SkAlphaType alphaType, + const SkIRect& contentArea, uint32_t uniqueID, + SkColorSpace* cs) + : INHERITED(contentArea.width(), contentArea.height(), + GrPixelConfigIsAlphaOnly(original->config())) + , fOriginal(original) + , fAlphaType(alphaType) + , fColorSpace(cs) + , fUniqueID(uniqueID) { + SkASSERT(SkIRect::MakeWH(original->width(), original->height()).contains(contentArea)); + if (contentArea.fLeft > 0 || contentArea.fTop > 0 || + contentArea.fRight < original->width() || contentArea.fBottom < original->height()) { + fContentArea.set(contentArea); + } +} + +void GrTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey, + SkDestinationSurfaceColorMode) { + // Color mode is irrelevant in this case - we already have a texture so we're just sub-setting + GrUniqueKey baseKey; + GrMakeKeyFromImageID(&baseKey, fUniqueID, SkIRect::MakeWH(this->width(), this->height())); + MakeCopyKeyFromOrigKey(baseKey, params, copyKey); +} + +void GrTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey) { + // We don't currently have a mechanism for notifications on Images! +} + +GrTexture* GrTextureAdjuster::refCopy(const CopyParams& copyParams) { + GrTexture* texture = this->originalTexture(); + GrContext* context = texture->getContext(); + const SkIRect* contentArea = this->contentAreaOrNull(); + GrUniqueKey key; + this->makeCopyKey(copyParams, &key, SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware); + if (key.isValid()) { + GrTexture* cachedCopy = context->textureProvider()->findAndRefTextureByUniqueKey(key); + if (cachedCopy) { + return cachedCopy; + } + } + GrTexture* copy = CopyOnGpu(texture, contentArea, copyParams); + if (copy) { + if (key.isValid()) { + copy->resourcePriv().setUniqueKey(key); + this->didCacheCopy(key); + } + } + return copy; +} + +GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrSamplerParams& params, + SkDestinationSurfaceColorMode colorMode, + SkIPoint* outOffset) { + GrTexture* texture = this->originalTexture(); + GrContext* context = texture->getContext(); + CopyParams copyParams; + const SkIRect* contentArea = this->contentAreaOrNull(); + + if (!context) { + // The texture was abandoned. + return nullptr; + } + + if (contentArea && GrSamplerParams::kMipMap_FilterMode == params.filterMode()) { + // If we generate a MIP chain for texture it will read pixel values from outside the content + // area. + copyParams.fWidth = contentArea->width(); + copyParams.fHeight = contentArea->height(); + copyParams.fFilter = GrSamplerParams::kBilerp_FilterMode; + } else if (!context->getGpu()->makeCopyForTextureParams(texture, params, ©Params)) { + if (outOffset) { + if (contentArea) { + outOffset->set(contentArea->fLeft, contentArea->fRight); + } else { + outOffset->set(0, 0); + } + } + return SkRef(texture); + } + + GrTexture* copy = this->refCopy(copyParams); + if (copy && outOffset) { + outOffset->set(0, 0); + } + return copy; +} + +sk_sp<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor( + const SkMatrix& origTextureMatrix, + const SkRect& origConstraintRect, + FilterConstraint filterConstraint, + bool coordsLimitedToConstraintRect, + const GrSamplerParams::FilterMode* filterOrNullForBicubic, + SkColorSpace* dstColorSpace, + SkDestinationSurfaceColorMode colorMode) { + + SkMatrix textureMatrix = origTextureMatrix; + const SkIRect* contentArea = this->contentAreaOrNull(); + // Convert the constraintRect to be relative to the texture rather than the content area so + // that both rects are in the same coordinate system. + SkTCopyOnFirstWrite<SkRect> constraintRect(origConstraintRect); + if (contentArea) { + SkScalar l = SkIntToScalar(contentArea->fLeft); + SkScalar t = SkIntToScalar(contentArea->fTop); + constraintRect.writable()->offset(l, t); + textureMatrix.postTranslate(l, t); + } + + SkRect domain; + GrSamplerParams params; + if (filterOrNullForBicubic) { + params.setFilterMode(*filterOrNullForBicubic); + } + sk_sp<GrTexture> texture(this->refTextureSafeForParams(params, colorMode, nullptr)); + if (!texture) { + return nullptr; + } + // If we made a copy then we only copied the contentArea, in which case the new texture is all + // content. + if (texture.get() != this->originalTexture()) { + contentArea = nullptr; + } + + DomainMode domainMode = + DetermineDomainMode(*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 && + GrSamplerParams::kMipMap_FilterMode == *filterOrNullForBicubic); + static const GrSamplerParams::FilterMode kBilerp = GrSamplerParams::kBilerp_FilterMode; + domainMode = + DetermineDomainMode(*constraintRect, filterConstraint, coordsLimitedToConstraintRect, + texture->width(), texture->height(), + contentArea, &kBilerp, &domain); + SkASSERT(kTightCopy_DomainMode != domainMode); + } + SkASSERT(kNoDomain_DomainMode == domainMode || + (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom)); + textureMatrix.postIDiv(texture->width(), texture->height()); + sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace, + dstColorSpace); + return CreateFragmentProcessorForDomainAndFilter(texture.get(), std::move(colorSpaceXform), + textureMatrix, domainMode, domain, + filterOrNullForBicubic); +} diff --git a/src/gpu/GrTextureAdjuster.h b/src/gpu/GrTextureAdjuster.h new file mode 100644 index 0000000000..0e3fe52df9 --- /dev/null +++ b/src/gpu/GrTextureAdjuster.h @@ -0,0 +1,66 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureAdjuster_DEFINED +#define GrTextureAdjuster_DEFINED + +#include "GrTextureProducer.h" +#include "SkTLazy.h" + +/** + * Base class for sources that start out as textures. Optionally allows for a content area subrect. + * The intent is not to use content area for subrect rendering. Rather, the pixels outside the + * content area have undefined values and shouldn't be read *regardless* of filtering mode or + * the SkCanvas::SrcRectConstraint used for subrect draws. + */ +class GrTextureAdjuster : public GrTextureProducer { +public: + /** Makes the subset of the texture safe to use with the given texture parameters. + outOffset will be the top-left corner of the subset if a copy is not made. Otherwise, + the copy will be tight to the contents and outOffset will be (0, 0). If the copy's size + does not match subset's dimensions then the contents are scaled to fit the copy.*/ + GrTexture* refTextureSafeForParams(const GrSamplerParams&, SkDestinationSurfaceColorMode, + SkIPoint* outOffset); + + sk_sp<GrFragmentProcessor> createFragmentProcessor( + const SkMatrix& textureMatrix, + const SkRect& constraintRect, + FilterConstraint, + bool coordsLimitedToConstraintRect, + const GrSamplerParams::FilterMode* filterOrNullForBicubic, + SkColorSpace* dstColorSpace, + SkDestinationSurfaceColorMode) override; + + // We do not ref the texture nor the colorspace, so the caller must keep them in scope while + // this Adjuster is alive. + GrTextureAdjuster(GrTexture*, SkAlphaType, const SkIRect& area, uint32_t uniqueID, + SkColorSpace*); + +protected: + SkAlphaType alphaType() const override { return fAlphaType; } + void makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey, + SkDestinationSurfaceColorMode colorMode) override; + void didCacheCopy(const GrUniqueKey& copyKey) override; + + GrTexture* originalTexture() const { return fOriginal; } + + /** Returns the content area or null for the whole original texture */ + const SkIRect* contentAreaOrNull() { return fContentArea.getMaybeNull(); } + +private: + SkTLazy<SkIRect> fContentArea; + GrTexture* fOriginal; + SkAlphaType fAlphaType; + SkColorSpace* fColorSpace; + uint32_t fUniqueID; + + GrTexture* refCopy(const CopyParams ©Params); + + typedef GrTextureProducer INHERITED; +}; + +#endif diff --git a/src/gpu/GrTextureMaker.cpp b/src/gpu/GrTextureMaker.cpp new file mode 100644 index 0000000000..d303dcf895 --- /dev/null +++ b/src/gpu/GrTextureMaker.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTextureMaker.h" + +#include "GrContext.h" +#include "GrGpu.h" + +GrTexture* GrTextureMaker::refTextureForParams(const GrSamplerParams& params, + SkDestinationSurfaceColorMode colorMode, + sk_sp<SkColorSpace>* texColorSpace) { + CopyParams copyParams; + bool willBeMipped = params.filterMode() == GrSamplerParams::kMipMap_FilterMode; + + if (!fContext->caps()->mipMapSupport()) { + willBeMipped = false; + } + + if (texColorSpace) { + *texColorSpace = this->getColorSpace(colorMode); + } + + if (!fContext->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params, + ©Params)) { + return this->refOriginalTexture(willBeMipped, colorMode); + } + GrUniqueKey copyKey; + this->makeCopyKey(copyParams, ©Key, colorMode); + if (copyKey.isValid()) { + GrTexture* result = fContext->textureProvider()->findAndRefTextureByUniqueKey(copyKey); + if (result) { + return result; + } + } + + GrTexture* result = this->generateTextureForParams(copyParams, willBeMipped, colorMode); + if (!result) { + return nullptr; + } + + if (copyKey.isValid()) { + fContext->textureProvider()->assignUniqueKeyToTexture(copyKey, result); + this->didCacheCopy(copyKey); + } + return result; +} + +sk_sp<GrFragmentProcessor> GrTextureMaker::createFragmentProcessor( + const SkMatrix& textureMatrix, + const SkRect& constraintRect, + FilterConstraint filterConstraint, + bool coordsLimitedToConstraintRect, + const GrSamplerParams::FilterMode* filterOrNullForBicubic, + SkColorSpace* dstColorSpace, + SkDestinationSurfaceColorMode colorMode) { + + const GrSamplerParams::FilterMode* fmForDetermineDomain = filterOrNullForBicubic; + if (filterOrNullForBicubic && GrSamplerParams::kMipMap_FilterMode == *filterOrNullForBicubic && + kYes_FilterConstraint == filterConstraint) { + // TODo: Here we should force a copy restricted to the constraintRect since MIP maps will + // read outside the constraint rect. However, as in the adjuster case, we aren't currently + // doing that. + // We instead we compute the domain as though were bilerping which is only correct if we + // only sample level 0. + static const GrSamplerParams::FilterMode kBilerp = GrSamplerParams::kBilerp_FilterMode; + fmForDetermineDomain = &kBilerp; + } + + GrSamplerParams params; + if (filterOrNullForBicubic) { + params.reset(SkShader::kClamp_TileMode, *filterOrNullForBicubic); + } else { + // Bicubic doesn't use filtering for it's texture accesses. + params.reset(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode); + } + sk_sp<SkColorSpace> texColorSpace; + sk_sp<GrTexture> texture(this->refTextureForParams(params, colorMode, &texColorSpace)); + if (!texture) { + return nullptr; + } + SkRect domain; + DomainMode domainMode = + DetermineDomainMode(constraintRect, filterConstraint, coordsLimitedToConstraintRect, + texture->width(), texture->height(), nullptr, fmForDetermineDomain, + &domain); + SkASSERT(kTightCopy_DomainMode != domainMode); + SkMatrix normalizedTextureMatrix = textureMatrix; + normalizedTextureMatrix.postIDiv(texture->width(), texture->height()); + sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(), + dstColorSpace); + return CreateFragmentProcessorForDomainAndFilter(texture.get(), std::move(colorSpaceXform), + normalizedTextureMatrix, domainMode, domain, + filterOrNullForBicubic); +} + +GrTexture* GrTextureMaker::generateTextureForParams(const CopyParams& copyParams, bool willBeMipped, + SkDestinationSurfaceColorMode colorMode) { + sk_sp<GrTexture> original(this->refOriginalTexture(willBeMipped, colorMode)); + if (!original) { + return nullptr; + } + return CopyOnGpu(original.get(), nullptr, copyParams); +} diff --git a/src/gpu/GrTextureMaker.h b/src/gpu/GrTextureMaker.h new file mode 100644 index 0000000000..8c00e2161e --- /dev/null +++ b/src/gpu/GrTextureMaker.h @@ -0,0 +1,74 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureMaker_DEFINED +#define GrTextureMaker_DEFINED + +#include "GrTextureProducer.h" + +/** + * Base class for sources that start out as something other than a texture (encoded image, + * picture, ...). + */ +class GrTextureMaker : public GrTextureProducer { +public: + /** + * Returns a texture that is safe for use with the params. If the size of the returned texture + * does not match width()/height() then the contents of the original must be scaled to fit + * the texture. Places the color space of the texture in (*texColorSpace). + */ + GrTexture* refTextureForParams(const GrSamplerParams&, SkDestinationSurfaceColorMode, + sk_sp<SkColorSpace>* texColorSpace); + + sk_sp<GrFragmentProcessor> createFragmentProcessor( + const SkMatrix& textureMatrix, + const SkRect& constraintRect, + FilterConstraint filterConstraint, + bool coordsLimitedToConstraintRect, + const GrSamplerParams::FilterMode* filterOrNullForBicubic, + SkColorSpace* dstColorSpace, + SkDestinationSurfaceColorMode) override; + +protected: + GrTextureMaker(GrContext* context, int width, int height, bool isAlphaOnly) + : INHERITED(width, height, isAlphaOnly) + , fContext(context) {} + + /** + * Return the maker's "original" texture. It is the responsibility of the maker to handle any + * caching of the original if desired. + */ + virtual GrTexture* refOriginalTexture(bool willBeMipped, SkDestinationSurfaceColorMode) = 0; + + /** + * Returns the color space of the maker's "original" texture, assuming it was retrieved with + * the same destination color mode. + */ + virtual sk_sp<SkColorSpace> getColorSpace(SkDestinationSurfaceColorMode) = 0; + + /** + * Return a new (uncached) texture that is the stretch of the maker's original. + * + * The base-class handles general logic for this, and only needs access to the following + * method: + * - refOriginalTexture() + * + * Subclass may override this if they can handle creating the texture more directly than + * by copying. + */ + virtual GrTexture* generateTextureForParams(const CopyParams&, bool willBeMipped, + SkDestinationSurfaceColorMode); + + GrContext* context() const { return fContext; } + +private: + GrContext* fContext; + + typedef GrTextureProducer INHERITED; +}; + +#endif diff --git a/src/gpu/GrTextureParamsAdjuster.cpp b/src/gpu/GrTextureParamsAdjuster.cpp deleted file mode 100644 index d924f4a976..0000000000 --- a/src/gpu/GrTextureParamsAdjuster.cpp +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrTextureParamsAdjuster.h" - -#include "GrCaps.h" -#include "GrColorSpaceXform.h" -#include "GrContext.h" -#include "GrRenderTargetContext.h" -#include "GrGpu.h" -#include "GrGpuResourcePriv.h" -#include "GrResourceKey.h" -#include "GrTexture.h" -#include "GrSamplerParams.h" -#include "GrTextureProvider.h" -#include "SkCanvas.h" -#include "SkGr.h" -#include "SkGrPriv.h" -#include "effects/GrBicubicEffect.h" -#include "effects/GrSimpleTextureEffect.h" -#include "effects/GrTextureDomain.h" - -typedef GrTextureProducer::CopyParams CopyParams; - -////////////////////////////////////////////////////////////////////////////// - -static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset, - const CopyParams& copyParams) { - SkASSERT(!subset || !subset->isEmpty()); - GrContext* context = inputTexture->getContext(); - SkASSERT(context); - - GrPixelConfig config = GrMakePixelConfigUncompressed(inputTexture->config()); - - sk_sp<GrRenderTargetContext> copyRTC = context->makeRenderTargetContextWithFallback( - SkBackingFit::kExact, copyParams.fWidth, copyParams.fHeight, config, nullptr); - if (!copyRTC) { - return nullptr; - } - - GrPaint paint; - paint.setGammaCorrect(true); - - SkScalar sx SK_INIT_TO_AVOID_WARNING; - SkScalar sy SK_INIT_TO_AVOID_WARNING; - if (subset) { - sx = 1.f / inputTexture->width(); - sy = 1.f / inputTexture->height(); - } - - if (copyParams.fFilter != GrSamplerParams::kNone_FilterMode && subset && - (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) { - SkRect domain; - domain.fLeft = (subset->fLeft + 0.5f) * sx; - domain.fTop = (subset->fTop + 0.5f)* sy; - domain.fRight = (subset->fRight - 0.5f) * sx; - domain.fBottom = (subset->fBottom - 0.5f) * sy; - // This would cause us to read values from outside the subset. Surely, the caller knows - // better! - SkASSERT(copyParams.fFilter != GrSamplerParams::kMipMap_FilterMode); - paint.addColorFragmentProcessor( - GrTextureDomainEffect::Make(inputTexture, nullptr, SkMatrix::I(), domain, - GrTextureDomain::kClamp_Mode, - copyParams.fFilter)); - } else { - GrSamplerParams params(SkShader::kClamp_TileMode, copyParams.fFilter); - paint.addColorTextureProcessor(inputTexture, nullptr, SkMatrix::I(), params); - } - paint.setPorterDuffXPFactory(SkBlendMode::kSrc); - - SkRect localRect; - if (subset) { - localRect = SkRect::Make(*subset); - localRect.fLeft *= sx; - localRect.fTop *= sy; - localRect.fRight *= sx; - localRect.fBottom *= sy; - } else { - localRect = SkRect::MakeWH(1.f, 1.f); - } - - SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight); - copyRTC->fillRectToRect(GrNoClip(), paint, SkMatrix::I(), dstRect, localRect); - return copyRTC->asTexture().release(); -} - -GrTextureAdjuster::GrTextureAdjuster(GrTexture* original, SkAlphaType alphaType, - const SkIRect& contentArea, uint32_t uniqueID, - SkColorSpace* cs) - : INHERITED(contentArea.width(), contentArea.height(), - GrPixelConfigIsAlphaOnly(original->config())) - , fOriginal(original) - , fAlphaType(alphaType) - , fColorSpace(cs) - , fUniqueID(uniqueID) -{ - SkASSERT(SkIRect::MakeWH(original->width(), original->height()).contains(contentArea)); - if (contentArea.fLeft > 0 || contentArea.fTop > 0 || - contentArea.fRight < original->width() || contentArea.fBottom < original->height()) { - fContentArea.set(contentArea); - } -} - -void GrTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey, - SkDestinationSurfaceColorMode) { - // Color mode is irrelevant in this case - we already have a texture so we're just sub-setting - GrUniqueKey baseKey; - GrMakeKeyFromImageID(&baseKey, fUniqueID, SkIRect::MakeWH(this->width(), this->height())); - MakeCopyKeyFromOrigKey(baseKey, params, copyKey); -} - -void GrTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey) { - // We don't currently have a mechanism for notifications on Images! -} - -GrTexture* GrTextureAdjuster::refCopy(const CopyParams& copyParams) { - GrTexture* texture = this->originalTexture(); - GrContext* context = texture->getContext(); - const SkIRect* contentArea = this->contentAreaOrNull(); - GrUniqueKey key; - this->makeCopyKey(copyParams, &key, SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware); - if (key.isValid()) { - GrTexture* cachedCopy = context->textureProvider()->findAndRefTextureByUniqueKey(key); - if (cachedCopy) { - return cachedCopy; - } - } - GrTexture* copy = copy_on_gpu(texture, contentArea, copyParams); - if (copy) { - if (key.isValid()) { - copy->resourcePriv().setUniqueKey(key); - this->didCacheCopy(key); - } - } - return copy; -} - -GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrSamplerParams& params, - SkDestinationSurfaceColorMode colorMode, - SkIPoint* outOffset) { - GrTexture* texture = this->originalTexture(); - GrContext* context = texture->getContext(); - CopyParams copyParams; - const SkIRect* contentArea = this->contentAreaOrNull(); - - if (!context) { - // The texture was abandoned. - return nullptr; - } - - if (contentArea && GrSamplerParams::kMipMap_FilterMode == params.filterMode()) { - // If we generate a MIP chain for texture it will read pixel values from outside the content - // area. - copyParams.fWidth = contentArea->width(); - copyParams.fHeight = contentArea->height(); - copyParams.fFilter = GrSamplerParams::kBilerp_FilterMode; - } else if (!context->getGpu()->makeCopyForTextureParams(texture, params, ©Params)) { - if (outOffset) { - if (contentArea) { - outOffset->set(contentArea->fLeft, contentArea->fRight); - } else { - outOffset->set(0, 0); - } - } - return SkRef(texture); - } - - GrTexture* copy = this->refCopy(copyParams); - if (copy && outOffset) { - outOffset->set(0, 0); - } - return copy; -} - -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 GrSamplerParams::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 == GrTextureProducer::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 GrSamplerParams::kNone_FilterMode: - if (coordsLimitedToConstraintRect) { - return kNoDomain_DomainMode; - } else { - filterHalfWidth = 0.f; - } - break; - case GrSamplerParams::kBilerp_FilterMode: - filterHalfWidth = .5f; - break; - case GrSamplerParams::kMipMap_FilterMode: - if (restrictFilterToRect || textureContentArea) { - // No domain can save us here. - return kTightCopy_DomainMode; - } - return kNoDomain_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; -} - -static sk_sp<GrFragmentProcessor> create_fp_for_domain_and_filter( - GrTexture* texture, - sk_sp<GrColorSpaceXform> colorSpaceXform, - const SkMatrix& textureMatrix, - DomainMode domainMode, - const SkRect& domain, - const GrSamplerParams::FilterMode* filterOrNullForBicubic) { - SkASSERT(kTightCopy_DomainMode != domainMode); - if (filterOrNullForBicubic) { - if (kDomain_DomainMode == domainMode) { - return GrTextureDomainEffect::Make(texture, std::move(colorSpaceXform), textureMatrix, - domain, GrTextureDomain::kClamp_Mode, - *filterOrNullForBicubic); - } else { - GrSamplerParams params(SkShader::kClamp_TileMode, *filterOrNullForBicubic); - return GrSimpleTextureEffect::Make(texture, std::move(colorSpaceXform), textureMatrix, - params); - } - } else { - if (kDomain_DomainMode == domainMode) { - return GrBicubicEffect::Make(texture, std::move(colorSpaceXform), textureMatrix, - domain); - } else { - static const SkShader::TileMode kClampClamp[] = - { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode }; - return GrBicubicEffect::Make(texture, std::move(colorSpaceXform), textureMatrix, - kClampClamp); - } - } -} - -sk_sp<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor( - const SkMatrix& origTextureMatrix, - const SkRect& origConstraintRect, - FilterConstraint filterConstraint, - bool coordsLimitedToConstraintRect, - const GrSamplerParams::FilterMode* filterOrNullForBicubic, - SkColorSpace* dstColorSpace, - SkDestinationSurfaceColorMode colorMode) { - - SkMatrix textureMatrix = origTextureMatrix; - const SkIRect* contentArea = this->contentAreaOrNull(); - // Convert the constraintRect to be relative to the texture rather than the content area so - // that both rects are in the same coordinate system. - SkTCopyOnFirstWrite<SkRect> constraintRect(origConstraintRect); - if (contentArea) { - SkScalar l = SkIntToScalar(contentArea->fLeft); - SkScalar t = SkIntToScalar(contentArea->fTop); - constraintRect.writable()->offset(l, t); - textureMatrix.postTranslate(l, t); - } - - SkRect domain; - GrSamplerParams params; - if (filterOrNullForBicubic) { - params.setFilterMode(*filterOrNullForBicubic); - } - sk_sp<GrTexture> texture(this->refTextureSafeForParams(params, colorMode, nullptr)); - if (!texture) { - return nullptr; - } - // If we made a copy then we only copied the contentArea, in which case the new texture is all - // content. - if (texture.get() != this->originalTexture()) { - contentArea = nullptr; - } - - 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 && - GrSamplerParams::kMipMap_FilterMode == *filterOrNullForBicubic); - static const GrSamplerParams::FilterMode kBilerp = GrSamplerParams::kBilerp_FilterMode; - domainMode = - determine_domain_mode(*constraintRect, filterConstraint, coordsLimitedToConstraintRect, - texture->width(), texture->height(), - contentArea, &kBilerp, &domain); - SkASSERT(kTightCopy_DomainMode != domainMode); - } - SkASSERT(kNoDomain_DomainMode == domainMode || - (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom)); - textureMatrix.postIDiv(texture->width(), texture->height()); - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace, - dstColorSpace); - return create_fp_for_domain_and_filter(texture.get(), std::move(colorSpaceXform), textureMatrix, - domainMode, domain, filterOrNullForBicubic); -} - -////////////////////////////////////////////////////////////////////////////// - -GrTexture* GrTextureMaker::refTextureForParams(const GrSamplerParams& params, - SkDestinationSurfaceColorMode colorMode, - sk_sp<SkColorSpace>* texColorSpace) { - CopyParams copyParams; - bool willBeMipped = params.filterMode() == GrSamplerParams::kMipMap_FilterMode; - - if (!fContext->caps()->mipMapSupport()) { - willBeMipped = false; - } - - if (texColorSpace) { - *texColorSpace = this->getColorSpace(colorMode); - } - - if (!fContext->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params, - ©Params)) { - return this->refOriginalTexture(willBeMipped, colorMode); - } - GrUniqueKey copyKey; - this->makeCopyKey(copyParams, ©Key, colorMode); - if (copyKey.isValid()) { - GrTexture* result = fContext->textureProvider()->findAndRefTextureByUniqueKey(copyKey); - if (result) { - return result; - } - } - - GrTexture* result = this->generateTextureForParams(copyParams, willBeMipped, colorMode); - if (!result) { - return nullptr; - } - - if (copyKey.isValid()) { - fContext->textureProvider()->assignUniqueKeyToTexture(copyKey, result); - this->didCacheCopy(copyKey); - } - return result; -} - -sk_sp<GrFragmentProcessor> GrTextureMaker::createFragmentProcessor( - const SkMatrix& textureMatrix, - const SkRect& constraintRect, - FilterConstraint filterConstraint, - bool coordsLimitedToConstraintRect, - const GrSamplerParams::FilterMode* filterOrNullForBicubic, - SkColorSpace* dstColorSpace, - SkDestinationSurfaceColorMode colorMode) { - - const GrSamplerParams::FilterMode* fmForDetermineDomain = filterOrNullForBicubic; - if (filterOrNullForBicubic && GrSamplerParams::kMipMap_FilterMode == *filterOrNullForBicubic && - kYes_FilterConstraint == filterConstraint) { - // TODo: Here we should force a copy restricted to the constraintRect since MIP maps will - // read outside the constraint rect. However, as in the adjuster case, we aren't currently - // doing that. - // We instead we compute the domain as though were bilerping which is only correct if we - // only sample level 0. - static const GrSamplerParams::FilterMode kBilerp = GrSamplerParams::kBilerp_FilterMode; - fmForDetermineDomain = &kBilerp; - } - - GrSamplerParams params; - if (filterOrNullForBicubic) { - params.reset(SkShader::kClamp_TileMode, *filterOrNullForBicubic); - } else { - // Bicubic doesn't use filtering for it's texture accesses. - params.reset(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode); - } - sk_sp<SkColorSpace> texColorSpace; - sk_sp<GrTexture> texture(this->refTextureForParams(params, colorMode, &texColorSpace)); - if (!texture) { - return nullptr; - } - SkRect domain; - DomainMode domainMode = - determine_domain_mode(constraintRect, filterConstraint, coordsLimitedToConstraintRect, - texture->width(), texture->height(), nullptr, fmForDetermineDomain, - &domain); - SkASSERT(kTightCopy_DomainMode != domainMode); - SkMatrix normalizedTextureMatrix = textureMatrix; - normalizedTextureMatrix.postIDiv(texture->width(), texture->height()); - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(), - dstColorSpace); - return create_fp_for_domain_and_filter(texture.get(), std::move(colorSpaceXform), - normalizedTextureMatrix, domainMode, domain, - filterOrNullForBicubic); -} - -GrTexture* GrTextureMaker::generateTextureForParams(const CopyParams& copyParams, bool willBeMipped, - SkDestinationSurfaceColorMode colorMode) { - sk_sp<GrTexture> original(this->refOriginalTexture(willBeMipped, colorMode)); - if (!original) { - return nullptr; - } - return copy_on_gpu(original.get(), nullptr, copyParams); -} diff --git a/src/gpu/GrTextureParamsAdjuster.h b/src/gpu/GrTextureParamsAdjuster.h deleted file mode 100644 index c3f020196c..0000000000 --- a/src/gpu/GrTextureParamsAdjuster.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef GrTextureMaker_DEFINED -#define GrTextureMaker_DEFINED - -#include "GrSamplerParams.h" -#include "GrResourceKey.h" -#include "GrTexture.h" -#include "SkTLazy.h" - -class GrContext; -class GrSamplerParams; -class GrUniqueKey; -class SkBitmap; - -/** - * Different GPUs and API extensions have different requirements with respect to what texture - * sampling parameters may be used with textures of various types. This class facilitates making - * texture compatible with a given GrSamplerParams. There are two immediate subclasses defined - * below. One is a base class for sources that are inherently texture-backed (e.g. a texture-backed - * SkImage). It supports subsetting the original texture. The other is for use cases where the - * source can generate a texture that represents some content (e.g. cpu pixels, SkPicture, ...). - */ -class GrTextureProducer : public SkNoncopyable { -public: - struct CopyParams { - GrSamplerParams::FilterMode fFilter; - int fWidth; - int fHeight; - }; - - enum FilterConstraint { - kYes_FilterConstraint, - kNo_FilterConstraint, - }; - - /** - * Helper for creating a fragment processor to sample the texture with a given filtering mode. - * It attempts to avoid making texture copies or using domains whenever possible. - * - * @param textureMatrix Matrix used to access the texture. It is applied to - * the local coords. The post-transformed coords should - * be in texel units (rather than normalized) with - * respect to this Producer's bounds (width()/height()). - * @param constraintRect A rect that represents the area of the texture to be - * sampled. It must be contained in the Producer's bounds - * as defined by width()/height(). - * @param filterConstriant Indicates whether filtering is limited to - * constraintRect. - * @param coordsLimitedToConstraintRect Is it known that textureMatrix*localCoords is bound - * by the portion of the texture indicated by - * constraintRect (without consideration of filter - * width, just the raw coords). - * @param filterOrNullForBicubic If non-null indicates the filter mode. If null means - * use bicubic filtering. - **/ - virtual sk_sp<GrFragmentProcessor> createFragmentProcessor( - const SkMatrix& textureMatrix, - const SkRect& constraintRect, - FilterConstraint filterConstraint, - bool coordsLimitedToConstraintRect, - const GrSamplerParams::FilterMode* filterOrNullForBicubic, - SkColorSpace* dstColorSpace, - SkDestinationSurfaceColorMode) = 0; - - virtual ~GrTextureProducer() {} - - int width() const { return fWidth; } - int height() const { return fHeight; } - bool isAlphaOnly() const { return fIsAlphaOnly; } - virtual SkAlphaType alphaType() const = 0; - -protected: - GrTextureProducer(int width, int height, bool isAlphaOnly) - : fWidth(width) - , fHeight(height) - , fIsAlphaOnly(isAlphaOnly) {} - - /** Helper for creating a key for a copy from an original key. */ - static void MakeCopyKeyFromOrigKey(const GrUniqueKey& origKey, - const CopyParams& copyParams, - GrUniqueKey* copyKey) { - SkASSERT(!copyKey->isValid()); - if (origKey.isValid()) { - static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); - GrUniqueKey::Builder builder(copyKey, origKey, kDomain, 3); - builder[0] = copyParams.fFilter; - builder[1] = copyParams.fWidth; - builder[2] = copyParams.fHeight; - } - } - - /** - * If we need to make a copy in order to be compatible with GrTextureParams producer is asked to - * return a key that identifies its original content + the CopyParms parameter. If the producer - * does not want to cache the stretched version (e.g. the producer is volatile), this should - * simply return without initializing the copyKey. If the texture generated by this producer - * depends on colorMode, then that information should also be incorporated in the key. - */ - virtual void makeCopyKey(const CopyParams&, GrUniqueKey* copyKey, - SkDestinationSurfaceColorMode colorMode) = 0; - - /** - * If a stretched version of the texture is generated, it may be cached (assuming that - * makeCopyKey() returns true). In that case, the maker is notified in case it - * wants to note that for when the maker is destroyed. - */ - virtual void didCacheCopy(const GrUniqueKey& copyKey) = 0; - -private: - const int fWidth; - const int fHeight; - const bool fIsAlphaOnly; - - typedef SkNoncopyable INHERITED; -}; - -/** - * Base class for sources that start out as textures. Optionally allows for a content area subrect. - * The intent is not to use content area for subrect rendering. Rather, the pixels outside the - * content area have undefined values and shouldn't be read *regardless* of filtering mode or - * the SkCanvas::SrcRectConstraint used for subrect draws. - */ -class GrTextureAdjuster : public GrTextureProducer { -public: - /** Makes the subset of the texture safe to use with the given texture parameters. - outOffset will be the top-left corner of the subset if a copy is not made. Otherwise, - the copy will be tight to the contents and outOffset will be (0, 0). If the copy's size - does not match subset's dimensions then the contents are scaled to fit the copy.*/ - GrTexture* refTextureSafeForParams(const GrSamplerParams&, SkDestinationSurfaceColorMode, - SkIPoint* outOffset); - - sk_sp<GrFragmentProcessor> createFragmentProcessor( - const SkMatrix& textureMatrix, - const SkRect& constraintRect, - FilterConstraint, - bool coordsLimitedToConstraintRect, - const GrSamplerParams::FilterMode* filterOrNullForBicubic, - SkColorSpace* dstColorSpace, - SkDestinationSurfaceColorMode) override; - - // We do not ref the texture nor the colorspace, so the caller must keep them in scope while - // this Adjuster is alive. - GrTextureAdjuster(GrTexture*, SkAlphaType, const SkIRect& area, uint32_t uniqueID, - SkColorSpace*); - -protected: - SkAlphaType alphaType() const override { return fAlphaType; } - void makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey, - SkDestinationSurfaceColorMode colorMode) override; - void didCacheCopy(const GrUniqueKey& copyKey) override; - - GrTexture* originalTexture() const { return fOriginal; } - - /** Returns the content area or null for the whole original texture */ - const SkIRect* contentAreaOrNull() { return fContentArea.getMaybeNull(); } - -private: - SkTLazy<SkIRect> fContentArea; - GrTexture* fOriginal; - SkAlphaType fAlphaType; - SkColorSpace* fColorSpace; - uint32_t fUniqueID; - - GrTexture* refCopy(const CopyParams ©Params); - - typedef GrTextureProducer INHERITED; -}; - -/** - * Base class for sources that start out as something other than a texture (encoded image, - * picture, ...). - */ -class GrTextureMaker : public GrTextureProducer { -public: - /** - * Returns a texture that is safe for use with the params. If the size of the returned texture - * does not match width()/height() then the contents of the original must be scaled to fit - * the texture. Places the color space of the texture in (*texColorSpace). - */ - GrTexture* refTextureForParams(const GrSamplerParams&, SkDestinationSurfaceColorMode, - sk_sp<SkColorSpace>* texColorSpace); - - sk_sp<GrFragmentProcessor> createFragmentProcessor( - const SkMatrix& textureMatrix, - const SkRect& constraintRect, - FilterConstraint filterConstraint, - bool coordsLimitedToConstraintRect, - const GrSamplerParams::FilterMode* filterOrNullForBicubic, - SkColorSpace* dstColorSpace, - SkDestinationSurfaceColorMode) override; - -protected: - GrTextureMaker(GrContext* context, int width, int height, bool isAlphaOnly) - : INHERITED(width, height, isAlphaOnly) - , fContext(context) {} - - /** - * Return the maker's "original" texture. It is the responsibility of the maker to handle any - * caching of the original if desired. - */ - virtual GrTexture* refOriginalTexture(bool willBeMipped, SkDestinationSurfaceColorMode) = 0; - - /** - * Returns the color space of the maker's "original" texture, assuming it was retrieved with - * the same destination color mode. - */ - virtual sk_sp<SkColorSpace> getColorSpace(SkDestinationSurfaceColorMode) = 0; - - /** - * Return a new (uncached) texture that is the stretch of the maker's original. - * - * The base-class handles general logic for this, and only needs access to the following - * method: - * - refOriginalTexture() - * - * Subclass may override this if they can handle creating the texture more directly than - * by copying. - */ - virtual GrTexture* generateTextureForParams(const CopyParams&, bool willBeMipped, - SkDestinationSurfaceColorMode); - - GrContext* context() const { return fContext; } - -private: - GrContext* fContext; - - typedef GrTextureProducer INHERITED; -}; - -#endif diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp new file mode 100644 index 0000000000..14e2c29bb8 --- /dev/null +++ b/src/gpu/GrTextureProducer.cpp @@ -0,0 +1,252 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTextureProducer.h" + +#include "GrRenderTargetContext.h" +#include "GrTexture.h" +#include "effects/GrBicubicEffect.h" +#include "effects/GrSimpleTextureEffect.h" +#include "effects/GrTextureDomain.h" + +GrTexture* GrTextureProducer::CopyOnGpu(GrTexture* inputTexture, const SkIRect* subset, + const CopyParams& copyParams) { + SkASSERT(!subset || !subset->isEmpty()); + GrContext* context = inputTexture->getContext(); + SkASSERT(context); + + GrPixelConfig config = GrMakePixelConfigUncompressed(inputTexture->config()); + + sk_sp<GrRenderTargetContext> copyRTC = context->makeRenderTargetContextWithFallback( + SkBackingFit::kExact, copyParams.fWidth, copyParams.fHeight, config, nullptr); + if (!copyRTC) { + return nullptr; + } + + GrPaint paint; + paint.setGammaCorrect(true); + + SkScalar sx SK_INIT_TO_AVOID_WARNING; + SkScalar sy SK_INIT_TO_AVOID_WARNING; + if (subset) { + sx = 1.f / inputTexture->width(); + sy = 1.f / inputTexture->height(); + } + + if (copyParams.fFilter != GrSamplerParams::kNone_FilterMode && subset && + (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) { + SkRect domain; + domain.fLeft = (subset->fLeft + 0.5f) * sx; + domain.fTop = (subset->fTop + 0.5f)* sy; + domain.fRight = (subset->fRight - 0.5f) * sx; + domain.fBottom = (subset->fBottom - 0.5f) * sy; + // This would cause us to read values from outside the subset. Surely, the caller knows + // better! + SkASSERT(copyParams.fFilter != GrSamplerParams::kMipMap_FilterMode); + paint.addColorFragmentProcessor( + GrTextureDomainEffect::Make(inputTexture, nullptr, SkMatrix::I(), domain, + GrTextureDomain::kClamp_Mode, + copyParams.fFilter)); + } else { + GrSamplerParams params(SkShader::kClamp_TileMode, copyParams.fFilter); + paint.addColorTextureProcessor(inputTexture, nullptr, SkMatrix::I(), params); + } + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + + SkRect localRect; + if (subset) { + localRect = SkRect::Make(*subset); + localRect.fLeft *= sx; + localRect.fTop *= sy; + localRect.fRight *= sx; + localRect.fBottom *= sy; + } else { + localRect = SkRect::MakeWH(1.f, 1.f); + } + + SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight); + copyRTC->fillRectToRect(GrNoClip(), paint, SkMatrix::I(), dstRect, localRect); + return copyRTC->asTexture().release(); +} + +/** 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. + */ +GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode( + const SkRect& constraintRect, + FilterConstraint filterConstraint, + bool coordsLimitedToConstraintRect, + int texW, int texH, + const SkIRect* textureContentArea, + const GrSamplerParams::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 == GrTextureProducer::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 GrSamplerParams::kNone_FilterMode: + if (coordsLimitedToConstraintRect) { + return kNoDomain_DomainMode; + } else { + filterHalfWidth = 0.f; + } + break; + case GrSamplerParams::kBilerp_FilterMode: + filterHalfWidth = .5f; + break; + case GrSamplerParams::kMipMap_FilterMode: + if (restrictFilterToRect || textureContentArea) { + // No domain can save us here. + return kTightCopy_DomainMode; + } + return kNoDomain_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; +} + +sk_sp<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter( + GrTexture* texture, + sk_sp<GrColorSpaceXform> colorSpaceXform, + const SkMatrix& textureMatrix, + DomainMode domainMode, + const SkRect& domain, + const GrSamplerParams::FilterMode* filterOrNullForBicubic) { + SkASSERT(kTightCopy_DomainMode != domainMode); + if (filterOrNullForBicubic) { + if (kDomain_DomainMode == domainMode) { + return GrTextureDomainEffect::Make(texture, std::move(colorSpaceXform), textureMatrix, + domain, GrTextureDomain::kClamp_Mode, + *filterOrNullForBicubic); + } else { + GrSamplerParams params(SkShader::kClamp_TileMode, *filterOrNullForBicubic); + return GrSimpleTextureEffect::Make(texture, std::move(colorSpaceXform), textureMatrix, + params); + } + } else { + if (kDomain_DomainMode == domainMode) { + return GrBicubicEffect::Make(texture, std::move(colorSpaceXform), textureMatrix, + domain); + } else { + static const SkShader::TileMode kClampClamp[] = + { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode }; + return GrBicubicEffect::Make(texture, std::move(colorSpaceXform), textureMatrix, + kClampClamp); + } + } +} diff --git a/src/gpu/GrTextureProducer.h b/src/gpu/GrTextureProducer.h new file mode 100644 index 0000000000..4cae933168 --- /dev/null +++ b/src/gpu/GrTextureProducer.h @@ -0,0 +1,146 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureProducer_DEFINED +#define GrTextureProducer_DEFINED + +#include "GrSamplerParams.h" +#include "GrResourceKey.h" + +class GrColorSpaceXform; +class GrTexture; + +/** + * Different GPUs and API extensions have different requirements with respect to what texture + * sampling parameters may be used with textures of various types. This class facilitates making + * texture compatible with a given GrSamplerParams. There are two immediate subclasses defined + * below. One is a base class for sources that are inherently texture-backed (e.g. a texture-backed + * SkImage). It supports subsetting the original texture. The other is for use cases where the + * source can generate a texture that represents some content (e.g. cpu pixels, SkPicture, ...). + */ +class GrTextureProducer : public SkNoncopyable { +public: + struct CopyParams { + GrSamplerParams::FilterMode fFilter; + int fWidth; + int fHeight; + }; + + enum FilterConstraint { + kYes_FilterConstraint, + kNo_FilterConstraint, + }; + + /** + * Helper for creating a fragment processor to sample the texture with a given filtering mode. + * It attempts to avoid making texture copies or using domains whenever possible. + * + * @param textureMatrix Matrix used to access the texture. It is applied to + * the local coords. The post-transformed coords should + * be in texel units (rather than normalized) with + * respect to this Producer's bounds (width()/height()). + * @param constraintRect A rect that represents the area of the texture to be + * sampled. It must be contained in the Producer's + * bounds as defined by width()/height(). + * @param filterConstriant Indicates whether filtering is limited to + * constraintRect. + * @param coordsLimitedToConstraintRect Is it known that textureMatrix*localCoords is bound + * by the portion of the texture indicated by + * constraintRect (without consideration of filter + * width, just the raw coords). + * @param filterOrNullForBicubic If non-null indicates the filter mode. If null means + * use bicubic filtering. + **/ + virtual sk_sp<GrFragmentProcessor> createFragmentProcessor( + const SkMatrix& textureMatrix, + const SkRect& constraintRect, + FilterConstraint filterConstraint, + bool coordsLimitedToConstraintRect, + const GrSamplerParams::FilterMode* filterOrNullForBicubic, + SkColorSpace* dstColorSpace, + SkDestinationSurfaceColorMode) = 0; + + virtual ~GrTextureProducer() {} + + int width() const { return fWidth; } + int height() const { return fHeight; } + bool isAlphaOnly() const { return fIsAlphaOnly; } + virtual SkAlphaType alphaType() const = 0; + +protected: + GrTextureProducer(int width, int height, bool isAlphaOnly) + : fWidth(width) + , fHeight(height) + , fIsAlphaOnly(isAlphaOnly) {} + + /** Helper for creating a key for a copy from an original key. */ + static void MakeCopyKeyFromOrigKey(const GrUniqueKey& origKey, + const CopyParams& copyParams, + GrUniqueKey* copyKey) { + SkASSERT(!copyKey->isValid()); + if (origKey.isValid()) { + static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); + GrUniqueKey::Builder builder(copyKey, origKey, kDomain, 3); + builder[0] = copyParams.fFilter; + builder[1] = copyParams.fWidth; + builder[2] = copyParams.fHeight; + } + } + + /** + * If we need to make a copy in order to be compatible with GrTextureParams producer is asked to + * return a key that identifies its original content + the CopyParms parameter. If the producer + * does not want to cache the stretched version (e.g. the producer is volatile), this should + * simply return without initializing the copyKey. If the texture generated by this producer + * depends on colorMode, then that information should also be incorporated in the key. + */ + virtual void makeCopyKey(const CopyParams&, GrUniqueKey* copyKey, + SkDestinationSurfaceColorMode colorMode) = 0; + + /** + * If a stretched version of the texture is generated, it may be cached (assuming that + * makeCopyKey() returns true). In that case, the maker is notified in case it + * wants to note that for when the maker is destroyed. + */ + virtual void didCacheCopy(const GrUniqueKey& copyKey) = 0; + + + enum DomainMode { + kNoDomain_DomainMode, + kDomain_DomainMode, + kTightCopy_DomainMode + }; + + static GrTexture* CopyOnGpu(GrTexture* inputTexture, const SkIRect* subset, + const CopyParams& copyParams); + + static DomainMode DetermineDomainMode( + const SkRect& constraintRect, + FilterConstraint filterConstraint, + bool coordsLimitedToConstraintRect, + int texW, int texH, + const SkIRect* textureContentArea, + const GrSamplerParams::FilterMode* filterModeOrNullForBicubic, + SkRect* domainRect); + + static sk_sp<GrFragmentProcessor> CreateFragmentProcessorForDomainAndFilter( + GrTexture* texture, + sk_sp<GrColorSpaceXform> colorSpaceXform, + const SkMatrix& textureMatrix, + DomainMode domainMode, + const SkRect& domain, + const GrSamplerParams::FilterMode* filterOrNullForBicubic); + +private: + const int fWidth; + const int fHeight; + const bool fIsAlphaOnly; + + typedef SkNoncopyable INHERITED; +}; + +#endif diff --git a/src/gpu/GrTextureProvider.cpp b/src/gpu/GrTextureProvider.cpp index b49c2c96d6..8d6239d2a1 100644 --- a/src/gpu/GrTextureProvider.cpp +++ b/src/gpu/GrTextureProvider.cpp @@ -14,6 +14,7 @@ #include "../private/GrSingleOwner.h" #include "SkMathPriv.h" #include "SkTArray.h" +#include "SkTLazy.h" #define ASSERT_SINGLE_OWNER \ SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);) diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index aae3b95ffb..529dfa3097 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -14,6 +14,7 @@ #include "GrImageTextureMaker.h" #include "GrRenderTargetContextPriv.h" #include "GrStyle.h" +#include "GrTextureAdjuster.h" #include "GrTracing.h" #include "SkCanvasPriv.h" diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp index 74afca9beb..93c9f18737 100644 --- a/src/gpu/SkGpuDevice_drawTexture.cpp +++ b/src/gpu/SkGpuDevice_drawTexture.cpp @@ -11,7 +11,7 @@ #include "GrCaps.h" #include "GrRenderTargetContext.h" #include "GrStyle.h" -#include "GrTextureParamsAdjuster.h" +#include "GrTextureAdjuster.h" #include "SkDraw.h" #include "SkGrPriv.h" #include "SkMaskFilter.h" @@ -168,7 +168,7 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, &doBicubic); const GrSamplerParams::FilterMode* filterMode = doBicubic ? nullptr : &fm; - GrTextureAdjuster::FilterConstraint constraintMode; + GrTextureProducer::FilterConstraint constraintMode; if (SkCanvas::kFast_SrcRectConstraint == constraint) { constraintMode = GrTextureAdjuster::kNo_FilterConstraint; } else { diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index c8eaac03e6..6ce9caed19 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -14,7 +14,6 @@ #include "GrContext.h" #include "GrGpuResourcePriv.h" #include "GrRenderTargetContext.h" -#include "GrTextureParamsAdjuster.h" #include "GrTexturePriv.h" #include "GrTypes.h" #include "GrXferProcessor.h" |