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>
This commit is contained in:
Brian Osman 2016-11-28 10:06:27 -05:00 committed by Skia Commit-Bot
parent e9f78b41c6
commit e8e54580c3
18 changed files with 827 additions and 769 deletions

View File

@ -200,10 +200,14 @@ skia_gpu_sources = [
"$_src/gpu/GrSurfaceProxy.cpp",
"$_src/gpu/GrSwizzle.h",
"$_src/gpu/GrTexture.cpp",
"$_src/gpu/GrTextureAdjuster.cpp",
"$_src/gpu/GrTextureAdjuster.h",
"$_src/gpu/GrTextureContext.cpp",
"$_src/gpu/GrTextureParamsAdjuster.h",
"$_src/gpu/GrTextureParamsAdjuster.cpp",
"$_src/gpu/GrTextureMaker.cpp",
"$_src/gpu/GrTextureMaker.h",
"$_src/gpu/GrTexturePriv.h",
"$_src/gpu/GrTextureProducer.cpp",
"$_src/gpu/GrTextureProducer.h",
"$_src/gpu/GrTextureProvider.cpp",
"$_src/gpu/GrTextureProxy.cpp",
"$_src/gpu/GrTextureRenderTargetProxy.cpp",

View File

@ -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

View File

@ -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"

View File

@ -8,7 +8,7 @@
#ifndef GrImageTextureMaker_DEFINED
#define GrImageTextureMaker_DEFINED
#include "GrTextureParamsAdjuster.h"
#include "GrTextureMaker.h"
#include "SkImage.h"
class SkImageCacherator;

View File

@ -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, &copyParams)) {
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);
}

View File

@ -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 &copyParams);
typedef GrTextureProducer INHERITED;
};
#endif

107
src/gpu/GrTextureMaker.cpp Normal file
View File

@ -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,
&copyParams)) {
return this->refOriginalTexture(willBeMipped, colorMode);
}
GrUniqueKey copyKey;
this->makeCopyKey(copyParams, &copyKey, 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);
}

74
src/gpu/GrTextureMaker.h Normal file
View File

@ -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

View File

@ -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, &copyParams)) {
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,
&copyParams)) {
return this->refOriginalTexture(willBeMipped, colorMode);
}
GrUniqueKey copyKey;
this->makeCopyKey(copyParams, &copyKey, 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);
}

View File

@ -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 &copyParams);
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

View File

@ -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);
}
}
}

146
src/gpu/GrTextureProducer.h Normal file
View File

@ -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

View File

@ -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);)

View File

@ -14,6 +14,7 @@
#include "GrImageTextureMaker.h"
#include "GrRenderTargetContextPriv.h"
#include "GrStyle.h"
#include "GrTextureAdjuster.h"
#include "GrTracing.h"
#include "SkCanvasPriv.h"

View File

@ -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 {

View File

@ -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"

View File

@ -15,6 +15,7 @@
#include "GrContext.h"
#include "GrImageTextureMaker.h"
#include "GrRenderTargetContext.h"
#include "GrTextureAdjuster.h"
#include "GrTexturePriv.h"
#include "effects/GrYUVEffect.h"
#include "SkCanvas.h"

View File

@ -17,7 +17,7 @@
#if SK_SUPPORT_GPU
#include "GrContext.h"
#include "GrTextureParamsAdjuster.h"
#include "GrTextureAdjuster.h"
#include "SkGr.h"
#include "SkGrPriv.h"
#endif