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/base/SkTLazy.h"
9 #include "src/core/SkMatrixProvider.h"
10 #include "src/core/SkVM.h"
11 #include "src/shaders/SkLocalMatrixShader.h"
12
13 #if defined(SK_GANESH)
14 #include "src/gpu/ganesh/GrFPArgs.h"
15 #include "src/gpu/ganesh/GrFragmentProcessor.h"
16 #include "src/gpu/ganesh/effects/GrMatrixEffect.h"
17 #endif
18
19 #if defined(SK_GRAPHITE)
20 #include "src/gpu/graphite/KeyContext.h"
21 #include "src/gpu/graphite/KeyHelpers.h"
22 #include "src/gpu/graphite/PaintParamsKey.h"
23 #endif
24
asGradient(GradientInfo * info,SkMatrix * localMatrix) const25 SkShaderBase::GradientType SkLocalMatrixShader::asGradient(GradientInfo* info,
26 SkMatrix* localMatrix) const {
27 GradientType type = as_SB(fWrappedShader)->asGradient(info, localMatrix);
28 if (type != SkShaderBase::GradientType::kNone && localMatrix) {
29 *localMatrix = ConcatLocalMatrices(fLocalMatrix, *localMatrix);
30 }
31 return type;
32 }
33
34 #if defined(SK_GANESH)
asFragmentProcessor(const GrFPArgs & args,const MatrixRec & mRec) const35 std::unique_ptr<GrFragmentProcessor> SkLocalMatrixShader::asFragmentProcessor(
36 const GrFPArgs& args, const MatrixRec& mRec) const {
37 return as_SB(fWrappedShader)->asFragmentProcessor(args, mRec.concat(fLocalMatrix));
38 }
39 #endif
40
41 #if defined(SK_GRAPHITE)
addToKey(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer) const42 void SkLocalMatrixShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
43 skgpu::graphite::PaintParamsKeyBuilder* builder,
44 skgpu::graphite::PipelineDataGatherer* gatherer) const {
45 using namespace skgpu::graphite;
46
47 LocalMatrixShaderBlock::LMShaderData lmShaderData(fLocalMatrix);
48
49 KeyContextWithLocalMatrix newContext(keyContext, fLocalMatrix);
50
51 LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, &lmShaderData);
52
53 as_SB(fWrappedShader)->addToKey(newContext, builder, gatherer);
54
55 builder->endBlock();
56 }
57 #endif
58
CreateProc(SkReadBuffer & buffer)59 sk_sp<SkFlattenable> SkLocalMatrixShader::CreateProc(SkReadBuffer& buffer) {
60 SkMatrix lm;
61 buffer.readMatrix(&lm);
62 auto baseShader(buffer.readShader());
63 if (!baseShader) {
64 return nullptr;
65 }
66 return baseShader->makeWithLocalMatrix(lm);
67 }
68
flatten(SkWriteBuffer & buffer) const69 void SkLocalMatrixShader::flatten(SkWriteBuffer& buffer) const {
70 buffer.writeMatrix(fLocalMatrix);
71 buffer.writeFlattenable(fWrappedShader.get());
72 }
73
74 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const75 SkShaderBase::Context* SkLocalMatrixShader::onMakeContext(
76 const ContextRec& rec, SkArenaAlloc* alloc) const
77 {
78 SkTCopyOnFirstWrite<SkMatrix> lm(fLocalMatrix);
79 if (rec.fLocalMatrix) {
80 *lm.writable() = ConcatLocalMatrices(*rec.fLocalMatrix, *lm);
81 }
82
83 ContextRec newRec(rec);
84 newRec.fLocalMatrix = lm;
85
86 return as_SB(fWrappedShader)->makeContext(newRec, alloc);
87 }
88 #endif
89
onIsAImage(SkMatrix * outMatrix,SkTileMode * mode) const90 SkImage* SkLocalMatrixShader::onIsAImage(SkMatrix* outMatrix, SkTileMode* mode) const {
91 SkMatrix imageMatrix;
92 SkImage* image = fWrappedShader->isAImage(&imageMatrix, mode);
93 if (image && outMatrix) {
94 *outMatrix = ConcatLocalMatrices(fLocalMatrix, imageMatrix);
95 }
96
97 return image;
98 }
99
appendStages(const SkStageRec & rec,const MatrixRec & mRec) const100 bool SkLocalMatrixShader::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
101 return as_SB(fWrappedShader)->appendStages(rec, mRec.concat(fLocalMatrix));
102 }
103
program(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const MatrixRec & mRec,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const104 skvm::Color SkLocalMatrixShader::program(skvm::Builder* p,
105 skvm::Coord device,
106 skvm::Coord local,
107 skvm::Color paint,
108 const MatrixRec& mRec,
109 const SkColorInfo& dst,
110 skvm::Uniforms* uniforms,
111 SkArenaAlloc* alloc) const {
112 return as_SB(fWrappedShader)->program(p,
113 device,
114 local,
115 paint,
116 mRec.concat(fLocalMatrix),
117 dst,
118 uniforms,
119 alloc);
120 }
121
makeWithLocalMatrix(const SkMatrix & localMatrix) const122 sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const {
123 if (localMatrix.isIdentity()) {
124 return sk_ref_sp(const_cast<SkShader*>(this));
125 }
126
127 const SkMatrix* lm = &localMatrix;
128
129 sk_sp<SkShader> baseShader;
130 SkMatrix otherLocalMatrix;
131 sk_sp<SkShader> proxy = as_SB(this)->makeAsALocalMatrixShader(&otherLocalMatrix);
132 if (proxy) {
133 otherLocalMatrix = SkShaderBase::ConcatLocalMatrices(localMatrix, otherLocalMatrix);
134 lm = &otherLocalMatrix;
135 baseShader = proxy;
136 } else {
137 baseShader = sk_ref_sp(const_cast<SkShader*>(this));
138 }
139
140 return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm);
141 }
142
143 ////////////////////////////////////////////////////////////////////
144
145 /**
146 * Replaces the CTM when used. Created to support clipShaders, which have to be evaluated
147 * using the CTM that was present at the time they were specified (which may be different
148 * from the CTM at the time something is drawn through the clip.
149 */
150 class SkCTMShader final : public SkShaderBase {
151 public:
SkCTMShader(sk_sp<SkShader> proxy,const SkMatrix & ctm)152 SkCTMShader(sk_sp<SkShader> proxy, const SkMatrix& ctm)
153 : fProxyShader(std::move(proxy))
154 , fCTM(ctm)
155 {}
156
asGradient(GradientInfo * info,SkMatrix * localMatrix) const157 GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override {
158 return as_SB(fProxyShader)->asGradient(info, localMatrix);
159 }
160
161 #if defined(SK_GANESH)
162 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
163 const MatrixRec&) const override;
164 #endif
165
166 protected:
flatten(SkWriteBuffer &) const167 void flatten(SkWriteBuffer&) const override { SkASSERT(false); }
168
appendStages(const SkStageRec & rec,const MatrixRec &) const169 bool appendStages(const SkStageRec& rec, const MatrixRec&) const override {
170 return as_SB(fProxyShader)->appendRootStages(rec, fCTM);
171 }
172
program(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const MatrixRec & mRec,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const173 skvm::Color program(skvm::Builder* p,
174 skvm::Coord device,
175 skvm::Coord local,
176 skvm::Color paint,
177 const MatrixRec& mRec,
178 const SkColorInfo& dst,
179 skvm::Uniforms* uniforms,
180 SkArenaAlloc* alloc) const override {
181 return as_SB(fProxyShader)->rootProgram(p, device, paint, fCTM, dst, uniforms, alloc);
182 }
183
184 private:
185 SK_FLATTENABLE_HOOKS(SkCTMShader)
186
187 sk_sp<SkShader> fProxyShader;
188 SkMatrix fCTM;
189
190 using INHERITED = SkShaderBase;
191 };
192
193
194 #if defined(SK_GANESH)
asFragmentProcessor(const GrFPArgs & args,const MatrixRec & mRec) const195 std::unique_ptr<GrFragmentProcessor> SkCTMShader::asFragmentProcessor(const GrFPArgs& args,
196 const MatrixRec& mRec) const {
197 SkMatrix ctmInv;
198 if (!fCTM.invert(&ctmInv)) {
199 return nullptr;
200 }
201
202 auto base = as_SB(fProxyShader)->asRootFragmentProcessor(args, fCTM);
203 if (!base) {
204 return nullptr;
205 }
206
207 // In order for the shader to be evaluated with the original CTM, we explicitly evaluate it
208 // at sk_FragCoord, and pass that through the inverse of the original CTM. This avoids requiring
209 // local coords for the shader and mapping from the draw's local to device and then back.
210 return GrFragmentProcessor::DeviceSpace(GrMatrixEffect::Make(ctmInv, std::move(base)));
211 }
212 #endif
213
CreateProc(SkReadBuffer & buffer)214 sk_sp<SkFlattenable> SkCTMShader::CreateProc(SkReadBuffer& buffer) {
215 SkASSERT(false);
216 return nullptr;
217 }
218
makeWithCTM(const SkMatrix & postM) const219 sk_sp<SkShader> SkShaderBase::makeWithCTM(const SkMatrix& postM) const {
220 return sk_sp<SkShader>(new SkCTMShader(sk_ref_sp(this), postM));
221 }
222