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