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