blob: 8d3549d5d18c8acc8ea9bfc8e1b5f23bb1416705 [file] [log] [blame]
Brian Osmane8e54582016-11-28 10:06:27 -05001/*
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"
Brian Salomonc65aec92017-03-09 09:03:58 -05009#include "GrClip.h"
Robert Phillips1afd4cd2018-01-08 13:40:32 -050010#include "GrProxyProvider.h"
Brian Osmane8e54582016-11-28 10:06:27 -050011#include "GrRenderTargetContext.h"
Robert Phillipsd9d84852017-06-09 10:48:29 -040012#include "GrTextureProxy.h"
Greg Daniel09c94002018-06-08 22:11:51 +000013#include "SkGr.h"
Greg Daniel8e9b4c42018-07-20 10:30:48 -040014#include "SkMipMap.h"
Mike Reed274218e2018-01-08 15:05:02 -050015#include "SkRectPriv.h"
Brian Osmane8e54582016-11-28 10:06:27 -050016#include "effects/GrBicubicEffect.h"
17#include "effects/GrSimpleTextureEffect.h"
18#include "effects/GrTextureDomain.h"
19
Robert Phillipsb66b42f2017-03-14 08:53:02 -040020sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context,
21 sk_sp<GrTextureProxy> inputProxy,
Greg Daniele1da1d92017-10-06 15:59:27 -040022 const CopyParams& copyParams,
23 bool dstWillRequireMipMaps) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -040024 SkASSERT(context);
25
Robert Phillipsb66b42f2017-03-14 08:53:02 -040026 const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
Greg Daniel45d63032017-10-30 13:41:26 -040027 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040028
Greg Daniel09c94002018-06-08 22:11:51 +000029 SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
30
31 bool needsDomain = false;
32 bool resizing = false;
33 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
34 bool resizing = localRect.width() != dstRect.width() ||
35 localRect.height() != dstRect.height();
36 needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get());
37 }
38
39 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
40 dstWillRequireMipMaps) {
41 sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get());
42 if (proxy) {
43 return proxy;
44 }
45 }
46
Robert Phillips0c4b7b12018-03-06 08:20:37 -050047 sk_sp<GrRenderTargetContext> copyRTC =
48 context->contextPriv().makeDeferredRenderTargetContextWithFallback(
Brian Salomon366093f2018-02-13 09:25:22 -050049 SkBackingFit::kExact, dstRect.width(), dstRect.height(), inputProxy->config(),
Brian Osman9363ac42018-06-01 16:10:53 -040050 nullptr, 1, mipMapped, inputProxy->origin());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040051 if (!copyRTC) {
52 return nullptr;
53 }
54
55 GrPaint paint;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040056
57 if (needsDomain) {
58 const SkRect domain = localRect.makeInset(0.5f, 0.5f);
59 // This would cause us to read values from outside the subset. Surely, the caller knows
60 // better!
Brian Salomon2bbdcc42017-09-07 12:36:34 -040061 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040062 paint.addColorFragmentProcessor(
Brian Osman2240be92017-10-18 13:15:13 -040063 GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain,
64 GrTextureDomain::kClamp_Mode, copyParams.fFilter));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040065 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -040066 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
Brian Osman2240be92017-10-18 13:15:13 -040067 paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040068 }
69 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
70
71 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
72 localRect);
73 return copyRTC->asTextureProxyRef();
74}
75
Brian Osmane8e54582016-11-28 10:06:27 -050076/** Determines whether a texture domain is necessary and if so what domain to use. There are two
77 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040078 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
79 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
80 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
81 * be contained by the content area. The filterConstraint specifies whether we are allowed to
82 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050083 *
84 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
85 * and whether the coords generated by the draw would all fall within the constraint rect. If the
86 * latter is true we only need to consider whether the filter would extend beyond the rects.
87 */
88GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -040089 const SkRect& constraintRect,
90 FilterConstraint filterConstraint,
91 bool coordsLimitedToConstraintRect,
92 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040093 const GrSamplerState::Filter* filterModeOrNullForBicubic,
94 SkRect* domainRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -040095 const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
96
97 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -040098
Robert Phillips1afd4cd2018-01-08 13:40:32 -050099 const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);
Brian Salomon4df00922017-09-07 16:34:11 +0000100
Robert Phillips51e7ca32017-03-27 10:14:08 -0400101 // If the constraint rectangle contains the whole proxy then no need for a domain.
102 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
103 return kNoDomain_DomainMode;
104 }
105
Robert Phillips51e7ca32017-03-27 10:14:08 -0400106 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
107
108 // If we can filter outside the constraint rect, and there is no non-content area of the
109 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
110 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400111 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400112 return kNoDomain_DomainMode;
113 }
114
115 // Get the domain inset based on sampling mode (or bail if mipped)
Brian Salomon4df00922017-09-07 16:34:11 +0000116 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400117 if (filterModeOrNullForBicubic) {
118 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400119 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400120 if (coordsLimitedToConstraintRect) {
121 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000122 } else {
123 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400124 }
125 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400126 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000127 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400128 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400129 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400130 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000131 // No domain can save us here.
132 return kTightCopy_DomainMode;
133 }
134 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400135 }
Brian Salomon4df00922017-09-07 16:34:11 +0000136 } else {
137 // bicubic does nearest filtering internally.
138 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400139 }
140
141 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
142 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
143
144 static const SkScalar kDomainInset = 0.5f;
145 // Figure out the limits of pixels we're allowed to sample from.
146 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
147 // the domain.
148 if (restrictFilterToRect) {
149 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Greg Danielc77085d2017-11-01 16:38:48 -0400150 } else if (!proxyIsExact) {
151 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000152 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400153 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000154 // We will only set the sides that are required.
155
Mike Reed274218e2018-01-08 15:05:02 -0500156 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000157 if (coordsLimitedToConstraintRect) {
158 // We may be able to use the fact that the texture coords are limited to the constraint
159 // rect in order to avoid having to add a domain.
160 bool needContentAreaConstraint = false;
Greg Danielc77085d2017-11-01 16:38:48 -0400161 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
162 domainRect->fRight = proxyBounds.fRight - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000163 needContentAreaConstraint = true;
164 }
Greg Danielc77085d2017-11-01 16:38:48 -0400165 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
166 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000167 needContentAreaConstraint = true;
168 }
169 if (!needContentAreaConstraint) {
170 return kNoDomain_DomainMode;
171 }
172 } else {
173 // Our sample coords for the texture are allowed to be outside the constraintRect so we
174 // don't consider it when computing the domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400175 domainRect->fRight = proxyBounds.fRight - kDomainInset;
176 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400177 }
Brian Salomon4df00922017-09-07 16:34:11 +0000178 } else {
179 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400180 }
Brian Salomon4df00922017-09-07 16:34:11 +0000181
182 if (domainRect->fLeft > domainRect->fRight) {
183 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
184 }
185 if (domainRect->fTop > domainRect->fBottom) {
186 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
187 }
188 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400189}
190
Brian Salomonaff329b2017-08-11 09:40:37 -0400191std::unique_ptr<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter(
192 sk_sp<GrTextureProxy> proxy,
Brian Salomonaff329b2017-08-11 09:40:37 -0400193 const SkMatrix& textureMatrix,
194 DomainMode domainMode,
195 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400196 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400197 SkASSERT(kTightCopy_DomainMode != domainMode);
198 if (filterOrNullForBicubic) {
199 if (kDomain_DomainMode == domainMode) {
Brian Osman2240be92017-10-18 13:15:13 -0400200 return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
Brian Osman5e341672017-10-18 10:23:18 -0400201 GrTextureDomain::kClamp_Mode,
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400202 *filterOrNullForBicubic);
203 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400204 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic);
Brian Osman2240be92017-10-18 13:15:13 -0400205 return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400206 }
207 } else {
208 if (kDomain_DomainMode == domainMode) {
Brian Osman5e341672017-10-18 10:23:18 -0400209 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, domain);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400210 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400211 static const GrSamplerState::WrapMode kClampClamp[] = {
212 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
Brian Osman5e341672017-10-18 10:23:18 -0400213 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400214 }
215 }
216}
Brian Salomon2a943df2018-05-04 13:43:19 -0400217
218sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
219 const GrSamplerState& sampler,
220 SkColorSpace* dstColorSpace,
221 sk_sp<SkColorSpace>* proxyColorSpace,
222 SkScalar scaleAdjust[2]) {
223 // Check that the caller pre-initialized scaleAdjust
224 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
225 // Check that if the caller passed nullptr for scaleAdjust that we're in the case where there
226 // can be no scaling.
227 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
228 !sampler.isRepeated()));
229 SkASSERT(scaleAdjust || expectNoScale);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400230
231 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
232 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
233 fContext->contextPriv().caps()->mipMapSupport();
234
Brian Salomon2a943df2018-05-04 13:43:19 -0400235 auto result =
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400236 this->onRefTextureProxyForParams(sampler, dstColorSpace, proxyColorSpace, willBeMipped,
237 scaleAdjust);
Brian Salomon2a943df2018-05-04 13:43:19 -0400238
Greg Daniel022b1e02018-07-20 14:54:00 -0400239 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
240 // maps, unless the config is not copyable.
241 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
242 !fContext->contextPriv().caps()->isConfigCopyable(result->config()));
243
Brian Salomon2a943df2018-05-04 13:43:19 -0400244 // Check that the "no scaling expected" case always returns a proxy of the same size as the
245 // producer.
246 SkASSERT(!result || !expectNoScale ||
247 (result->width() == this->width() && result->height() == this->height()));
248 return result;
249}
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400250
251sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxy(GrMipMapped willNeedMips,
252 SkColorSpace* dstColorSpace,
253 sk_sp<SkColorSpace>* proxyColorSpace) {
254 GrSamplerState::Filter filter =
255 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
256 : GrSamplerState::Filter::kMipMap;
257 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400258
259 int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
260 bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
261 fContext->contextPriv().caps()->mipMapSupport();
262
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400263 auto result =
Greg Daniel8e9b4c42018-07-20 10:30:48 -0400264 this->onRefTextureProxyForParams(sampler, dstColorSpace, proxyColorSpace,
265 willBeMipped, nullptr);
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400266
Greg Daniel022b1e02018-07-20 14:54:00 -0400267 // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
268 // maps, unless the config is not copyable.
269 SkASSERT(!result || !willBeMipped || result->mipMapped() == GrMipMapped::kYes ||
270 !fContext->contextPriv().caps()->isConfigCopyable(result->config()));
271
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400272 // Check that no scaling occured and we returned a proxy of the same size as the producer.
273 SkASSERT(!result || (result->width() == this->width() && result->height() == this->height()));
274 return result;
275}