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