blob: 59962c55b116407be43b130f48b308289a83d8b5 [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"
Mike Reed274218e2018-01-08 15:05:02 -050014#include "SkRectPriv.h"
Brian Osmane8e54582016-11-28 10:06:27 -050015#include "effects/GrBicubicEffect.h"
16#include "effects/GrSimpleTextureEffect.h"
17#include "effects/GrTextureDomain.h"
18
Robert Phillipsb66b42f2017-03-14 08:53:02 -040019sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context,
20 sk_sp<GrTextureProxy> inputProxy,
Greg Daniele1da1d92017-10-06 15:59:27 -040021 const CopyParams& copyParams,
22 bool dstWillRequireMipMaps) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -040023 SkASSERT(context);
24
Robert Phillipsb66b42f2017-03-14 08:53:02 -040025 const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
Greg Daniel45d63032017-10-30 13:41:26 -040026 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040027
Greg Daniel09c94002018-06-08 22:11:51 +000028 SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
29
30 bool needsDomain = false;
31 bool resizing = false;
32 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
33 bool resizing = localRect.width() != dstRect.width() ||
34 localRect.height() != dstRect.height();
35 needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get());
36 }
37
38 if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
39 dstWillRequireMipMaps) {
40 sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get());
41 if (proxy) {
42 return proxy;
43 }
44 }
45
Robert Phillips0c4b7b12018-03-06 08:20:37 -050046 sk_sp<GrRenderTargetContext> copyRTC =
47 context->contextPriv().makeDeferredRenderTargetContextWithFallback(
Brian Salomon366093f2018-02-13 09:25:22 -050048 SkBackingFit::kExact, dstRect.width(), dstRect.height(), inputProxy->config(),
Brian Osman9363ac42018-06-01 16:10:53 -040049 nullptr, 1, mipMapped, inputProxy->origin());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040050 if (!copyRTC) {
51 return nullptr;
52 }
53
54 GrPaint paint;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040055
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!
Brian Salomon2bbdcc42017-09-07 12:36:34 -040060 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040061 paint.addColorFragmentProcessor(
Brian Osman2240be92017-10-18 13:15:13 -040062 GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain,
63 GrTextureDomain::kClamp_Mode, copyParams.fFilter));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040064 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -040065 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
Brian Osman2240be92017-10-18 13:15:13 -040066 paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040067 }
68 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
69
70 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
71 localRect);
72 return copyRTC->asTextureProxyRef();
73}
74
Brian Osmane8e54582016-11-28 10:06:27 -050075/** Determines whether a texture domain is necessary and if so what domain to use. There are two
76 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040077 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
78 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
79 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
80 * be contained by the content area. The filterConstraint specifies whether we are allowed to
81 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050082 *
83 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
84 * and whether the coords generated by the draw would all fall within the constraint rect. If the
85 * latter is true we only need to consider whether the filter would extend beyond the rects.
86 */
87GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -040088 const SkRect& constraintRect,
89 FilterConstraint filterConstraint,
90 bool coordsLimitedToConstraintRect,
91 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040092 const GrSamplerState::Filter* filterModeOrNullForBicubic,
93 SkRect* domainRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -040094 const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
95
96 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -040097
Robert Phillips1afd4cd2018-01-08 13:40:32 -050098 const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);
Brian Salomon4df00922017-09-07 16:34:11 +000099
Robert Phillips51e7ca32017-03-27 10:14:08 -0400100 // If the constraint rectangle contains the whole proxy then no need for a domain.
101 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
102 return kNoDomain_DomainMode;
103 }
104
Robert Phillips51e7ca32017-03-27 10:14:08 -0400105 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
106
107 // If we can filter outside the constraint rect, and there is no non-content area of the
108 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
109 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400110 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400111 return kNoDomain_DomainMode;
112 }
113
114 // Get the domain inset based on sampling mode (or bail if mipped)
Brian Salomon4df00922017-09-07 16:34:11 +0000115 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400116 if (filterModeOrNullForBicubic) {
117 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400118 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400119 if (coordsLimitedToConstraintRect) {
120 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000121 } else {
122 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400123 }
124 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400125 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000126 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400127 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400128 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400129 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000130 // No domain can save us here.
131 return kTightCopy_DomainMode;
132 }
133 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400134 }
Brian Salomon4df00922017-09-07 16:34:11 +0000135 } else {
136 // bicubic does nearest filtering internally.
137 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400138 }
139
140 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
141 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
142
143 static const SkScalar kDomainInset = 0.5f;
144 // Figure out the limits of pixels we're allowed to sample from.
145 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
146 // the domain.
147 if (restrictFilterToRect) {
148 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Greg Danielc77085d2017-11-01 16:38:48 -0400149 } else if (!proxyIsExact) {
150 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000151 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400152 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000153 // We will only set the sides that are required.
154
Mike Reed274218e2018-01-08 15:05:02 -0500155 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000156 if (coordsLimitedToConstraintRect) {
157 // We may be able to use the fact that the texture coords are limited to the constraint
158 // rect in order to avoid having to add a domain.
159 bool needContentAreaConstraint = false;
Greg Danielc77085d2017-11-01 16:38:48 -0400160 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
161 domainRect->fRight = proxyBounds.fRight - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000162 needContentAreaConstraint = true;
163 }
Greg Danielc77085d2017-11-01 16:38:48 -0400164 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
165 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000166 needContentAreaConstraint = true;
167 }
168 if (!needContentAreaConstraint) {
169 return kNoDomain_DomainMode;
170 }
171 } else {
172 // Our sample coords for the texture are allowed to be outside the constraintRect so we
173 // don't consider it when computing the domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400174 domainRect->fRight = proxyBounds.fRight - kDomainInset;
175 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400176 }
Brian Salomon4df00922017-09-07 16:34:11 +0000177 } else {
178 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400179 }
Brian Salomon4df00922017-09-07 16:34:11 +0000180
181 if (domainRect->fLeft > domainRect->fRight) {
182 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
183 }
184 if (domainRect->fTop > domainRect->fBottom) {
185 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
186 }
187 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400188}
189
Brian Salomonaff329b2017-08-11 09:40:37 -0400190std::unique_ptr<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter(
191 sk_sp<GrTextureProxy> proxy,
Brian Salomonaff329b2017-08-11 09:40:37 -0400192 const SkMatrix& textureMatrix,
193 DomainMode domainMode,
194 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400195 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400196 SkASSERT(kTightCopy_DomainMode != domainMode);
197 if (filterOrNullForBicubic) {
198 if (kDomain_DomainMode == domainMode) {
Brian Osman2240be92017-10-18 13:15:13 -0400199 return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
Brian Osman5e341672017-10-18 10:23:18 -0400200 GrTextureDomain::kClamp_Mode,
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400201 *filterOrNullForBicubic);
202 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400203 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic);
Brian Osman2240be92017-10-18 13:15:13 -0400204 return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400205 }
206 } else {
207 if (kDomain_DomainMode == domainMode) {
Brian Osman5e341672017-10-18 10:23:18 -0400208 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, domain);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400209 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400210 static const GrSamplerState::WrapMode kClampClamp[] = {
211 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
Brian Osman5e341672017-10-18 10:23:18 -0400212 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400213 }
214 }
215}
Brian Salomon2a943df2018-05-04 13:43:19 -0400216
217sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
218 const GrSamplerState& sampler,
219 SkColorSpace* dstColorSpace,
220 sk_sp<SkColorSpace>* proxyColorSpace,
221 SkScalar scaleAdjust[2]) {
222 // Check that the caller pre-initialized scaleAdjust
223 SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
224 // Check that if the caller passed nullptr for scaleAdjust that we're in the case where there
225 // can be no scaling.
226 SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
227 !sampler.isRepeated()));
228 SkASSERT(scaleAdjust || expectNoScale);
229 auto result =
230 this->onRefTextureProxyForParams(sampler, dstColorSpace, proxyColorSpace, scaleAdjust);
231
232 // Check that the "no scaling expected" case always returns a proxy of the same size as the
233 // producer.
234 SkASSERT(!result || !expectNoScale ||
235 (result->width() == this->width() && result->height() == this->height()));
236 return result;
237}
Greg Daniel5f4b09d2018-06-12 16:39:59 -0400238
239sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxy(GrMipMapped willNeedMips,
240 SkColorSpace* dstColorSpace,
241 sk_sp<SkColorSpace>* proxyColorSpace) {
242 GrSamplerState::Filter filter =
243 GrMipMapped::kNo == willNeedMips ? GrSamplerState::Filter::kNearest
244 : GrSamplerState::Filter::kMipMap;
245 GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
246 auto result =
247 this->onRefTextureProxyForParams(sampler, dstColorSpace, proxyColorSpace, nullptr);
248
249 // Check that no scaling occured and we returned a proxy of the same size as the producer.
250 SkASSERT(!result || (result->width() == this->width() && result->height() == this->height()));
251 return result;
252}