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 "GrTextureAdjuster.h"
9 #include "GrColorSpaceXform.h"
10 #include "GrContext.h"
11 #include "GrContextPriv.h"
12 #include "GrGpu.h"
13 #include "GrProxyProvider.h"
14 #include "SkGr.h"
15 
16 GrTextureAdjuster::GrTextureAdjuster(GrContext* context, sk_sp<GrTextureProxy> original,
17                                      SkAlphaType alphaType,
18                                      uint32_t uniqueID,
19                                      SkColorSpace* cs)
20     : INHERITED(context, original->width(), original->height(),
21                 GrPixelConfigIsAlphaOnly(original->config()))
22     , fOriginal(std::move(original))
23     , fAlphaType(alphaType)
24     , fColorSpace(cs)
25     , fUniqueID(uniqueID) {}
26 
27 void GrTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) {
28     // Destination color space is irrelevant - we already have a texture so we're just sub-setting
29     GrUniqueKey baseKey;
30     GrMakeKeyFromImageID(&baseKey, fUniqueID, SkIRect::MakeWH(this->width(), this->height()));
31     MakeCopyKeyFromOrigKey(baseKey, params, copyKey);
32 }
33 
34 void GrTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) {
35     // We don't currently have a mechanism for notifications on Images!
36 }
37 
38 sk_sp<GrTextureProxy> GrTextureAdjuster::refTextureProxyCopy(const CopyParams& copyParams,
39                                                              bool willBeMipped) {
40     GrProxyProvider* proxyProvider = fContext->contextPriv().proxyProvider();
41 
42     GrUniqueKey key;
43     this->makeCopyKey(copyParams, &key);
44     sk_sp<GrTextureProxy> cachedCopy;
45     if (key.isValid()) {
46         cachedCopy = proxyProvider->findOrCreateProxyByUniqueKey(key,
47                                                                  this->originalProxy()->origin());
48         if (cachedCopy && (!willBeMipped || GrMipMapped::kYes == cachedCopy->mipMapped())) {
49             return cachedCopy;
50         }
51     }
52 
53     sk_sp<GrTextureProxy> proxy = this->originalProxyRef();
54 
55     sk_sp<GrTextureProxy> copy = CopyOnGpu(fContext, std::move(proxy), copyParams, willBeMipped);
56     if (copy) {
57         if (key.isValid()) {
58             SkASSERT(copy->origin() == this->originalProxy()->origin());
59             if (cachedCopy) {
60                 SkASSERT(GrMipMapped::kYes == copy->mipMapped() &&
61                          GrMipMapped::kNo == cachedCopy->mipMapped());
62                 // If we had a cachedProxy, that means there already is a proxy in the cache which
63                 // matches the key, but it does not have mip levels and we require them. Thus we
64                 // must remove the unique key from that proxy.
65                 SkASSERT(cachedCopy->getUniqueKey() == key);
66                 proxyProvider->removeUniqueKeyFromProxy(cachedCopy.get());
67             }
68             proxyProvider->assignUniqueKeyToProxy(key, copy.get());
69             this->didCacheCopy(key, proxyProvider->contextUniqueID());
70         }
71     }
72     return copy;
73 }
74 
75 sk_sp<GrTextureProxy> GrTextureAdjuster::onRefTextureProxyForParams(
76         const GrSamplerState& params,
77         bool willBeMipped,
78         SkScalar scaleAdjust[2]) {
79     sk_sp<GrTextureProxy> proxy = this->originalProxyRef();
80     CopyParams copyParams;
81 
82     if (!fContext) {
83         // The texture was abandoned.
84         return nullptr;
85     }
86 
87     SkASSERT(this->width() <= fContext->contextPriv().caps()->maxTextureSize() &&
88              this->height() <= fContext->contextPriv().caps()->maxTextureSize());
89 
90     bool needsCopyForMipsOnly = false;
91     if (!params.isRepeated() ||
92         !GrGpu::IsACopyNeededForRepeatWrapMode(fContext->contextPriv().caps(), proxy.get(),
93                                                proxy->width(), proxy->height(), params.filter(),
94                                                &copyParams, scaleAdjust)) {
95         needsCopyForMipsOnly = GrGpu::IsACopyNeededForMips(fContext->contextPriv().caps(),
96                                                            proxy.get(), params.filter(),
97                                                            &copyParams);
98         if (!needsCopyForMipsOnly) {
99             return proxy;
100         }
101     }
102 
103     sk_sp<GrTextureProxy> result = this->refTextureProxyCopy(copyParams, willBeMipped);
104     if (!result && needsCopyForMipsOnly) {
105         // If we were unable to make a copy and we only needed a copy for mips, then we will return
106         // the source texture here and require that the GPU backend is able to fall back to using
107         // bilerp if mips are required.
108         return this->originalProxyRef();
109     }
110     return result;
111 }
112 
113 std::unique_ptr<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor(
114         const SkMatrix& origTextureMatrix,
115         const SkRect& constraintRect,
116         FilterConstraint filterConstraint,
117         bool coordsLimitedToConstraintRect,
118         const GrSamplerState::Filter* filterOrNullForBicubic) {
119     SkMatrix textureMatrix = origTextureMatrix;
120 
121     SkRect domain;
122     GrSamplerState samplerState;
123     if (filterOrNullForBicubic) {
124         samplerState.setFilterMode(*filterOrNullForBicubic);
125     }
126     SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
127     sk_sp<GrTextureProxy> proxy(
128             this->refTextureProxyForParams(samplerState, scaleAdjust));
129     if (!proxy) {
130         return nullptr;
131     }
132     // If we made a copy then we only copied the contentArea, in which case the new texture is all
133     // content.
134     if (proxy.get() != this->originalProxy()) {
135         textureMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
136     }
137 
138     DomainMode domainMode =
139         DetermineDomainMode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
140                             proxy.get(), filterOrNullForBicubic, &domain);
141     if (kTightCopy_DomainMode == domainMode) {
142         // TODO: Copy the texture and adjust the texture matrix (both parts need to consider
143         // non-int constraint rect)
144         // For now: treat as bilerp and ignore what goes on above level 0.
145 
146         // We only expect MIP maps to require a tight copy.
147         SkASSERT(filterOrNullForBicubic &&
148                  GrSamplerState::Filter::kMipMap == *filterOrNullForBicubic);
149         static const GrSamplerState::Filter kBilerp = GrSamplerState::Filter::kBilerp;
150         domainMode =
151             DetermineDomainMode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
152                                 proxy.get(), &kBilerp, &domain);
153         SkASSERT(kTightCopy_DomainMode != domainMode);
154     }
155     SkASSERT(kNoDomain_DomainMode == domainMode ||
156              (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom));
157     return CreateFragmentProcessorForDomainAndFilter(std::move(proxy), textureMatrix, domainMode,
158                                                      domain, filterOrNullForBicubic);
159 }
160