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