GrTextureProducer.cpp revision 2af746c1e74af30d0fb02d5c35a178e61d152965
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
19sk_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 */
91GrTextureProducer::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
226sk_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