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 "SkGr.h" 14 #include "SkMipMap.h" 15 #include "SkRectPriv.h" 16 #include "effects/GrBicubicEffect.h" 17 #include "effects/GrSimpleTextureEffect.h" 18 #include "effects/GrTextureDomain.h" 19 20 sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context, 21 sk_sp<GrTextureProxy> inputProxy, 22 const CopyParams& copyParams, 23 bool dstWillRequireMipMaps) { 24 SkASSERT(context); 25 26 GrPixelConfig config = GrMakePixelConfigUncompressed(inputProxy->config()); 27 28 const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight); 29 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo; 30 31 SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height()); 32 33 bool needsDomain = false; 34 bool resizing = false; 35 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) { 36 bool resizing = localRect.width() != dstRect.width() || 37 localRect.height() != dstRect.height(); 38 needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get()); 39 } 40 41 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing && 42 dstWillRequireMipMaps) { 43 sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get()); 44 if (proxy) { 45 return proxy; 46 } 47 } 48 49 GrBackendFormat format = inputProxy->backendFormat().makeTexture2D(); 50 if (!format.isValid()) { 51 return nullptr; 52 } 53 54 sk_sp<GrRenderTargetContext> copyRTC = 55 context->contextPriv().makeDeferredRenderTargetContextWithFallback( 56 format, SkBackingFit::kExact, dstRect.width(), dstRect.height(), 57 config, nullptr, 1, mipMapped, inputProxy->origin()); 58 if (!copyRTC) { 59 return nullptr; 60 } 61 62 GrPaint paint; 63 64 if (needsDomain) { 65 const SkRect domain = localRect.makeInset(0.5f, 0.5f); 66 // This would cause us to read values from outside the subset. Surely, the caller knows 67 // better! 68 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap); 69 paint.addColorFragmentProcessor( 70 GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain, 71 GrTextureDomain::kClamp_Mode, copyParams.fFilter)); 72 } else { 73 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter); 74 paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState); 75 } 76 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 77 78 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect, 79 localRect); 80 return copyRTC->asTextureProxyRef(); 81 } 82 83 /** Determines whether a texture domain is necessary and if so what domain to use. There are two 84 * rectangles to consider: 85 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea). 86 * We can *never* allow filtering to cause bleed of pixels outside this rectangle. 87 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to 88 * be contained by the content area. The filterConstraint specifies whether we are allowed to 89 * bleed across this rect. 90 * 91 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type, 92 * and whether the coords generated by the draw would all fall within the constraint rect. If the 93 * latter is true we only need to consider whether the filter would extend beyond the rects. 94 */ 95 GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode( 96 const SkRect& constraintRect, 97 FilterConstraint filterConstraint, 98 bool coordsLimitedToConstraintRect, 99 GrTextureProxy* proxy, 100 const GrSamplerState::Filter* filterModeOrNullForBicubic, 101 SkRect* domainRect) { 102 const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height()); 103 104 SkASSERT(proxyBounds.contains(constraintRect)); 105 106 const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy); 107 108 // If the constraint rectangle contains the whole proxy then no need for a domain. 109 if (constraintRect.contains(proxyBounds) && proxyIsExact) { 110 return kNoDomain_DomainMode; 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 && proxyIsExact && 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 GrSamplerState::Filter::kNearest: 127 if (coordsLimitedToConstraintRect) { 128 return kNoDomain_DomainMode; 129 } else { 130 filterHalfWidth = 0.f; 131 } 132 break; 133 case GrSamplerState::Filter::kBilerp: 134 filterHalfWidth = .5f; 135 break; 136 case GrSamplerState::Filter::kMipMap: 137 if (restrictFilterToRect || !proxyIsExact) { 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 (!proxyIsExact) { 158 // If we got here then: proxy is not exact, 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 proxy. 161 // We will only set the sides that are required. 162 163 *domainRect = SkRectPriv::MakeLargest(); 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 (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) { 169 domainRect->fRight = proxyBounds.fRight - kDomainInset; 170 needContentAreaConstraint = true; 171 } 172 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) { 173 domainRect->fBottom = proxyBounds.fBottom - kDomainInset; 174 needContentAreaConstraint = true; 175 } 176 if (!needContentAreaConstraint) { 177 return kNoDomain_DomainMode; 178 } 179 } else { 180 // Our sample coords for the texture are allowed to be outside the constraintRect so we 181 // don't consider it when computing the domain. 182 domainRect->fRight = proxyBounds.fRight - kDomainInset; 183 domainRect->fBottom = proxyBounds.fBottom - kDomainInset; 184 } 185 } else { 186 return kNoDomain_DomainMode; 187 } 188 189 if (domainRect->fLeft > domainRect->fRight) { 190 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight); 191 } 192 if (domainRect->fTop > domainRect->fBottom) { 193 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom); 194 } 195 return kDomain_DomainMode; 196 } 197 198 std::unique_ptr<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter( 199 sk_sp<GrTextureProxy> proxy, 200 const SkMatrix& textureMatrix, 201 DomainMode domainMode, 202 const SkRect& domain, 203 const GrSamplerState::Filter* filterOrNullForBicubic) { 204 SkASSERT(kTightCopy_DomainMode != domainMode); 205 if (filterOrNullForBicubic) { 206 if (kDomain_DomainMode == domainMode) { 207 return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain, 208 GrTextureDomain::kClamp_Mode, 209 *filterOrNullForBicubic); 210 } else { 211 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic); 212 return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState); 213 } 214 } else { 215 if (kDomain_DomainMode == domainMode) { 216 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, domain); 217 } else { 218 static const GrSamplerState::WrapMode kClampClamp[] = { 219 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp}; 220 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp); 221 } 222 } 223 } 224 225 sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams( 226 const GrSamplerState& sampler, 227 SkScalar scaleAdjust[2]) { 228 // Check that the caller pre-initialized scaleAdjust 229 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1)); 230 // Check that if the caller passed nullptr for scaleAdjust that we're in the case where there 231 // can be no scaling. 232 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap && 233 !sampler.isRepeated())); 234 SkASSERT(scaleAdjust || expectNoScale); 235 236 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height()); 237 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount && 238 fContext->contextPriv().caps()->mipMapSupport(); 239 240 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, scaleAdjust); 241 242 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip 243 // maps, unless the config is not copyable. 244 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes || 245 !fContext->contextPriv().caps()->isConfigCopyable(result->config())); 246 247 // Check that the "no scaling expected" case always returns a proxy of the same size as the 248 // producer. 249 SkASSERT(!result || !expectNoScale || 250 (result->width() == this->width() && result->height() == this->height())); 251 return result; 252 } 253 254 sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxy(GrMipMapped willNeedMips) { 255 GrSamplerState::Filter filter = 256 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest 257 : GrSamplerState::Filter::kMipMap; 258 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter); 259 260 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height()); 261 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount && 262 fContext->contextPriv().caps()->mipMapSupport(); 263 264 auto result = this->onRefTextureProxyForParams(sampler, willBeMipped, nullptr); 265 266 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip 267 // maps, unless the config is not copyable. 268 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes || 269 !fContext->contextPriv().caps()->isConfigCopyable(result->config())); 270 271 // Check that no scaling occured and we returned a proxy of the same size as the producer. 272 SkASSERT(!result || (result->width() == this->width() && result->height() == this->height())); 273 return result; 274 } 275