| /* |
| * Copyright 2012 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/effects/GrTextureDomain.h" |
| |
| #include "include/gpu/GrTexture.h" |
| #include "include/private/SkFloatingPoint.h" |
| #include "src/gpu/GrProxyProvider.h" |
| #include "src/gpu/GrShaderCaps.h" |
| #include "src/gpu/GrSurfaceProxyPriv.h" |
| #include "src/gpu/effects/generated/GrSimpleTextureEffect.h" |
| #include "src/gpu/glsl/GrGLSLFragmentProcessor.h" |
| #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
| #include "src/gpu/glsl/GrGLSLProgramDataManager.h" |
| #include "src/gpu/glsl/GrGLSLShaderBuilder.h" |
| #include "src/gpu/glsl/GrGLSLUniformHandler.h" |
| |
| #include <utility> |
| |
| GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode modeX, |
| Mode modeY, int index) |
| : fModeX(modeX) |
| , fModeY(modeY) |
| , fIndex(index) { |
| |
| if (!proxy) { |
| SkASSERT(modeX == kIgnore_Mode && modeY == kIgnore_Mode); |
| return; |
| } |
| |
| const SkRect kFullRect = SkRect::MakeIWH(proxy->width(), proxy->height()); |
| |
| // We don't currently handle domains that are empty or don't intersect the texture. |
| // It is OK if the domain rect is a line or point, but it should not be inverted. We do not |
| // handle rects that do not intersect the [0..1]x[0..1] rect. |
| SkASSERT(domain.fLeft <= domain.fRight); |
| SkASSERT(domain.fTop <= domain.fBottom); |
| fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight); |
| fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight); |
| fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom); |
| fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom); |
| SkASSERT(fDomain.fLeft <= fDomain.fRight); |
| SkASSERT(fDomain.fTop <= fDomain.fBottom); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| static SkString clamp_expression(GrTextureDomain::Mode mode, const char* inCoord, |
| const char* coordSwizzle, const char* domain, |
| const char* minSwizzle, const char* maxSwizzle) { |
| SkString clampedExpr; |
| switch(mode) { |
| case GrTextureDomain::kIgnore_Mode: |
| clampedExpr.printf("%s.%s\n", inCoord, coordSwizzle); |
| break; |
| case GrTextureDomain::kDecal_Mode: |
| // The lookup coordinate to use for decal will be clamped just like kClamp_Mode, |
| // it's just that the post-processing will be different, so fall through |
| case GrTextureDomain::kClamp_Mode: |
| clampedExpr.printf("clamp(%s.%s, %s.%s, %s.%s)", |
| inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle); |
| break; |
| case GrTextureDomain::kRepeat_Mode: |
| clampedExpr.printf("mod(%s.%s - %s.%s, %s.%s - %s.%s) + %s.%s", |
| inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle, |
| domain, minSwizzle, domain, minSwizzle); |
| break; |
| default: |
| SkASSERTF(false, "Unknown texture domain mode: %u\n", (uint32_t) mode); |
| break; |
| } |
| return clampedExpr; |
| } |
| |
| void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder, |
| GrGLSLUniformHandler* uniformHandler, |
| const GrShaderCaps* shaderCaps, |
| const GrTextureDomain& textureDomain, |
| const char* outColor, |
| const SkString& inCoords, |
| GrGLSLFragmentProcessor::SamplerHandle sampler, |
| const char* inModulateColor) { |
| SkASSERT(!fHasMode || (textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY)); |
| SkDEBUGCODE(fModeX = textureDomain.modeX();) |
| SkDEBUGCODE(fModeY = textureDomain.modeY();) |
| SkDEBUGCODE(fHasMode = true;) |
| |
| if ((textureDomain.modeX() != kIgnore_Mode || textureDomain.modeY() != kIgnore_Mode) && |
| !fDomainUni.isValid()) { |
| // Must include the domain uniform since at least one axis uses it |
| const char* name; |
| SkString uniName("TexDom"); |
| if (textureDomain.fIndex >= 0) { |
| uniName.appendS32(textureDomain.fIndex); |
| } |
| fDomainUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, |
| uniName.c_str(), &name); |
| fDomainName = name; |
| } |
| |
| bool decalX = textureDomain.modeX() == kDecal_Mode; |
| bool decalY = textureDomain.modeY() == kDecal_Mode; |
| if ((decalX || decalY) && !fDecalUni.isValid()) { |
| const char* name; |
| SkString uniName("DecalParams"); |
| if (textureDomain.fIndex >= 0) { |
| uniName.appendS32(textureDomain.fIndex); |
| } |
| // Half3 since this will hold texture width, height, and then a step function control param |
| fDecalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType, |
| uniName.c_str(), &name); |
| fDecalName = name; |
| } |
| |
| // Add a block so that we can declare variables |
| GrGLSLShaderBuilder::ShaderBlock block(builder); |
| // Always use a local variable for the input coordinates; often callers pass in an expression |
| // and we want to cache it across all of its references in the code below |
| builder->codeAppendf("float2 origCoord = %s;", inCoords.c_str()); |
| builder->codeAppend("float2 clampedCoord = "); |
| if (textureDomain.modeX() != textureDomain.modeY()) { |
| // The wrap modes differ on the two axes, so build up a coordinate that respects each axis' |
| // domain rule independently before sampling the texture. |
| SkString tcX = clamp_expression(textureDomain.modeX(), "origCoord", "x", |
| fDomainName.c_str(), "x", "z"); |
| SkString tcY = clamp_expression(textureDomain.modeY(), "origCoord", "y", |
| fDomainName.c_str(), "y", "w"); |
| builder->codeAppendf("float2(%s, %s)", tcX.c_str(), tcY.c_str()); |
| } else { |
| // Since the x and y axis wrap modes are the same, they can be calculated together using |
| // more efficient vector operations |
| SkString tc = clamp_expression(textureDomain.modeX(), "origCoord", "xy", |
| fDomainName.c_str(), "xy", "zw"); |
| builder->codeAppend(tc.c_str()); |
| } |
| builder->codeAppend(";"); |
| |
| // Look up the texture sample at the clamped coordinate location |
| builder->codeAppend("half4 inside = "); |
| builder->appendTextureLookupAndModulate(inModulateColor, sampler, "clampedCoord", |
| kFloat2_GrSLType); |
| builder->codeAppend(";"); |
| |
| // Apply decal mode's transparency interpolation if needed |
| if (decalX || decalY) { |
| // The decal err is the max absoluate value between the clamped coordinate and the original |
| // pixel coordinate. This will then be clamped to 1.f if it's greater than the control |
| // parameter, which simulates kNearest and kBilerp behavior depending on if it's 0 or 1. |
| if (decalX && decalY) { |
| builder->codeAppendf("half err = max(half(abs(clampedCoord.x - origCoord.x) * %s.x), " |
| "half(abs(clampedCoord.y - origCoord.y) * %s.y));", |
| fDecalName.c_str(), fDecalName.c_str()); |
| } else if (decalX) { |
| builder->codeAppendf("half err = half(abs(clampedCoord.x - origCoord.x) * %s.x);", |
| fDecalName.c_str()); |
| } else { |
| SkASSERT(decalY); |
| builder->codeAppendf("half err = half(abs(clampedCoord.y - origCoord.y) * %s.y);", |
| fDecalName.c_str()); |
| } |
| |
| // Apply a transform to the error rate, which let's us simulate nearest or bilerp filtering |
| // in the same shader. When the texture is nearest filtered, fSizeName.z is set to 1/2 so |
| // this becomes a step function centered at .5 away from the clamped coordinate (but the |
| // domain for decal is inset by .5 so the edge lines up properly). When bilerp, fSizeName.z |
| // is set to 1 and it becomes a simple linear blend between texture and transparent. |
| builder->codeAppendf("if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }", |
| fDecalName.c_str(), fDecalName.c_str()); |
| builder->codeAppendf("%s = mix(inside, half4(0, 0, 0, 0), err);", outColor); |
| } else { |
| // A simple look up |
| builder->codeAppendf("%s = inside;", outColor); |
| } |
| } |
| |
| void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman, |
| const GrTextureDomain& textureDomain, |
| GrTextureProxy* proxy, |
| const GrSamplerState& sampler) { |
| GrTexture* tex = proxy->peekTexture(); |
| SkASSERT(fHasMode && textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY); |
| if (kIgnore_Mode != textureDomain.modeX() || kIgnore_Mode != textureDomain.modeY()) { |
| bool sendDecalData = textureDomain.modeX() == kDecal_Mode || |
| textureDomain.modeY() == kDecal_Mode; |
| |
| // If the texture is using nearest filtering, then the decal filter weight should step from |
| // 0 (texture) to 1 (transparent) one half pixel away from the domain. When doing any other |
| // form of filtering, the weight should be 1.0 so that it smoothly interpolates between the |
| // texture and transparent. |
| SkScalar decalFilterWeight = sampler.filter() == GrSamplerState::Filter::kNearest ? |
| SK_ScalarHalf : 1.0f; |
| SkScalar wInv, hInv, h; |
| if (proxy->textureType() == GrTextureType::kRectangle) { |
| wInv = hInv = 1.f; |
| h = tex->height(); |
| |
| // Don't do any scaling by texture size for decal filter rate, it's already in pixels |
| if (sendDecalData) { |
| pdman.set3f(fDecalUni, 1.f, 1.f, decalFilterWeight); |
| } |
| } else { |
| wInv = SK_Scalar1 / tex->width(); |
| hInv = SK_Scalar1 / tex->height(); |
| h = 1.f; |
| |
| if (sendDecalData) { |
| pdman.set3f(fDecalUni, tex->width(), tex->height(), decalFilterWeight); |
| } |
| } |
| |
| float values[kPrevDomainCount] = { |
| SkScalarToFloat(textureDomain.domain().fLeft * wInv), |
| SkScalarToFloat(textureDomain.domain().fTop * hInv), |
| SkScalarToFloat(textureDomain.domain().fRight * wInv), |
| SkScalarToFloat(textureDomain.domain().fBottom * hInv) |
| }; |
| |
| if (proxy->textureType() == GrTextureType::kRectangle) { |
| SkASSERT(values[0] >= 0.0f && values[0] <= proxy->width()); |
| SkASSERT(values[1] >= 0.0f && values[1] <= proxy->height()); |
| SkASSERT(values[2] >= 0.0f && values[2] <= proxy->width()); |
| SkASSERT(values[3] >= 0.0f && values[3] <= proxy->height()); |
| } else { |
| SkASSERT(values[0] >= 0.0f && values[0] <= 1.0f); |
| SkASSERT(values[1] >= 0.0f && values[1] <= 1.0f); |
| SkASSERT(values[2] >= 0.0f && values[2] <= 1.0f); |
| SkASSERT(values[3] >= 0.0f && values[3] <= 1.0f); |
| } |
| |
| // vertical flip if necessary |
| if (kBottomLeft_GrSurfaceOrigin == proxy->origin()) { |
| values[1] = h - values[1]; |
| values[3] = h - values[3]; |
| |
| // The top and bottom were just flipped, so correct the ordering |
| // of elements so that values = (l, t, r, b). |
| using std::swap; |
| swap(values[1], values[3]); |
| } |
| if (0 != memcmp(values, fPrevDomain, kPrevDomainCount * sizeof(float))) { |
| pdman.set4fv(fDomainUni, 1, values); |
| memcpy(fPrevDomain, values, kPrevDomainCount * sizeof(float)); |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make( |
| sk_sp<GrTextureProxy> proxy, |
| GrColorType srcColorType, |
| const SkMatrix& matrix, |
| const SkRect& domain, |
| GrTextureDomain::Mode mode, |
| GrSamplerState::Filter filterMode) { |
| return Make(std::move(proxy), srcColorType, matrix, domain, mode, mode, |
| GrSamplerState(GrSamplerState::WrapMode::kClamp, filterMode)); |
| } |
| |
| std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make( |
| sk_sp<GrTextureProxy> proxy, |
| GrColorType srcColorType, |
| const SkMatrix& matrix, |
| const SkRect& domain, |
| GrTextureDomain::Mode modeX, |
| GrTextureDomain::Mode modeY, |
| const GrSamplerState& sampler) { |
| // If both domain modes happen to be ignore, it would be faster to just drop the domain logic |
| // entirely Technically, we could also use the simple texture effect if the domain modes agree |
| // with the sampler modes and the proxy is the same size as the domain. It's a lot easier for |
| // calling code to detect these cases and handle it themselves. |
| return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect( |
| std::move(proxy), srcColorType, matrix, domain, modeX, modeY, sampler)); |
| } |
| |
| GrTextureDomainEffect::GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy, |
| GrColorType srcColorType, |
| const SkMatrix& matrix, |
| const SkRect& domain, |
| GrTextureDomain::Mode modeX, |
| GrTextureDomain::Mode modeY, |
| const GrSamplerState& sampler) |
| : INHERITED(kGrTextureDomainEffect_ClassID, |
| ModulateForSamplerOptFlags(srcColorType, |
| GrTextureDomain::IsDecalSampled(sampler, modeX, modeY))) |
| , fCoordTransform(matrix, proxy.get()) |
| , fTextureDomain(proxy.get(), domain, modeX, modeY) |
| , fTextureSampler(std::move(proxy), sampler) { |
| SkASSERT((modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode) || |
| sampler.filter() == GrSamplerState::Filter::kNearest); |
| this->addCoordTransform(&fCoordTransform); |
| this->setTextureSamplerCnt(1); |
| } |
| |
| GrTextureDomainEffect::GrTextureDomainEffect(const GrTextureDomainEffect& that) |
| : INHERITED(kGrTextureDomainEffect_ClassID, that.optimizationFlags()) |
| , fCoordTransform(that.fCoordTransform) |
| , fTextureDomain(that.fTextureDomain) |
| , fTextureSampler(that.fTextureSampler) { |
| this->addCoordTransform(&fCoordTransform); |
| this->setTextureSamplerCnt(1); |
| } |
| |
| void GrTextureDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, |
| GrProcessorKeyBuilder* b) const { |
| b->add32(GrTextureDomain::GLDomain::DomainKey(fTextureDomain)); |
| } |
| |
| GrGLSLFragmentProcessor* GrTextureDomainEffect::onCreateGLSLInstance() const { |
| class GLSLProcessor : public GrGLSLFragmentProcessor { |
| public: |
| void emitCode(EmitArgs& args) override { |
| const GrTextureDomainEffect& tde = args.fFp.cast<GrTextureDomainEffect>(); |
| const GrTextureDomain& domain = tde.fTextureDomain; |
| |
| GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
| SkString coords2D = |
| fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint); |
| |
| fGLDomain.sampleTexture(fragBuilder, |
| args.fUniformHandler, |
| args.fShaderCaps, |
| domain, |
| args.fOutputColor, |
| coords2D, |
| args.fTexSamplers[0], |
| args.fInputColor); |
| } |
| |
| protected: |
| void onSetData(const GrGLSLProgramDataManager& pdman, |
| const GrFragmentProcessor& fp) override { |
| const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>(); |
| const GrTextureDomain& domain = tde.fTextureDomain; |
| GrTextureProxy* proxy = tde.textureSampler(0).proxy(); |
| |
| fGLDomain.setData(pdman, domain, proxy, tde.textureSampler(0).samplerState()); |
| } |
| |
| private: |
| GrTextureDomain::GLDomain fGLDomain; |
| }; |
| |
| return new GLSLProcessor; |
| } |
| |
| bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const { |
| const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>(); |
| return this->fTextureDomain == s.fTextureDomain; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect); |
| |
| #if GR_TEST_UTILS |
| std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::TestCreate(GrProcessorTestData* d) { |
| int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx |
| : GrProcessorUnitTest::kAlphaTextureIdx; |
| sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); |
| SkRect domain; |
| domain.fLeft = d->fRandom->nextRangeScalar(0, proxy->width()); |
| domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width()); |
| domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height()); |
| domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height()); |
| GrTextureDomain::Mode modeX = |
| (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount); |
| GrTextureDomain::Mode modeY = |
| (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount); |
| const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom); |
| bool bilerp = modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode ? |
| d->fRandom->nextBool() : false; |
| return GrTextureDomainEffect::Make( |
| std::move(proxy), |
| d->textureProxyColorType(texIdx), |
| matrix, |
| domain, |
| modeX, |
| modeY, |
| GrSamplerState(GrSamplerState::WrapMode::kClamp, bilerp ? |
| GrSamplerState::Filter::kBilerp : GrSamplerState::Filter::kNearest)); |
| } |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make( |
| sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) { |
| return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor( |
| std::move(proxy), subset, deviceSpaceOffset)); |
| } |
| |
| GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor( |
| sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) |
| : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID, |
| kCompatibleWithCoverageAsAlpha_OptimizationFlag) |
| , fTextureSampler(proxy, GrSamplerState::ClampNearest()) |
| , fTextureDomain(proxy.get(), |
| GrTextureDomain::MakeTexelDomain(subset, GrTextureDomain::kDecal_Mode), |
| GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode) { |
| this->setTextureSamplerCnt(1); |
| fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft; |
| fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop; |
| } |
| |
| GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor( |
| const GrDeviceSpaceTextureDecalFragmentProcessor& that) |
| : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID, |
| kCompatibleWithCoverageAsAlpha_OptimizationFlag) |
| , fTextureSampler(that.fTextureSampler) |
| , fTextureDomain(that.fTextureDomain) |
| , fDeviceSpaceOffset(that.fDeviceSpaceOffset) { |
| this->setTextureSamplerCnt(1); |
| } |
| |
| std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::clone() const { |
| return std::unique_ptr<GrFragmentProcessor>( |
| new GrDeviceSpaceTextureDecalFragmentProcessor(*this)); |
| } |
| |
| GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const { |
| class GLSLProcessor : public GrGLSLFragmentProcessor { |
| public: |
| void emitCode(EmitArgs& args) override { |
| const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp = |
| args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>(); |
| const char* scaleAndTranslateName; |
| fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, |
| kHalf4_GrSLType, |
| "scaleAndTranslate", |
| &scaleAndTranslateName); |
| args.fFragBuilder->codeAppendf("half2 coords = half2(sk_FragCoord.xy * %s.xy + %s.zw);", |
| scaleAndTranslateName, scaleAndTranslateName); |
| fGLDomain.sampleTexture(args.fFragBuilder, |
| args.fUniformHandler, |
| args.fShaderCaps, |
| dstdfp.fTextureDomain, |
| args.fOutputColor, |
| SkString("coords"), |
| args.fTexSamplers[0], |
| args.fInputColor); |
| } |
| |
| protected: |
| void onSetData(const GrGLSLProgramDataManager& pdman, |
| const GrFragmentProcessor& fp) override { |
| const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp = |
| fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>(); |
| GrTextureProxy* proxy = dstdfp.textureSampler(0).proxy(); |
| GrTexture* texture = proxy->peekTexture(); |
| |
| fGLDomain.setData(pdman, dstdfp.fTextureDomain, proxy, |
| dstdfp.textureSampler(0).samplerState()); |
| float iw = 1.f / texture->width(); |
| float ih = 1.f / texture->height(); |
| float scaleAndTransData[4] = { |
| iw, ih, |
| -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih |
| }; |
| if (proxy->origin() == kBottomLeft_GrSurfaceOrigin) { |
| scaleAndTransData[1] = -scaleAndTransData[1]; |
| scaleAndTransData[3] = 1 - scaleAndTransData[3]; |
| } |
| pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData); |
| } |
| |
| private: |
| GrTextureDomain::GLDomain fGLDomain; |
| UniformHandle fScaleAndTranslateUni; |
| }; |
| |
| return new GLSLProcessor; |
| } |
| |
| bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const { |
| const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp = |
| fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>(); |
| return dstdfp.fTextureSampler.proxy()->underlyingUniqueID() == |
| fTextureSampler.proxy()->underlyingUniqueID() && |
| dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset && |
| dstdfp.fTextureDomain == fTextureDomain; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor); |
| |
| #if GR_TEST_UTILS |
| std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate( |
| GrProcessorTestData* d) { |
| int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx |
| : GrProcessorUnitTest::kAlphaTextureIdx; |
| sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); |
| SkIRect subset; |
| subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1); |
| subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width()); |
| subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1); |
| subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height()); |
| SkIPoint pt; |
| pt.fX = d->fRandom->nextULessThan(2048); |
| pt.fY = d->fRandom->nextULessThan(2048); |
| return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(proxy), subset, pt); |
| } |
| #endif |