Line data Source code
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 :
19 0 : sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context,
20 : sk_sp<GrTextureProxy> inputProxy,
21 : const SkIRect* subset,
22 : const CopyParams& copyParams) {
23 0 : SkASSERT(!subset || !subset->isEmpty());
24 0 : SkASSERT(context);
25 :
26 0 : GrPixelConfig config = GrMakePixelConfigUncompressed(inputProxy->config());
27 :
28 0 : const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
29 :
30 : sk_sp<GrRenderTargetContext> copyRTC = context->makeRenderTargetContextWithFallback(
31 0 : SkBackingFit::kExact, dstRect.width(), dstRect.height(), config, nullptr);
32 0 : if (!copyRTC) {
33 0 : return nullptr;
34 : }
35 :
36 0 : GrPaint paint;
37 0 : paint.setGammaCorrect(true);
38 :
39 : SkRect localRect;
40 0 : if (subset) {
41 0 : localRect = SkRect::Make(*subset);
42 : } else {
43 0 : localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
44 : }
45 :
46 0 : bool needsDomain = false;
47 0 : if (copyParams.fFilter != GrSamplerParams::kNone_FilterMode) {
48 0 : bool resizing = localRect.width() != dstRect.width() ||
49 0 : localRect.height() != dstRect.height();
50 :
51 0 : if (GrResourceProvider::IsFunctionallyExact(inputProxy.get())) {
52 0 : needsDomain = subset && resizing;
53 : } else {
54 0 : needsDomain = resizing;
55 : }
56 : }
57 :
58 0 : if (needsDomain) {
59 0 : 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 0 : SkASSERT(copyParams.fFilter != GrSamplerParams::kMipMap_FilterMode);
63 : paint.addColorFragmentProcessor(
64 0 : GrTextureDomainEffect::Make(context->resourceProvider(), std::move(inputProxy),
65 : nullptr, SkMatrix::I(),
66 0 : domain, GrTextureDomain::kClamp_Mode, copyParams.fFilter));
67 : } else {
68 0 : GrSamplerParams params(SkShader::kClamp_TileMode, copyParams.fFilter);
69 0 : paint.addColorTextureProcessor(context->resourceProvider(), std::move(inputProxy),
70 0 : nullptr, SkMatrix::I(), params);
71 : }
72 0 : paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
73 :
74 0 : copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
75 0 : localRect);
76 0 : 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 : */
91 0 : 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 0 : const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
100 :
101 0 : SkASSERT(proxyBounds.contains(constraintRect));
102 : // We only expect a content area rect if there is some non-content area.
103 0 : SkASSERT(!contentRect ||
104 : (!contentRect->contains(proxyBounds) &&
105 : proxyBounds.contains(*contentRect) &&
106 : contentRect->contains(constraintRect)));
107 :
108 0 : const bool proxyIsExact = GrResourceProvider::IsFunctionallyExact(proxy);
109 :
110 : // If the constraint rectangle contains the whole proxy then no need for a domain.
111 0 : if (constraintRect.contains(proxyBounds) && proxyIsExact) {
112 0 : return kNoDomain_DomainMode;
113 : }
114 :
115 0 : if (!contentRect && !proxyIsExact) {
116 0 : contentRect = &proxyBounds;
117 : }
118 :
119 0 : 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 0 : if (!restrictFilterToRect && !contentRect && coordsLimitedToConstraintRect) {
125 0 : return kNoDomain_DomainMode;
126 : }
127 :
128 : // Get the domain inset based on sampling mode (or bail if mipped)
129 0 : SkScalar filterHalfWidth = 0.f;
130 0 : if (filterModeOrNullForBicubic) {
131 0 : switch (*filterModeOrNullForBicubic) {
132 : case GrSamplerParams::kNone_FilterMode:
133 0 : if (coordsLimitedToConstraintRect) {
134 0 : return kNoDomain_DomainMode;
135 : } else {
136 0 : filterHalfWidth = 0.f;
137 : }
138 0 : break;
139 : case GrSamplerParams::kBilerp_FilterMode:
140 0 : filterHalfWidth = .5f;
141 0 : break;
142 : case GrSamplerParams::kMipMap_FilterMode:
143 0 : if (restrictFilterToRect || contentRect) {
144 : // No domain can save us here.
145 0 : return kTightCopy_DomainMode;
146 : }
147 0 : return kNoDomain_DomainMode;
148 : }
149 : } else {
150 : // bicubic does nearest filtering internally.
151 0 : 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 0 : if (restrictFilterToRect) {
162 0 : *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
163 0 : } 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 0 : domainRect->setLargest();
170 0 : 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 0 : bool needContentAreaConstraint = false;
174 0 : if (contentRect->fLeft > 0 &&
175 0 : contentRect->fLeft + filterHalfWidth > constraintRect.fLeft) {
176 0 : domainRect->fLeft = contentRect->fLeft + kDomainInset;
177 0 : needContentAreaConstraint = true;
178 : }
179 0 : if (contentRect->fTop > 0 &&
180 0 : contentRect->fTop + filterHalfWidth > constraintRect.fTop) {
181 0 : domainRect->fTop = contentRect->fTop + kDomainInset;
182 0 : needContentAreaConstraint = true;
183 : }
184 0 : if ((!proxyIsExact || contentRect->fRight < proxy->width()) &&
185 0 : contentRect->fRight - filterHalfWidth < constraintRect.fRight) {
186 0 : domainRect->fRight = contentRect->fRight - kDomainInset;
187 0 : needContentAreaConstraint = true;
188 : }
189 0 : if ((!proxyIsExact || contentRect->fBottom < proxy->height()) &&
190 0 : contentRect->fBottom - filterHalfWidth < constraintRect.fBottom) {
191 0 : domainRect->fBottom = contentRect->fBottom - kDomainInset;
192 0 : needContentAreaConstraint = true;
193 : }
194 0 : if (!needContentAreaConstraint) {
195 0 : 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 0 : if (contentRect->fLeft > 0) {
201 0 : domainRect->fLeft = contentRect->fLeft + kDomainInset;
202 : }
203 0 : if (contentRect->fTop > 0) {
204 0 : domainRect->fTop = contentRect->fTop + kDomainInset;
205 : }
206 0 : if (!proxyIsExact || contentRect->fRight < proxy->width()) {
207 0 : domainRect->fRight = contentRect->fRight - kDomainInset;
208 : }
209 0 : if (!proxyIsExact || contentRect->fBottom < proxy->height()) {
210 0 : domainRect->fBottom = contentRect->fBottom - kDomainInset;
211 : }
212 : }
213 : } else {
214 0 : return kNoDomain_DomainMode;
215 : }
216 :
217 0 : if (domainRect->fLeft > domainRect->fRight) {
218 0 : domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
219 : }
220 0 : if (domainRect->fTop > domainRect->fBottom) {
221 0 : domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
222 : }
223 0 : return kDomain_DomainMode;
224 : }
225 :
226 0 : 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 0 : SkASSERT(kTightCopy_DomainMode != domainMode);
235 0 : if (filterOrNullForBicubic) {
236 0 : if (kDomain_DomainMode == domainMode) {
237 0 : return GrTextureDomainEffect::Make(resourceProvider, std::move(proxy),
238 0 : std::move(colorSpaceXform), textureMatrix,
239 : domain, GrTextureDomain::kClamp_Mode,
240 0 : *filterOrNullForBicubic);
241 : } else {
242 0 : GrSamplerParams params(SkShader::kClamp_TileMode, *filterOrNullForBicubic);
243 0 : return GrSimpleTextureEffect::Make(resourceProvider, std::move(proxy),
244 0 : std::move(colorSpaceXform), textureMatrix,
245 0 : params);
246 : }
247 : } else {
248 0 : if (kDomain_DomainMode == domainMode) {
249 0 : return GrBicubicEffect::Make(resourceProvider, std::move(proxy),
250 0 : std::move(colorSpaceXform),
251 0 : textureMatrix, domain);
252 : } else {
253 : static const SkShader::TileMode kClampClamp[] =
254 : { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode };
255 0 : return GrBicubicEffect::Make(resourceProvider, std::move(proxy),
256 0 : std::move(colorSpaceXform),
257 0 : textureMatrix, kClampClamp);
258 : }
259 : }
260 : }
|