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