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 "src/gpu/GrTextureProducer.h"
9
10#include "include/private/GrRecordingContext.h"
11#include "src/core/SkMipMap.h"
12#include "src/core/SkRectPriv.h"
13#include "src/gpu/GrClip.h"
14#include "src/gpu/GrContextPriv.h"
15#include "src/gpu/GrProxyProvider.h"
16#include "src/gpu/GrRecordingContextPriv.h"
17#include "src/gpu/GrRenderTargetContext.h"
18#include "src/gpu/GrTextureProxy.h"
19#include "src/gpu/SkGr.h"
20#include "src/gpu/effects/GrBicubicEffect.h"
21#include "src/gpu/effects/GrTextureDomain.h"
22#include "src/gpu/effects/GrTextureEffect.h"
23
24/** Determines whether a texture domain is necessary and if so what domain to use. There are two
25 * rectangles to consider:
26 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
27 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
28 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
29 * be contained by the content area. The filterConstraint specifies whether we are allowed to
30 * bleed across this rect.
31 *
32 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
33 * and whether the coords generated by the draw would all fall within the constraint rect. If the
34 * latter is true we only need to consider whether the filter would extend beyond the rects.
35 */
36GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
37 const SkRect& constraintRect,
38 FilterConstraint filterConstraint,
39 bool coordsLimitedToConstraintRect,
40 GrSurfaceProxy* proxy,
41 const GrSamplerState::Filter* filterModeOrNullForBicubic,
42 SkRect* domainRect) {
43 const SkIRect proxyBounds = SkIRect::MakeSize(proxy->dimensions());
44
45 SkASSERT(proxyBounds.contains(constraintRect));
46
47 const bool proxyIsExact = proxy->isFunctionallyExact();
48
49 // If the constraint rectangle contains the whole proxy then no need for a domain.
50 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
51 return kNoDomain_DomainMode;
52 }
53
54 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
55
56 // If we can filter outside the constraint rect, and there is no non-content area of the
57 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
58 // don't need a domain.
59 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
60 return kNoDomain_DomainMode;
61 }
62
63 // Get the domain inset based on sampling mode (or bail if mipped). This is used
64 // to evaluate whether we will read outside a non-exact proxy's dimensions.
65 // TODO: Let GrTextureEffect handle this.
66 SkScalar filterHalfWidth = 0.f;
67 if (filterModeOrNullForBicubic) {
68 switch (*filterModeOrNullForBicubic) {
69 case GrSamplerState::Filter::kNearest:
70 if (coordsLimitedToConstraintRect) {
71 return kNoDomain_DomainMode;
72 } else {
73 filterHalfWidth = 0.f;
74 }
75 break;
76 case GrSamplerState::Filter::kBilerp:
77 filterHalfWidth = .5f;
78 break;
79 case GrSamplerState::Filter::kMipMap:
80 if (restrictFilterToRect || !proxyIsExact) {
81 // No domain can save us here.
82 return kTightCopy_DomainMode;
83 }
84 return kNoDomain_DomainMode;
85 }
86 } else {
87 // bicubic does nearest filtering internally.
88 filterHalfWidth = 1.5f;
89 }
90
91 if (restrictFilterToRect) {
92 *domainRect = constraintRect;
93 } else if (!proxyIsExact) {
94 // If we got here then: proxy is not exact, the coords are limited to the
95 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
96 // we check whether the filter would reach across the edge of the proxy.
97 // We will only set the sides that are required.
98
99 *domainRect = SkRectPriv::MakeLargest();
100 if (coordsLimitedToConstraintRect) {
101 // We may be able to use the fact that the texture coords are limited to the constraint
102 // rect in order to avoid having to add a domain.
103 bool needContentAreaConstraint = false;
104 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
105 domainRect->fRight = proxyBounds.fRight;
106 needContentAreaConstraint = true;
107 }
108 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
109 domainRect->fBottom = proxyBounds.fBottom;
110 needContentAreaConstraint = true;
111 }
112 if (!needContentAreaConstraint) {
113 return kNoDomain_DomainMode;
114 }
115 }
116 } else {
117 return kNoDomain_DomainMode;
118 }
119
120 if (!filterModeOrNullForBicubic) {
121 // Bicubic doesn't yet rely on GrTextureEffect to do this insetting.
122 domainRect->inset(0.5f, 0.5f);
123 if (domainRect->fLeft > domainRect->fRight) {
124 domainRect->fLeft = domainRect->fRight =
125 SkScalarAve(domainRect->fLeft, domainRect->fRight);
126 }
127 if (domainRect->fTop > domainRect->fBottom) {
128 domainRect->fTop = domainRect->fBottom =
129 SkScalarAve(domainRect->fTop, domainRect->fBottom);
130 }
131 }
132
133 return kDomain_DomainMode;
134}
135
136std::unique_ptr<GrFragmentProcessor> GrTextureProducer::createFragmentProcessorForSubsetAndFilter(
137 GrSurfaceProxyView view,
138 const SkMatrix& textureMatrix,
139 DomainMode domainMode,
140 const SkRect& domain,
141 GrSamplerState::WrapMode wrapX,
142 GrSamplerState::WrapMode wrapY,
143 const GrSamplerState::Filter* filterOrNullForBicubic) {
144 SkASSERT(kTightCopy_DomainMode != domainMode);
145 SkASSERT(view.asTextureProxy());
146 const auto& caps = *fContext->priv().caps();
147 SkAlphaType srcAlphaType = this->alphaType();
148 if (filterOrNullForBicubic) {
149 GrSamplerState samplerState(wrapX, wrapY, *filterOrNullForBicubic);
150 if (kNoDomain_DomainMode == domainMode) {
151 return GrTextureEffect::Make(std::move(view), srcAlphaType, textureMatrix, samplerState,
152 caps);
153 }
154 return GrTextureEffect::MakeSubset(std::move(view), srcAlphaType, textureMatrix,
155 samplerState, domain, caps);
156 } else {
157
158 static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
159 const auto& caps = *fContext->priv().caps();
160 if (kDomain_DomainMode == domainMode) {
161 return GrBicubicEffect::MakeSubset(std::move(view), srcAlphaType, textureMatrix, wrapX,
162 wrapY, domain, kDir, caps);
163 } else {
164 return GrBicubicEffect::Make(std::move(view), srcAlphaType, textureMatrix, wrapX, wrapY,
165 kDir, caps);
166 }
167 }
168}
169
170GrSurfaceProxyView GrTextureProducer::view(GrMipMapped mipMapped) {
171 const GrCaps* caps = this->context()->priv().caps();
172 // Sanitize the MIP map request.
173 if (mipMapped == GrMipMapped::kYes) {
174 if ((this->width() == 1 && this->height() == 1) || !caps->mipMapSupport()) {
175 mipMapped = GrMipMapped::kNo;
176 }
177 }
178 auto result = this->onView(mipMapped);
179 // Check to make sure if we requested MIPs that the returned texture has MIP maps or the format
180 // is not copyable.
181 SkASSERT(!result || mipMapped == GrMipMapped::kNo ||
182 result.asTextureProxy()->mipMapped() == GrMipMapped::kYes ||
183 !caps->isFormatCopyable(result.proxy()->backendFormat()));
184 return result;
185}
186
187GrSurfaceProxyView GrTextureProducer::view(GrSamplerState::Filter filter) {
188 auto mipMapped = filter == GrSamplerState::Filter::kMipMap ? GrMipMapped::kYes
189 : GrMipMapped::kNo;
190 return this->view(mipMapped);
191}
192