• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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/core/SkMatrixProvider.h"
9 #include "src/core/SkTLazy.h"
10 #include "src/core/SkVM.h"
11 #include "src/shaders/SkLocalMatrixShader.h"
12 
13 #if SK_SUPPORT_GPU
14 #include "src/gpu/GrFragmentProcessor.h"
15 #include "src/gpu/effects/GrMatrixEffect.h"
16 #endif
17 
18 #if SK_SUPPORT_GPU
asFragmentProcessor(const GrFPArgs & args) const19 std::unique_ptr<GrFragmentProcessor> SkLocalMatrixShader::asFragmentProcessor(
20         const GrFPArgs& args) const {
21     return as_SB(fProxyShader)->asFragmentProcessor(
22         GrFPArgs::WithPreLocalMatrix(args, this->getLocalMatrix()));
23 }
24 #endif
25 
CreateProc(SkReadBuffer & buffer)26 sk_sp<SkFlattenable> SkLocalMatrixShader::CreateProc(SkReadBuffer& buffer) {
27     SkMatrix lm;
28     buffer.readMatrix(&lm);
29     auto baseShader(buffer.readShader());
30     if (!baseShader) {
31         return nullptr;
32     }
33     return baseShader->makeWithLocalMatrix(lm);
34 }
35 
flatten(SkWriteBuffer & buffer) const36 void SkLocalMatrixShader::flatten(SkWriteBuffer& buffer) const {
37     buffer.writeMatrix(this->getLocalMatrix());
38     buffer.writeFlattenable(fProxyShader.get());
39 }
40 
41 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const42 SkShaderBase::Context* SkLocalMatrixShader::onMakeContext(
43     const ContextRec& rec, SkArenaAlloc* alloc) const
44 {
45     SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix());
46     if (rec.fLocalMatrix) {
47         lm.writable()->preConcat(*rec.fLocalMatrix);
48     }
49 
50     ContextRec newRec(rec);
51     newRec.fLocalMatrix = lm;
52 
53     return as_SB(fProxyShader)->makeContext(newRec, alloc);
54 }
55 #endif
56 
onIsAImage(SkMatrix * outMatrix,SkTileMode * mode) const57 SkImage* SkLocalMatrixShader::onIsAImage(SkMatrix* outMatrix, SkTileMode* mode) const {
58     SkMatrix imageMatrix;
59     SkImage* image = fProxyShader->isAImage(&imageMatrix, mode);
60     if (image && outMatrix) {
61         // Local matrix must be applied first so it is on the right side of the concat.
62         *outMatrix = SkMatrix::Concat(imageMatrix, this->getLocalMatrix());
63     }
64 
65     return image;
66 }
67 
onAppendStages(const SkStageRec & rec) const68 bool SkLocalMatrixShader::onAppendStages(const SkStageRec& rec) const {
69     SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix());
70     if (rec.fLocalM) {
71         lm.writable()->preConcat(*rec.fLocalM);
72     }
73 
74     SkStageRec newRec = rec;
75     newRec.fLocalM = lm;
76     return as_SB(fProxyShader)->appendStages(newRec);
77 }
78 
79 
onProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const80 skvm::Color SkLocalMatrixShader::onProgram(skvm::Builder* p,
81                                            skvm::Coord device, skvm::Coord local, skvm::Color paint,
82                                            const SkMatrixProvider& matrices, const SkMatrix* localM,
83                                            const SkColorInfo& dst,
84                                            skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
85     SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix());
86     if (localM) {
87         lm.writable()->preConcat(*localM);
88     }
89     return as_SB(fProxyShader)->program(p, device,local, paint,
90                                         matrices,lm.get(), dst,
91                                         uniforms,alloc);
92 }
93 
makeWithLocalMatrix(const SkMatrix & localMatrix) const94 sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const {
95     if (localMatrix.isIdentity()) {
96         return sk_ref_sp(const_cast<SkShader*>(this));
97     }
98 
99     const SkMatrix* lm = &localMatrix;
100 
101     sk_sp<SkShader> baseShader;
102     SkMatrix otherLocalMatrix;
103     sk_sp<SkShader> proxy(as_SB(this)->makeAsALocalMatrixShader(&otherLocalMatrix));
104     if (proxy) {
105         otherLocalMatrix.preConcat(localMatrix);
106         lm = &otherLocalMatrix;
107         baseShader = proxy;
108     } else {
109         baseShader = sk_ref_sp(const_cast<SkShader*>(this));
110     }
111 
112     return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm);
113 }
114 
115 ////////////////////////////////////////////////////////////////////
116 
117 /**
118  *  Replaces the CTM when used. Created to support clipShaders, which have to be evaluated
119  *  using the CTM that was present at the time they were specified (which may be different
120  *  from the CTM at the time something is drawn through the clip.
121  */
122 class SkCTMShader final : public SkShaderBase {
123 public:
SkCTMShader(sk_sp<SkShader> proxy,const SkMatrix & ctm)124     SkCTMShader(sk_sp<SkShader> proxy, const SkMatrix& ctm)
125     : fProxyShader(std::move(proxy))
126     , fCTM(ctm)
127     {}
128 
asAGradient(GradientInfo * info) const129     GradientType asAGradient(GradientInfo* info) const override {
130         return fProxyShader->asAGradient(info);
131     }
132 
133 #if SK_SUPPORT_GPU
134     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
135 #endif
136 
137 protected:
flatten(SkWriteBuffer &) const138     void flatten(SkWriteBuffer&) const override { SkASSERT(false); }
139 
140 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
onMakeContext(const ContextRec &,SkArenaAlloc *) const141     Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override { return nullptr; }
142 #endif
143 
onAppendStages(const SkStageRec & rec) const144     bool onAppendStages(const SkStageRec& rec) const override {
145         SkOverrideDeviceMatrixProvider matrixProvider(rec.fMatrixProvider, fCTM);
146         SkStageRec newRec = {
147             rec.fPipeline,
148             rec.fAlloc,
149             rec.fDstColorType,
150             rec.fDstCS,
151             rec.fPaint,
152             rec.fLocalM,
153             matrixProvider,
154         };
155         return as_SB(fProxyShader)->appendStages(newRec);
156     }
157 
onProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const158     skvm::Color onProgram(skvm::Builder* p,
159                           skvm::Coord device, skvm::Coord local, skvm::Color paint,
160                           const SkMatrixProvider& matrices, const SkMatrix* localM,
161                           const SkColorInfo& dst,
162                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
163         SkOverrideDeviceMatrixProvider matrixProvider(matrices, fCTM);
164         return as_SB(fProxyShader)->program(p, device,local, paint,
165                                             matrixProvider,localM, dst,
166                                             uniforms,alloc);
167     }
168 
169 private:
170     SK_FLATTENABLE_HOOKS(SkCTMShader)
171 
172     sk_sp<SkShader> fProxyShader;
173     SkMatrix        fCTM;
174 
175     using INHERITED = SkShaderBase;
176 };
177 
178 
179 #if SK_SUPPORT_GPU
asFragmentProcessor(const GrFPArgs & args) const180 std::unique_ptr<GrFragmentProcessor> SkCTMShader::asFragmentProcessor(
181         const GrFPArgs& args) const {
182     SkMatrix ctmInv;
183     if (!fCTM.invert(&ctmInv)) {
184         return nullptr;
185     }
186 
187     auto ctmProvider = SkOverrideDeviceMatrixProvider(args.fMatrixProvider, fCTM);
188     auto base = as_SB(fProxyShader)->asFragmentProcessor(
189         GrFPArgs::WithPreLocalMatrix(args.withNewMatrixProvider(ctmProvider),
190                                      this->getLocalMatrix()));
191     if (!base) {
192         return nullptr;
193     }
194 
195     // In order for the shader to be evaluated with the original CTM, we explicitly evaluate it
196     // at sk_FragCoord, and pass that through the inverse of the original CTM. This avoids requiring
197     // local coords for the shader and mapping from the draw's local to device and then back.
198     return GrFragmentProcessor::DeviceSpace(GrMatrixEffect::Make(ctmInv, std::move(base)));
199 }
200 #endif
201 
CreateProc(SkReadBuffer & buffer)202 sk_sp<SkFlattenable> SkCTMShader::CreateProc(SkReadBuffer& buffer) {
203     SkASSERT(false);
204     return nullptr;
205 }
206 
makeWithCTM(const SkMatrix & postM) const207 sk_sp<SkShader> SkShaderBase::makeWithCTM(const SkMatrix& postM) const {
208     return sk_sp<SkShader>(new SkCTMShader(sk_ref_sp(this), postM));
209 }
210