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