blob: 496dbb2d0be7f01506157b710f54ccc5886174c5 [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"
9
10#include "GrRenderTargetContext.h"
11#include "GrTexture.h"
12#include "effects/GrBicubicEffect.h"
13#include "effects/GrSimpleTextureEffect.h"
14#include "effects/GrTextureDomain.h"
15
16GrTexture* GrTextureProducer::CopyOnGpu(GrTexture* inputTexture, const SkIRect* subset,
17 const CopyParams& copyParams) {
18 SkASSERT(!subset || !subset->isEmpty());
19 GrContext* context = inputTexture->getContext();
20 SkASSERT(context);
21
22 GrPixelConfig config = GrMakePixelConfigUncompressed(inputTexture->config());
23
24 sk_sp<GrRenderTargetContext> copyRTC = context->makeRenderTargetContextWithFallback(
25 SkBackingFit::kExact, copyParams.fWidth, copyParams.fHeight, config, nullptr);
26 if (!copyRTC) {
27 return nullptr;
28 }
29
30 GrPaint paint;
31 paint.setGammaCorrect(true);
32
Brian Osmane8e54582016-11-28 10:06:27 -050033 if (copyParams.fFilter != GrSamplerParams::kNone_FilterMode && subset &&
34 (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) {
35 SkRect domain;
Robert Phillipse98234f2017-01-09 14:23:59 -050036 domain.fLeft = subset->fLeft + 0.5f;
37 domain.fTop = subset->fTop + 0.5f;
38 domain.fRight = subset->fRight - 0.5f;
39 domain.fBottom = subset->fBottom - 0.5f;
Brian Osmane8e54582016-11-28 10:06:27 -050040 // This would cause us to read values from outside the subset. Surely, the caller knows
41 // better!
42 SkASSERT(copyParams.fFilter != GrSamplerParams::kMipMap_FilterMode);
43 paint.addColorFragmentProcessor(
44 GrTextureDomainEffect::Make(inputTexture, nullptr, SkMatrix::I(), domain,
45 GrTextureDomain::kClamp_Mode,
46 copyParams.fFilter));
47 } else {
48 GrSamplerParams params(SkShader::kClamp_TileMode, copyParams.fFilter);
49 paint.addColorTextureProcessor(inputTexture, nullptr, SkMatrix::I(), params);
50 }
51 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
52
53 SkRect localRect;
54 if (subset) {
55 localRect = SkRect::Make(*subset);
Brian Osmane8e54582016-11-28 10:06:27 -050056 } else {
Robert Phillips67c18d62017-01-20 12:44:06 -050057 localRect = SkRect::MakeWH(inputTexture->width(), inputTexture->height());
Brian Osmane8e54582016-11-28 10:06:27 -050058 }
59
60 SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
Brian Salomon82f44312017-01-11 13:42:54 -050061 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
62 localRect);
Brian Osmane8e54582016-11-28 10:06:27 -050063 return copyRTC->asTexture().release();
64}
65
66/** Determines whether a texture domain is necessary and if so what domain to use. There are two
67 * rectangles to consider:
68 * - The first is the content area specified by the texture adjuster. We can *never* allow
69 * filtering to cause bleed of pixels outside this rectangle.
70 * - The second rectangle is the constraint rectangle, which is known to be contained by the
71 * content area. The filterConstraint specifies whether we are allowed to bleed across this
72 * rect.
73 *
74 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
75 * and whether the coords generated by the draw would all fall within the constraint rect. If the
76 * latter is true we only need to consider whether the filter would extend beyond the rects.
77 */
78GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
79 const SkRect& constraintRect,
80 FilterConstraint filterConstraint,
81 bool coordsLimitedToConstraintRect,
82 int texW, int texH,
83 const SkIRect* textureContentArea,
84 const GrSamplerParams::FilterMode* filterModeOrNullForBicubic,
85 SkRect* domainRect) {
86
87 SkASSERT(SkRect::MakeIWH(texW, texH).contains(constraintRect));
88 // We only expect a content area rect if there is some non-content area.
89 SkASSERT(!textureContentArea ||
90 (!textureContentArea->contains(SkIRect::MakeWH(texW, texH)) &&
91 SkRect::Make(*textureContentArea).contains(constraintRect)));
92
93 SkRect textureBounds = SkRect::MakeIWH(texW, texH);
94 // If the src rectangle contains the whole texture then no need for a domain.
95 if (constraintRect.contains(textureBounds)) {
96 return kNoDomain_DomainMode;
97 }
98
99 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
100
101 // If we can filter outside the constraint rect, and there is no non-content area of the
102 // texture, and we aren't going to generate sample coords outside the constraint rect then we
103 // don't need a domain.
104 if (!restrictFilterToRect && !textureContentArea && coordsLimitedToConstraintRect) {
105 return kNoDomain_DomainMode;
106 }
107
108 // Get the domain inset based on sampling mode (or bail if mipped)
109 SkScalar filterHalfWidth = 0.f;
110 if (filterModeOrNullForBicubic) {
111 switch (*filterModeOrNullForBicubic) {
112 case GrSamplerParams::kNone_FilterMode:
113 if (coordsLimitedToConstraintRect) {
114 return kNoDomain_DomainMode;
115 } else {
116 filterHalfWidth = 0.f;
117 }
118 break;
119 case GrSamplerParams::kBilerp_FilterMode:
120 filterHalfWidth = .5f;
121 break;
122 case GrSamplerParams::kMipMap_FilterMode:
123 if (restrictFilterToRect || textureContentArea) {
124 // No domain can save us here.
125 return kTightCopy_DomainMode;
126 }
127 return kNoDomain_DomainMode;
128 }
129 } else {
130 // bicubic does nearest filtering internally.
131 filterHalfWidth = 1.5f;
132 }
133
134 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
135 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
136
137 static const SkScalar kDomainInset = 0.5f;
138 // Figure out the limits of pixels we're allowed to sample from.
139 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
140 // the domain.
141 if (restrictFilterToRect) {
Robert Phillipse98234f2017-01-09 14:23:59 -0500142 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Brian Osmane8e54582016-11-28 10:06:27 -0500143 } else if (textureContentArea) {
144 // If we got here then: there is a textureContentArea, the coords are limited to the
145 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
146 // we check whether the filter would reach across the edge of the content area.
147 // We will only set the sides that are required.
148
149 domainRect->setLargest();
150 if (coordsLimitedToConstraintRect) {
151 // We may be able to use the fact that the texture coords are limited to the constraint
152 // rect in order to avoid having to add a domain.
153 bool needContentAreaConstraint = false;
154 if (textureContentArea->fLeft > 0 &&
155 textureContentArea->fLeft + filterHalfWidth > constraintRect.fLeft) {
156 domainRect->fLeft = textureContentArea->fLeft + kDomainInset;
157 needContentAreaConstraint = true;
158 }
159 if (textureContentArea->fTop > 0 &&
160 textureContentArea->fTop + filterHalfWidth > constraintRect.fTop) {
161 domainRect->fTop = textureContentArea->fTop + kDomainInset;
162 needContentAreaConstraint = true;
163 }
164 if (textureContentArea->fRight < texW &&
165 textureContentArea->fRight - filterHalfWidth < constraintRect.fRight) {
166 domainRect->fRight = textureContentArea->fRight - kDomainInset;
167 needContentAreaConstraint = true;
168 }
169 if (textureContentArea->fBottom < texH &&
170 textureContentArea->fBottom - filterHalfWidth < constraintRect.fBottom) {
171 domainRect->fBottom = textureContentArea->fBottom - kDomainInset;
172 needContentAreaConstraint = true;
173 }
174 if (!needContentAreaConstraint) {
175 return kNoDomain_DomainMode;
176 }
177 } else {
178 // Our sample coords for the texture are allowed to be outside the constraintRect so we
179 // don't consider it when computing the domain.
180 if (textureContentArea->fLeft != 0) {
181 domainRect->fLeft = textureContentArea->fLeft + kDomainInset;
182 }
183 if (textureContentArea->fTop != 0) {
184 domainRect->fTop = textureContentArea->fTop + kDomainInset;
185 }
186 if (textureContentArea->fRight != texW) {
187 domainRect->fRight = textureContentArea->fRight - kDomainInset;
188 }
189 if (textureContentArea->fBottom != texH) {
190 domainRect->fBottom = textureContentArea->fBottom - kDomainInset;
191 }
192 }
193 } else {
194 return kNoDomain_DomainMode;
195 }
196
197 if (domainRect->fLeft > domainRect->fRight) {
198 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
199 }
200 if (domainRect->fTop > domainRect->fBottom) {
201 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
202 }
Brian Osmane8e54582016-11-28 10:06:27 -0500203 return kDomain_DomainMode;
204}
205
206sk_sp<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter(
207 GrTexture* texture,
208 sk_sp<GrColorSpaceXform> colorSpaceXform,
209 const SkMatrix& textureMatrix,
210 DomainMode domainMode,
211 const SkRect& domain,
212 const GrSamplerParams::FilterMode* filterOrNullForBicubic) {
213 SkASSERT(kTightCopy_DomainMode != domainMode);
214 if (filterOrNullForBicubic) {
215 if (kDomain_DomainMode == domainMode) {
216 return GrTextureDomainEffect::Make(texture, std::move(colorSpaceXform), textureMatrix,
217 domain, GrTextureDomain::kClamp_Mode,
218 *filterOrNullForBicubic);
219 } else {
220 GrSamplerParams params(SkShader::kClamp_TileMode, *filterOrNullForBicubic);
221 return GrSimpleTextureEffect::Make(texture, std::move(colorSpaceXform), textureMatrix,
222 params);
223 }
224 } else {
225 if (kDomain_DomainMode == domainMode) {
226 return GrBicubicEffect::Make(texture, std::move(colorSpaceXform), textureMatrix,
227 domain);
228 } else {
229 static const SkShader::TileMode kClampClamp[] =
230 { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode };
231 return GrBicubicEffect::Make(texture, std::move(colorSpaceXform), textureMatrix,
232 kClampClamp);
233 }
234 }
235}