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