blob: f1c8c8dbd0d8a2570df46d8a70c5b794f11e9bc9 [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"
Robert Phillips1afd4cd2018-01-08 13:40:32 -050010#include "GrProxyProvider.h"
Brian Osmane8e54582016-11-28 10:06:27 -050011#include "GrRenderTargetContext.h"
Robert Phillipsd9d84852017-06-09 10:48:29 -040012#include "GrTextureProxy.h"
Mike Reed274218e2018-01-08 15:05:02 -050013#include "SkRectPriv.h"
Brian Osmane8e54582016-11-28 10:06:27 -050014#include "effects/GrBicubicEffect.h"
15#include "effects/GrSimpleTextureEffect.h"
16#include "effects/GrTextureDomain.h"
17
Robert Phillipsb66b42f2017-03-14 08:53:02 -040018sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context,
19 sk_sp<GrTextureProxy> inputProxy,
Greg Daniele1da1d92017-10-06 15:59:27 -040020 const CopyParams& copyParams,
21 bool dstWillRequireMipMaps) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -040022 SkASSERT(context);
23
Robert Phillipsb66b42f2017-03-14 08:53:02 -040024 const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
Greg Daniel45d63032017-10-30 13:41:26 -040025 GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
Robert Phillipsb66b42f2017-03-14 08:53:02 -040026
Brian Salomon366093f2018-02-13 09:25:22 -050027 sk_sp<SkColorSpace> colorSpace;
28 if (GrPixelConfigIsSRGB(inputProxy->config())) {
29 colorSpace = SkColorSpace::MakeSRGB();
30 }
Robert Phillipsdd3b3f42017-04-24 10:57:28 -040031 sk_sp<GrRenderTargetContext> copyRTC = context->makeDeferredRenderTargetContextWithFallback(
Brian Salomon366093f2018-02-13 09:25:22 -050032 SkBackingFit::kExact, dstRect.width(), dstRect.height(), inputProxy->config(),
33 std::move(colorSpace), 1, mipMapped, inputProxy->origin());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040034 if (!copyRTC) {
35 return nullptr;
36 }
37
38 GrPaint paint;
39 paint.setGammaCorrect(true);
40
Greg Danielc77085d2017-11-01 16:38:48 -040041 SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040042
43 bool needsDomain = false;
Brian Salomon2bbdcc42017-09-07 12:36:34 -040044 if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
Robert Phillips4f358be2017-03-23 08:21:00 -040045 bool resizing = localRect.width() != dstRect.width() ||
46 localRect.height() != dstRect.height();
Robert Phillips1afd4cd2018-01-08 13:40:32 -050047 needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get());
Robert Phillipsb66b42f2017-03-14 08:53:02 -040048 }
49
50 if (needsDomain) {
51 const SkRect domain = localRect.makeInset(0.5f, 0.5f);
52 // This would cause us to read values from outside the subset. Surely, the caller knows
53 // better!
Brian Salomon2bbdcc42017-09-07 12:36:34 -040054 SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040055 paint.addColorFragmentProcessor(
Brian Osman2240be92017-10-18 13:15:13 -040056 GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain,
57 GrTextureDomain::kClamp_Mode, copyParams.fFilter));
Robert Phillipsb66b42f2017-03-14 08:53:02 -040058 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -040059 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
Brian Osman2240be92017-10-18 13:15:13 -040060 paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -040061 }
62 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
63
64 copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
65 localRect);
66 return copyRTC->asTextureProxyRef();
67}
68
Brian Osmane8e54582016-11-28 10:06:27 -050069/** Determines whether a texture domain is necessary and if so what domain to use. There are two
70 * rectangles to consider:
Robert Phillips3798c862017-03-27 11:08:16 -040071 * - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
72 * We can *never* allow filtering to cause bleed of pixels outside this rectangle.
73 * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
74 * be contained by the content area. The filterConstraint specifies whether we are allowed to
75 * bleed across this rect.
Brian Osmane8e54582016-11-28 10:06:27 -050076 *
77 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
78 * and whether the coords generated by the draw would all fall within the constraint rect. If the
79 * latter is true we only need to consider whether the filter would extend beyond the rects.
80 */
81GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
Brian Salomon2bbdcc42017-09-07 12:36:34 -040082 const SkRect& constraintRect,
83 FilterConstraint filterConstraint,
84 bool coordsLimitedToConstraintRect,
85 GrTextureProxy* proxy,
Brian Salomon2bbdcc42017-09-07 12:36:34 -040086 const GrSamplerState::Filter* filterModeOrNullForBicubic,
87 SkRect* domainRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -040088 const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());
89
90 SkASSERT(proxyBounds.contains(constraintRect));
Robert Phillips51e7ca32017-03-27 10:14:08 -040091
Robert Phillips1afd4cd2018-01-08 13:40:32 -050092 const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);
Brian Salomon4df00922017-09-07 16:34:11 +000093
Robert Phillips51e7ca32017-03-27 10:14:08 -040094 // If the constraint rectangle contains the whole proxy then no need for a domain.
95 if (constraintRect.contains(proxyBounds) && proxyIsExact) {
96 return kNoDomain_DomainMode;
97 }
98
Robert Phillips51e7ca32017-03-27 10:14:08 -040099 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 // proxy, and we aren't going to generate sample coords outside the constraint rect then we
103 // don't need a domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400104 if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
Robert Phillips51e7ca32017-03-27 10:14:08 -0400105 return kNoDomain_DomainMode;
106 }
107
108 // Get the domain inset based on sampling mode (or bail if mipped)
Brian Salomon4df00922017-09-07 16:34:11 +0000109 SkScalar filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400110 if (filterModeOrNullForBicubic) {
111 switch (*filterModeOrNullForBicubic) {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400112 case GrSamplerState::Filter::kNearest:
Robert Phillips51e7ca32017-03-27 10:14:08 -0400113 if (coordsLimitedToConstraintRect) {
114 return kNoDomain_DomainMode;
Brian Salomon4df00922017-09-07 16:34:11 +0000115 } else {
116 filterHalfWidth = 0.f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400117 }
118 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400119 case GrSamplerState::Filter::kBilerp:
Brian Salomon4df00922017-09-07 16:34:11 +0000120 filterHalfWidth = .5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400121 break;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400122 case GrSamplerState::Filter::kMipMap:
Greg Danielc77085d2017-11-01 16:38:48 -0400123 if (restrictFilterToRect || !proxyIsExact) {
Brian Salomon4df00922017-09-07 16:34:11 +0000124 // No domain can save us here.
125 return kTightCopy_DomainMode;
126 }
127 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400128 }
Brian Salomon4df00922017-09-07 16:34:11 +0000129 } else {
130 // bicubic does nearest filtering internally.
131 filterHalfWidth = 1.5f;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400132 }
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) {
142 *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
Greg Danielc77085d2017-11-01 16:38:48 -0400143 } else if (!proxyIsExact) {
144 // If we got here then: proxy is not exact, the coords are limited to the
Brian Salomon4df00922017-09-07 16:34:11 +0000145 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
Greg Danielc77085d2017-11-01 16:38:48 -0400146 // we check whether the filter would reach across the edge of the proxy.
Brian Salomon4df00922017-09-07 16:34:11 +0000147 // We will only set the sides that are required.
148
Mike Reed274218e2018-01-08 15:05:02 -0500149 *domainRect = SkRectPriv::MakeLargest();
Brian Salomon4df00922017-09-07 16:34:11 +0000150 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;
Greg Danielc77085d2017-11-01 16:38:48 -0400154 if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
155 domainRect->fRight = proxyBounds.fRight - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000156 needContentAreaConstraint = true;
157 }
Greg Danielc77085d2017-11-01 16:38:48 -0400158 if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
159 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Brian Salomon4df00922017-09-07 16:34:11 +0000160 needContentAreaConstraint = true;
161 }
162 if (!needContentAreaConstraint) {
163 return kNoDomain_DomainMode;
164 }
165 } else {
166 // Our sample coords for the texture are allowed to be outside the constraintRect so we
167 // don't consider it when computing the domain.
Greg Danielc77085d2017-11-01 16:38:48 -0400168 domainRect->fRight = proxyBounds.fRight - kDomainInset;
169 domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400170 }
Brian Salomon4df00922017-09-07 16:34:11 +0000171 } else {
172 return kNoDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400173 }
Brian Salomon4df00922017-09-07 16:34:11 +0000174
175 if (domainRect->fLeft > domainRect->fRight) {
176 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
177 }
178 if (domainRect->fTop > domainRect->fBottom) {
179 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
180 }
181 return kDomain_DomainMode;
Robert Phillips51e7ca32017-03-27 10:14:08 -0400182}
183
Brian Salomonaff329b2017-08-11 09:40:37 -0400184std::unique_ptr<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter(
185 sk_sp<GrTextureProxy> proxy,
Brian Salomonaff329b2017-08-11 09:40:37 -0400186 const SkMatrix& textureMatrix,
187 DomainMode domainMode,
188 const SkRect& domain,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400189 const GrSamplerState::Filter* filterOrNullForBicubic) {
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400190 SkASSERT(kTightCopy_DomainMode != domainMode);
191 if (filterOrNullForBicubic) {
192 if (kDomain_DomainMode == domainMode) {
Brian Osman2240be92017-10-18 13:15:13 -0400193 return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
Brian Osman5e341672017-10-18 10:23:18 -0400194 GrTextureDomain::kClamp_Mode,
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400195 *filterOrNullForBicubic);
196 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400197 GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic);
Brian Osman2240be92017-10-18 13:15:13 -0400198 return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400199 }
200 } else {
201 if (kDomain_DomainMode == domainMode) {
Brian Osman5e341672017-10-18 10:23:18 -0400202 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, domain);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400203 } else {
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400204 static const GrSamplerState::WrapMode kClampClamp[] = {
205 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
Brian Osman5e341672017-10-18 10:23:18 -0400206 return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp);
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400207 }
208 }
209}