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/SkTLazy.h"
9#include "src/core/SkVM.h"
10#include "src/shaders/SkLocalMatrixShader.h"
11
12#if SK_SUPPORT_GPU
13#include "src/gpu/GrFragmentProcessor.h"
14#endif
15
16#if SK_SUPPORT_GPU
17std::unique_ptr<GrFragmentProcessor> SkLocalMatrixShader::asFragmentProcessor(
18 const GrFPArgs& args) const {
19 return as_SB(fProxyShader)->asFragmentProcessor(
20 GrFPArgs::WithPreLocalMatrix(args, this->getLocalMatrix()));
21}
22#endif
23
24sk_sp<SkFlattenable> SkLocalMatrixShader::CreateProc(SkReadBuffer& buffer) {
25 SkMatrix lm;
26 buffer.readMatrix(&lm);
27 auto baseShader(buffer.readShader());
28 if (!baseShader) {
29 return nullptr;
30 }
31 return baseShader->makeWithLocalMatrix(lm);
32}
33
34void SkLocalMatrixShader::flatten(SkWriteBuffer& buffer) const {
35 buffer.writeMatrix(this->getLocalMatrix());
36 buffer.writeFlattenable(fProxyShader.get());
37}
38
39#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
40SkShaderBase::Context* SkLocalMatrixShader::onMakeContext(
41 const ContextRec& rec, SkArenaAlloc* alloc) const
42{
43 SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix());
44 if (rec.fLocalMatrix) {
45 lm.writable()->preConcat(*rec.fLocalMatrix);
46 }
47
48 ContextRec newRec(rec);
49 newRec.fLocalMatrix = lm;
50
51 return as_SB(fProxyShader)->makeContext(newRec, alloc);
52}
53#endif
54
55SkImage* SkLocalMatrixShader::onIsAImage(SkMatrix* outMatrix, SkTileMode* mode) const {
56 SkMatrix imageMatrix;
57 SkImage* image = fProxyShader->isAImage(&imageMatrix, mode);
58 if (image && outMatrix) {
59 // Local matrix must be applied first so it is on the right side of the concat.
60 *outMatrix = SkMatrix::Concat(imageMatrix, this->getLocalMatrix());
61 }
62
63 return image;
64}
65
66bool SkLocalMatrixShader::onAppendStages(const SkStageRec& rec) const {
67 SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix());
68 if (rec.fLocalM) {
69 lm.writable()->preConcat(*rec.fLocalM);
70 }
71
72 SkStageRec newRec = rec;
73 newRec.fLocalM = lm;
74 return as_SB(fProxyShader)->appendStages(newRec);
75}
76
77
78skvm::Color SkLocalMatrixShader::onProgram(skvm::Builder* p,
79 skvm::F32 x, skvm::F32 y, skvm::Color paint,
80 const SkMatrix& ctm, const SkMatrix* localM,
81 SkFilterQuality quality, const SkColorInfo& dst,
82 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
83 SkTCopyOnFirstWrite<SkMatrix> lm(this->getLocalMatrix());
84 if (localM) {
85 lm.writable()->preConcat(*localM);
86 }
87 return as_SB(fProxyShader)->program(p, x,y, paint, ctm,lm.get(), quality,dst, uniforms,alloc);
88}
89
90sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const {
91 if (localMatrix.isIdentity()) {
92 return sk_ref_sp(const_cast<SkShader*>(this));
93 }
94
95 const SkMatrix* lm = &localMatrix;
96
97 sk_sp<SkShader> baseShader;
98 SkMatrix otherLocalMatrix;
99 sk_sp<SkShader> proxy(as_SB(this)->makeAsALocalMatrixShader(&otherLocalMatrix));
100 if (proxy) {
101 otherLocalMatrix.preConcat(localMatrix);
102 lm = &otherLocalMatrix;
103 baseShader = proxy;
104 } else {
105 baseShader = sk_ref_sp(const_cast<SkShader*>(this));
106 }
107
108 return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm);
109}
110
111////////////////////////////////////////////////////////////////////
112
113/**
114 * Replaces the CTM when used. Created to support clipShaders, which have to be evaluated
115 * using the CTM that was present at the time they were specified (which may be different
116 * from the CTM at the time something is drawn through the clip.
117 */
118class SkCTMShader final : public SkShaderBase {
119public:
120 SkCTMShader(sk_sp<SkShader> proxy, const SkMatrix& ctm)
121 : fProxyShader(std::move(proxy))
122 , fCTM(ctm)
123 {}
124
125 GradientType asAGradient(GradientInfo* info) const override {
126 return fProxyShader->asAGradient(info);
127 }
128
129#if SK_SUPPORT_GPU
130 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
131#endif
132
133protected:
134 void flatten(SkWriteBuffer&) const override { SkASSERT(false); }
135
136#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
137 Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override { return nullptr; }
138#endif
139
140 bool onAppendStages(const SkStageRec& rec) const override {
141 SkStageRec newRec = {
142 rec.fPipeline,
143 rec.fAlloc,
144 rec.fDstColorType,
145 rec.fDstCS,
146 rec.fPaint,
147 rec.fLocalM,
148 fCTM,
149 };
150 return as_SB(fProxyShader)->appendStages(newRec);
151 }
152
153 skvm::Color onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
154 const SkMatrix& ctm, const SkMatrix* localM,
155 SkFilterQuality quality, const SkColorInfo& dst,
156 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
157 return as_SB(fProxyShader)->program(p, x,y,paint, fCTM,localM, quality,dst, uniforms,alloc);
158 }
159
160private:
161 SK_FLATTENABLE_HOOKS(SkCTMShader)
162
163 sk_sp<SkShader> fProxyShader;
164 SkMatrix fCTM;
165
166 typedef SkShaderBase INHERITED;
167};
168
169
170#if SK_SUPPORT_GPU
171std::unique_ptr<GrFragmentProcessor> SkCTMShader::asFragmentProcessor(
172 const GrFPArgs& args) const {
173 return as_SB(fProxyShader)->asFragmentProcessor(
174 GrFPArgs::WithPreLocalMatrix(args, this->getLocalMatrix()));
175}
176#endif
177
178sk_sp<SkFlattenable> SkCTMShader::CreateProc(SkReadBuffer& buffer) {
179 SkASSERT(false);
180 return nullptr;
181}
182
183sk_sp<SkShader> SkShaderBase::makeWithCTM(const SkMatrix& postM) const {
184 return postM.isIdentity() ? sk_ref_sp(this)
185 : sk_sp<SkShader>(new SkCTMShader(sk_ref_sp(this), postM));
186}
187