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
CopyOnGpu(GrContext * context,sk_sp<GrTextureProxy> inputProxy,const SkIRect * subset,const CopyParams & copyParams)19 sk_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 */
DetermineDomainMode(const SkRect & constraintRect,FilterConstraint filterConstraint,bool coordsLimitedToConstraintRect,GrTextureProxy * proxy,const SkIRect * contentRect,const GrSamplerParams::FilterMode * filterModeOrNullForBicubic,SkRect * domainRect)91 GrTextureProducer::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
CreateFragmentProcessorForDomainAndFilter(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & textureMatrix,DomainMode domainMode,const SkRect & domain,const GrSamplerParams::FilterMode * filterOrNullForBicubic)226 sk_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