Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrTextureDomain.h"
      9 
     10 #include "GrProxyProvider.h"
     11 #include "GrShaderCaps.h"
     12 #include "GrSimpleTextureEffect.h"
     13 #include "GrSurfaceProxyPriv.h"
     14 #include "GrTexture.h"
     15 #include "SkFloatingPoint.h"
     16 #include "glsl/GrGLSLFragmentProcessor.h"
     17 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     18 #include "glsl/GrGLSLProgramDataManager.h"
     19 #include "glsl/GrGLSLShaderBuilder.h"
     20 #include "glsl/GrGLSLUniformHandler.h"
     21 
     22 #include <utility>
     23 
     24 GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode modeX,
     25                                  Mode modeY, int index)
     26     : fModeX(modeX)
     27     , fModeY(modeY)
     28     , fIndex(index) {
     29 
     30     if (!proxy) {
     31         SkASSERT(modeX == kIgnore_Mode && modeY == kIgnore_Mode);
     32         return;
     33     }
     34 
     35     const SkRect kFullRect = SkRect::MakeIWH(proxy->width(), proxy->height());
     36 
     37     // We don't currently handle domains that are empty or don't intersect the texture.
     38     // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
     39     // handle rects that do not intersect the [0..1]x[0..1] rect.
     40     SkASSERT(domain.fLeft <= domain.fRight);
     41     SkASSERT(domain.fTop <= domain.fBottom);
     42     fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight);
     43     fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight);
     44     fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom);
     45     fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom);
     46     SkASSERT(fDomain.fLeft <= fDomain.fRight);
     47     SkASSERT(fDomain.fTop <= fDomain.fBottom);
     48 }
     49 
     50 //////////////////////////////////////////////////////////////////////////////
     51 
     52 static SkString clamp_expression(GrTextureDomain::Mode mode, const char* inCoord,
     53                                  const char* coordSwizzle, const char* domain,
     54                                  const char* minSwizzle, const char* maxSwizzle) {
     55     SkString clampedExpr;
     56     switch(mode) {
     57         case GrTextureDomain::kIgnore_Mode:
     58             clampedExpr.printf("%s.%s\n", inCoord, coordSwizzle);
     59             break;
     60         case GrTextureDomain::kDecal_Mode:
     61             // The lookup coordinate to use for decal will be clamped just like kClamp_Mode,
     62             // it's just that the post-processing will be different, so fall through
     63         case GrTextureDomain::kClamp_Mode:
     64             clampedExpr.printf("clamp(%s.%s, %s.%s, %s.%s)",
     65                                inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle);
     66             break;
     67         case GrTextureDomain::kRepeat_Mode:
     68             clampedExpr.printf("mod(%s.%s - %s.%s, %s.%s - %s.%s) + %s.%s",
     69                                inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle,
     70                                domain, minSwizzle, domain, minSwizzle);
     71             break;
     72         default:
     73             SkASSERTF(false, "Unknown texture domain mode: %u\n", (uint32_t) mode);
     74             break;
     75     }
     76     return clampedExpr;
     77 }
     78 
     79 void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder,
     80                                               GrGLSLUniformHandler* uniformHandler,
     81                                               const GrShaderCaps* shaderCaps,
     82                                               const GrTextureDomain& textureDomain,
     83                                               const char* outColor,
     84                                               const SkString& inCoords,
     85                                               GrGLSLFragmentProcessor::SamplerHandle sampler,
     86                                               const char* inModulateColor) {
     87     SkASSERT(!fHasMode || (textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY));
     88     SkDEBUGCODE(fModeX = textureDomain.modeX();)
     89     SkDEBUGCODE(fModeY = textureDomain.modeY();)
     90     SkDEBUGCODE(fHasMode = true;)
     91 
     92     if ((textureDomain.modeX() != kIgnore_Mode || textureDomain.modeY() != kIgnore_Mode) &&
     93         !fDomainUni.isValid()) {
     94         // Must include the domain uniform since at least one axis uses it
     95         const char* name;
     96         SkString uniName("TexDom");
     97         if (textureDomain.fIndex >= 0) {
     98             uniName.appendS32(textureDomain.fIndex);
     99         }
    100         fDomainUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
    101                                                 uniName.c_str(), &name);
    102         fDomainName = name;
    103     }
    104 
    105     bool decalX = textureDomain.modeX() == kDecal_Mode;
    106     bool decalY = textureDomain.modeY() == kDecal_Mode;
    107     if ((decalX || decalY) && !fDecalUni.isValid()) {
    108         const char* name;
    109         SkString uniName("DecalParams");
    110         if (textureDomain.fIndex >= 0) {
    111             uniName.appendS32(textureDomain.fIndex);
    112         }
    113         // Half3 since this will hold texture width, height, and then a step function control param
    114         fDecalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
    115                                                uniName.c_str(), &name);
    116         fDecalName = name;
    117     }
    118 
    119     // Add a block so that we can declare variables
    120     GrGLSLShaderBuilder::ShaderBlock block(builder);
    121     // Always use a local variable for the input coordinates; often callers pass in an expression
    122     // and we want to cache it across all of its references in the code below
    123     builder->codeAppendf("float2 origCoord = %s;", inCoords.c_str());
    124     builder->codeAppend("float2 clampedCoord = ");
    125     if (textureDomain.modeX() != textureDomain.modeY()) {
    126         // The wrap modes differ on the two axes, so build up a coordinate that respects each axis'
    127         // domain rule independently before sampling the texture.
    128         SkString tcX = clamp_expression(textureDomain.modeX(), "origCoord", "x",
    129                                         fDomainName.c_str(), "x", "z");
    130         SkString tcY = clamp_expression(textureDomain.modeY(), "origCoord", "y",
    131                                         fDomainName.c_str(), "y", "w");
    132         builder->codeAppendf("float2(%s, %s)", tcX.c_str(), tcY.c_str());
    133     } else {
    134         // Since the x and y axis wrap modes are the same, they can be calculated together using
    135         // more efficient vector operations
    136         SkString tc = clamp_expression(textureDomain.modeX(), "origCoord", "xy",
    137                                        fDomainName.c_str(), "xy", "zw");
    138         builder->codeAppend(tc.c_str());
    139     }
    140     builder->codeAppend(";");
    141 
    142     // Look up the texture sample at the clamped coordinate location
    143     builder->codeAppend("half4 inside = ");
    144     builder->appendTextureLookupAndModulate(inModulateColor, sampler, "clampedCoord",
    145                                             kFloat2_GrSLType);
    146     builder->codeAppend(";");
    147 
    148     // Apply decal mode's transparency interpolation if needed
    149     if (decalX || decalY) {
    150         // The decal err is the max absoluate value between the clamped coordinate and the original
    151         // pixel coordinate. This will then be clamped to 1.f if it's greater than the control
    152         // parameter, which simulates kNearest and kBilerp behavior depending on if it's 0 or 1.
    153         if (decalX && decalY) {
    154             builder->codeAppendf("half err = max(half(abs(clampedCoord.x - origCoord.x) * %s.x), "
    155                                                 "half(abs(clampedCoord.y - origCoord.y) * %s.y));",
    156                                  fDecalName.c_str(), fDecalName.c_str());
    157         } else if (decalX) {
    158             builder->codeAppendf("half err = half(abs(clampedCoord.x - origCoord.x) * %s.x);",
    159                                  fDecalName.c_str());
    160         } else {
    161             SkASSERT(decalY);
    162             builder->codeAppendf("half err = half(abs(clampedCoord.y - origCoord.y) * %s.y);",
    163                                  fDecalName.c_str());
    164         }
    165 
    166         // Apply a transform to the error rate, which let's us simulate nearest or bilerp filtering
    167         // in the same shader. When the texture is nearest filtered, fSizeName.z is set to 1/2 so
    168         // this becomes a step function centered at .5 away from the clamped coordinate (but the
    169         // domain for decal is inset by .5 so the edge lines up properly). When bilerp, fSizeName.z
    170         // is set to 1 and it becomes a simple linear blend between texture and transparent.
    171         builder->codeAppendf("if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }",
    172                              fDecalName.c_str(), fDecalName.c_str());
    173         builder->codeAppendf("%s = mix(inside, half4(0, 0, 0, 0), err);", outColor);
    174     } else {
    175         // A simple look up
    176         builder->codeAppendf("%s = inside;", outColor);
    177     }
    178 }
    179 
    180 void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
    181                                         const GrTextureDomain& textureDomain,
    182                                         GrTextureProxy* proxy,
    183                                         const GrSamplerState& sampler) {
    184     GrTexture* tex = proxy->peekTexture();
    185     SkASSERT(fHasMode && textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY);
    186     if (kIgnore_Mode != textureDomain.modeX() || kIgnore_Mode != textureDomain.modeY()) {
    187         bool sendDecalData = textureDomain.modeX() == kDecal_Mode ||
    188                              textureDomain.modeY() == kDecal_Mode;
    189 
    190         // If the texture is using nearest filtering, then the decal filter weight should step from
    191         // 0 (texture) to 1 (transparent) one half pixel away from the domain. When doing any other
    192         // form of filtering, the weight should be 1.0 so that it smoothly interpolates between the
    193         // texture and transparent.
    194         SkScalar decalFilterWeight = sampler.filter() == GrSamplerState::Filter::kNearest ?
    195                 SK_ScalarHalf : 1.0f;
    196         SkScalar wInv, hInv, h;
    197         if (proxy->textureType() == GrTextureType::kRectangle) {
    198             wInv = hInv = 1.f;
    199             h = tex->height();
    200 
    201             // Don't do any scaling by texture size for decal filter rate, it's already in pixels
    202             if (sendDecalData) {
    203                 pdman.set3f(fDecalUni, 1.f, 1.f, decalFilterWeight);
    204             }
    205         } else {
    206             wInv = SK_Scalar1 / tex->width();
    207             hInv = SK_Scalar1 / tex->height();
    208             h = 1.f;
    209 
    210             if (sendDecalData) {
    211                 pdman.set3f(fDecalUni, tex->width(), tex->height(), decalFilterWeight);
    212             }
    213         }
    214 
    215         float values[kPrevDomainCount] = {
    216             SkScalarToFloat(textureDomain.domain().fLeft * wInv),
    217             SkScalarToFloat(textureDomain.domain().fTop * hInv),
    218             SkScalarToFloat(textureDomain.domain().fRight * wInv),
    219             SkScalarToFloat(textureDomain.domain().fBottom * hInv)
    220         };
    221 
    222         if (proxy->textureType() == GrTextureType::kRectangle) {
    223             SkASSERT(values[0] >= 0.0f && values[0] <= proxy->height());
    224             SkASSERT(values[1] >= 0.0f && values[1] <= proxy->height());
    225             SkASSERT(values[2] >= 0.0f && values[2] <= proxy->height());
    226             SkASSERT(values[3] >= 0.0f && values[3] <= proxy->height());
    227         } else {
    228             SkASSERT(values[0] >= 0.0f && values[0] <= 1.0f);
    229             SkASSERT(values[1] >= 0.0f && values[1] <= 1.0f);
    230             SkASSERT(values[2] >= 0.0f && values[2] <= 1.0f);
    231             SkASSERT(values[3] >= 0.0f && values[3] <= 1.0f);
    232         }
    233 
    234         // vertical flip if necessary
    235         if (kBottomLeft_GrSurfaceOrigin == proxy->origin()) {
    236             values[1] = h - values[1];
    237             values[3] = h - values[3];
    238 
    239             // The top and bottom were just flipped, so correct the ordering
    240             // of elements so that values = (l, t, r, b).
    241             using std::swap;
    242             swap(values[1], values[3]);
    243         }
    244         if (0 != memcmp(values, fPrevDomain, kPrevDomainCount * sizeof(float))) {
    245             pdman.set4fv(fDomainUni, 1, values);
    246             memcpy(fPrevDomain, values, kPrevDomainCount * sizeof(float));
    247         }
    248     }
    249 }
    250 
    251 ///////////////////////////////////////////////////////////////////////////////
    252 
    253 std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make(
    254         sk_sp<GrTextureProxy> proxy,
    255         const SkMatrix& matrix,
    256         const SkRect& domain,
    257         GrTextureDomain::Mode mode,
    258         GrSamplerState::Filter filterMode) {
    259     return Make(std::move(proxy), matrix, domain, mode, mode,
    260                 GrSamplerState(GrSamplerState::WrapMode::kClamp, filterMode));
    261 }
    262 
    263 std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make(
    264         sk_sp<GrTextureProxy> proxy,
    265         const SkMatrix& matrix,
    266         const SkRect& domain,
    267         GrTextureDomain::Mode modeX,
    268         GrTextureDomain::Mode modeY,
    269         const GrSamplerState& sampler) {
    270     // If both domain modes happen to be ignore, it would be faster to just drop the domain logic
    271     // entirely Technically, we could also use the simple texture effect if the domain modes agree
    272     // with the sampler modes and the proxy is the same size as the domain. It's a lot easier for
    273     // calling code to detect these cases and handle it themselves.
    274     return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(
    275             std::move(proxy), matrix, domain, modeX, modeY, sampler));
    276 }
    277 
    278 GrTextureDomainEffect::GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy,
    279                                              const SkMatrix& matrix,
    280                                              const SkRect& domain,
    281                                              GrTextureDomain::Mode modeX,
    282                                              GrTextureDomain::Mode modeY,
    283                                              const GrSamplerState& sampler)
    284         : INHERITED(kGrTextureDomainEffect_ClassID,
    285                     ModulateForSamplerOptFlags(proxy->config(),
    286                             GrTextureDomain::IsDecalSampled(sampler, modeX, modeY)))
    287         , fCoordTransform(matrix, proxy.get())
    288         , fTextureDomain(proxy.get(), domain, modeX, modeY)
    289         , fTextureSampler(std::move(proxy), sampler) {
    290     SkASSERT((modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode) ||
    291              sampler.filter() == GrSamplerState::Filter::kNearest);
    292     this->addCoordTransform(&fCoordTransform);
    293     this->setTextureSamplerCnt(1);
    294 }
    295 
    296 GrTextureDomainEffect::GrTextureDomainEffect(const GrTextureDomainEffect& that)
    297         : INHERITED(kGrTextureDomainEffect_ClassID, that.optimizationFlags())
    298         , fCoordTransform(that.fCoordTransform)
    299         , fTextureDomain(that.fTextureDomain)
    300         , fTextureSampler(that.fTextureSampler) {
    301     this->addCoordTransform(&fCoordTransform);
    302     this->setTextureSamplerCnt(1);
    303 }
    304 
    305 void GrTextureDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
    306                                                   GrProcessorKeyBuilder* b) const {
    307     b->add32(GrTextureDomain::GLDomain::DomainKey(fTextureDomain));
    308 }
    309 
    310 GrGLSLFragmentProcessor* GrTextureDomainEffect::onCreateGLSLInstance() const  {
    311     class GLSLProcessor : public GrGLSLFragmentProcessor {
    312     public:
    313         void emitCode(EmitArgs& args) override {
    314             const GrTextureDomainEffect& tde = args.fFp.cast<GrTextureDomainEffect>();
    315             const GrTextureDomain& domain = tde.fTextureDomain;
    316 
    317             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
    318             SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
    319 
    320             fGLDomain.sampleTexture(fragBuilder,
    321                                     args.fUniformHandler,
    322                                     args.fShaderCaps,
    323                                     domain,
    324                                     args.fOutputColor,
    325                                     coords2D,
    326                                     args.fTexSamplers[0],
    327                                     args.fInputColor);
    328         }
    329 
    330     protected:
    331         void onSetData(const GrGLSLProgramDataManager& pdman,
    332                        const GrFragmentProcessor& fp) override {
    333             const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>();
    334             const GrTextureDomain& domain = tde.fTextureDomain;
    335             GrTextureProxy* proxy = tde.textureSampler(0).proxy();
    336 
    337             fGLDomain.setData(pdman, domain, proxy, tde.textureSampler(0).samplerState());
    338         }
    339 
    340     private:
    341         GrTextureDomain::GLDomain         fGLDomain;
    342     };
    343 
    344     return new GLSLProcessor;
    345 }
    346 
    347 bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
    348     const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>();
    349     return this->fTextureDomain == s.fTextureDomain;
    350 }
    351 
    352 ///////////////////////////////////////////////////////////////////////////////
    353 
    354 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect);
    355 
    356 #if GR_TEST_UTILS
    357 std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::TestCreate(GrProcessorTestData* d) {
    358     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
    359                                         : GrProcessorUnitTest::kAlphaTextureIdx;
    360     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
    361     SkRect domain;
    362     domain.fLeft = d->fRandom->nextRangeScalar(0, proxy->width());
    363     domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width());
    364     domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height());
    365     domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height());
    366     GrTextureDomain::Mode modeX =
    367         (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
    368     GrTextureDomain::Mode modeY =
    369         (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
    370     const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
    371     bool bilerp = modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode ?
    372             d->fRandom->nextBool() : false;
    373     return GrTextureDomainEffect::Make(
    374             std::move(proxy),
    375             matrix,
    376             domain,
    377             modeX,
    378             modeY,
    379             GrSamplerState(GrSamplerState::WrapMode::kClamp, bilerp ?
    380                            GrSamplerState::Filter::kBilerp : GrSamplerState::Filter::kNearest));
    381 }
    382 #endif
    383 
    384 ///////////////////////////////////////////////////////////////////////////////
    385 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
    386         sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
    387     return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
    388             std::move(proxy), subset, deviceSpaceOffset));
    389 }
    390 
    391 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
    392         sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset)
    393         : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
    394                     kCompatibleWithCoverageAsAlpha_OptimizationFlag)
    395         , fTextureSampler(proxy, GrSamplerState::ClampNearest())
    396         , fTextureDomain(proxy.get(),
    397                          GrTextureDomain::MakeTexelDomain(subset, GrTextureDomain::kDecal_Mode),
    398                          GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode) {
    399     this->setTextureSamplerCnt(1);
    400     fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft;
    401     fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop;
    402 }
    403 
    404 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
    405         const GrDeviceSpaceTextureDecalFragmentProcessor& that)
    406         : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
    407                     kCompatibleWithCoverageAsAlpha_OptimizationFlag)
    408         , fTextureSampler(that.fTextureSampler)
    409         , fTextureDomain(that.fTextureDomain)
    410         , fDeviceSpaceOffset(that.fDeviceSpaceOffset) {
    411     this->setTextureSamplerCnt(1);
    412 }
    413 
    414 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::clone() const {
    415     return std::unique_ptr<GrFragmentProcessor>(
    416             new GrDeviceSpaceTextureDecalFragmentProcessor(*this));
    417 }
    418 
    419 GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const  {
    420     class GLSLProcessor : public GrGLSLFragmentProcessor {
    421     public:
    422         void emitCode(EmitArgs& args) override {
    423             const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
    424                     args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
    425             const char* scaleAndTranslateName;
    426             fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
    427                                                                      kHalf4_GrSLType,
    428                                                                      "scaleAndTranslate",
    429                                                                      &scaleAndTranslateName);
    430             args.fFragBuilder->codeAppendf("half2 coords = half2(sk_FragCoord.xy * %s.xy + %s.zw);",
    431                                            scaleAndTranslateName, scaleAndTranslateName);
    432             fGLDomain.sampleTexture(args.fFragBuilder,
    433                                     args.fUniformHandler,
    434                                     args.fShaderCaps,
    435                                     dstdfp.fTextureDomain,
    436                                     args.fOutputColor,
    437                                     SkString("coords"),
    438                                     args.fTexSamplers[0],
    439                                     args.fInputColor);
    440         }
    441 
    442     protected:
    443         void onSetData(const GrGLSLProgramDataManager& pdman,
    444                        const GrFragmentProcessor& fp) override {
    445             const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
    446                     fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
    447             GrTextureProxy* proxy = dstdfp.textureSampler(0).proxy();
    448             GrTexture* texture = proxy->peekTexture();
    449 
    450             fGLDomain.setData(pdman, dstdfp.fTextureDomain, proxy,
    451                               dstdfp.textureSampler(0).samplerState());
    452             float iw = 1.f / texture->width();
    453             float ih = 1.f / texture->height();
    454             float scaleAndTransData[4] = {
    455                 iw, ih,
    456                 -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih
    457             };
    458             if (proxy->origin() == kBottomLeft_GrSurfaceOrigin) {
    459                 scaleAndTransData[1] = -scaleAndTransData[1];
    460                 scaleAndTransData[3] = 1 - scaleAndTransData[3];
    461             }
    462             pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData);
    463         }
    464 
    465     private:
    466         GrTextureDomain::GLDomain   fGLDomain;
    467         UniformHandle               fScaleAndTranslateUni;
    468     };
    469 
    470     return new GLSLProcessor;
    471 }
    472 
    473 bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const {
    474     const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
    475             fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
    476     return dstdfp.fTextureSampler.proxy()->underlyingUniqueID() ==
    477                    fTextureSampler.proxy()->underlyingUniqueID() &&
    478            dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset &&
    479            dstdfp.fTextureDomain == fTextureDomain;
    480 }
    481 
    482 ///////////////////////////////////////////////////////////////////////////////
    483 
    484 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor);
    485 
    486 #if GR_TEST_UTILS
    487 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate(
    488         GrProcessorTestData* d) {
    489     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
    490                                         : GrProcessorUnitTest::kAlphaTextureIdx;
    491     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
    492     SkIRect subset;
    493     subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1);
    494     subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width());
    495     subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1);
    496     subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height());
    497     SkIPoint pt;
    498     pt.fX = d->fRandom->nextULessThan(2048);
    499     pt.fY = d->fRandom->nextULessThan(2048);
    500     return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(proxy), subset, pt);
    501 }
    502 #endif
    503