Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2016 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 "GrTextureProducer.h"
      9 #include "GrClip.h"
     10 #include "GrProxyProvider.h"
     11 #include "GrRenderTargetContext.h"
     12 #include "GrTextureProxy.h"
     13 #include "SkRectPriv.h"
     14 #include "effects/GrBicubicEffect.h"
     15 #include "effects/GrSimpleTextureEffect.h"
     16 #include "effects/GrTextureDomain.h"
     17 
     18 sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context,
     19                                                    sk_sp<GrTextureProxy> inputProxy,
     20                                                    const CopyParams& copyParams,
     21                                                    bool dstWillRequireMipMaps) {
     22     SkASSERT(context);
     23 
     24     const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
     25     GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
     26 
     27     sk_sp<GrRenderTargetContext> copyRTC = context->makeDeferredRenderTargetContextWithFallback(
     28             SkBackingFit::kExact, dstRect.width(), dstRect.height(), inputProxy->config(), nullptr,
     29             1, mipMapped, inputProxy->origin());
     30     if (!copyRTC) {
     31         return nullptr;
     32     }
     33 
     34     GrPaint paint;
     35     paint.setGammaCorrect(true);
     36 
     37     SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
     38 
     39     bool needsDomain = false;
     40     if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
     41         bool resizing = localRect.width()  != dstRect.width() ||
     42                         localRect.height() != dstRect.height();
     43         needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get());
     44     }
     45 
     46     if (needsDomain) {
     47         const SkRect domain = localRect.makeInset(0.5f, 0.5f);
     48         // This would cause us to read values from outside the subset. Surely, the caller knows
     49         // better!
     50         SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
     51         paint.addColorFragmentProcessor(
     52             GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain,
     53                                         GrTextureDomain::kClamp_Mode, copyParams.fFilter));
     54     } else {
     55         GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
     56         paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState);
     57     }
     58     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     59 
     60     copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
     61                             localRect);
     62     return copyRTC->asTextureProxyRef();
     63 }
     64 
     65 /** Determines whether a texture domain is necessary and if so what domain to use. There are two
     66  *  rectangles to consider:
     67  *  - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
     68  *    We can *never* allow filtering to cause bleed of pixels outside this rectangle.
     69  *  - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
     70  *    be contained by the content area. The filterConstraint specifies whether we are allowed to
     71  *    bleed across this rect.
     72  *
     73  *  We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
     74  *  and whether the coords generated by the draw would all fall within the constraint rect. If the
     75  *  latter is true we only need to consider whether the filter would extend beyond the rects.
     76  */
     77 GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
     78         const SkRect& constraintRect,
     79         FilterConstraint filterConstraint,
     80         bool coordsLimitedToConstraintRect,
     81         GrTextureProxy* proxy,
     82         const GrSamplerState::Filter* filterModeOrNullForBicubic,
     83         SkRect* domainRect) {
     84     const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
     85 
     86     SkASSERT(proxyBounds.contains(constraintRect));
     87 
     88     const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);
     89 
     90     // If the constraint rectangle contains the whole proxy then no need for a domain.
     91     if (constraintRect.contains(proxyBounds) && proxyIsExact) {
     92         return kNoDomain_DomainMode;
     93     }
     94 
     95     bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
     96 
     97     // If we can filter outside the constraint rect, and there is no non-content area of the
     98     // proxy, and we aren't going to generate sample coords outside the constraint rect then we
     99     // don't need a domain.
    100     if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
    101         return kNoDomain_DomainMode;
    102     }
    103 
    104     // Get the domain inset based on sampling mode (or bail if mipped)
    105     SkScalar filterHalfWidth = 0.f;
    106     if (filterModeOrNullForBicubic) {
    107         switch (*filterModeOrNullForBicubic) {
    108             case GrSamplerState::Filter::kNearest:
    109                 if (coordsLimitedToConstraintRect) {
    110                     return kNoDomain_DomainMode;
    111                 } else {
    112                     filterHalfWidth = 0.f;
    113                 }
    114                 break;
    115             case GrSamplerState::Filter::kBilerp:
    116                 filterHalfWidth = .5f;
    117                 break;
    118             case GrSamplerState::Filter::kMipMap:
    119                 if (restrictFilterToRect || !proxyIsExact) {
    120                     // No domain can save us here.
    121                     return kTightCopy_DomainMode;
    122                 }
    123                 return kNoDomain_DomainMode;
    124         }
    125     } else {
    126         // bicubic does nearest filtering internally.
    127         filterHalfWidth = 1.5f;
    128     }
    129 
    130     // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
    131     // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
    132 
    133     static const SkScalar kDomainInset = 0.5f;
    134     // Figure out the limits of pixels we're allowed to sample from.
    135     // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
    136     // the domain.
    137     if (restrictFilterToRect) {
    138         *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
    139     } else if (!proxyIsExact) {
    140         // If we got here then: proxy is not exact, the coords are limited to the
    141         // constraint rect, and we're allowed to filter across the constraint rect boundary. So
    142         // we check whether the filter would reach across the edge of the proxy.
    143         // We will only set the sides that are required.
    144 
    145         *domainRect = SkRectPriv::MakeLargest();
    146         if (coordsLimitedToConstraintRect) {
    147             // We may be able to use the fact that the texture coords are limited to the constraint
    148             // rect in order to avoid having to add a domain.
    149             bool needContentAreaConstraint = false;
    150             if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
    151                 domainRect->fRight = proxyBounds.fRight - kDomainInset;
    152                 needContentAreaConstraint = true;
    153             }
    154             if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
    155                 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
    156                 needContentAreaConstraint = true;
    157             }
    158             if (!needContentAreaConstraint) {
    159                 return kNoDomain_DomainMode;
    160             }
    161         } else {
    162             // Our sample coords for the texture are allowed to be outside the constraintRect so we
    163             // don't consider it when computing the domain.
    164             domainRect->fRight = proxyBounds.fRight - kDomainInset;
    165             domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
    166         }
    167     } else {
    168         return kNoDomain_DomainMode;
    169     }
    170 
    171     if (domainRect->fLeft > domainRect->fRight) {
    172         domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
    173     }
    174     if (domainRect->fTop > domainRect->fBottom) {
    175         domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
    176     }
    177     return kDomain_DomainMode;
    178 }
    179 
    180 std::unique_ptr<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter(
    181         sk_sp<GrTextureProxy> proxy,
    182         const SkMatrix& textureMatrix,
    183         DomainMode domainMode,
    184         const SkRect& domain,
    185         const GrSamplerState::Filter* filterOrNullForBicubic) {
    186     SkASSERT(kTightCopy_DomainMode != domainMode);
    187     if (filterOrNullForBicubic) {
    188         if (kDomain_DomainMode == domainMode) {
    189             return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
    190                                                GrTextureDomain::kClamp_Mode,
    191                                                *filterOrNullForBicubic);
    192         } else {
    193             GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic);
    194             return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
    195         }
    196     } else {
    197         if (kDomain_DomainMode == domainMode) {
    198             return GrBicubicEffect::Make(std::move(proxy), textureMatrix, domain);
    199         } else {
    200             static const GrSamplerState::WrapMode kClampClamp[] = {
    201                     GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
    202             return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp);
    203         }
    204     }
    205 }
    206