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