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