1 | /* |
2 | * Copyright 2006 The Android Open Source Project |
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 "include/core/SkMallocPixelRef.h" |
9 | #include "include/core/SkPaint.h" |
10 | #include "include/core/SkPicture.h" |
11 | #include "include/core/SkScalar.h" |
12 | #include "src/core/SkArenaAlloc.h" |
13 | #include "src/core/SkColorSpacePriv.h" |
14 | #include "src/core/SkColorSpaceXformSteps.h" |
15 | #include "src/core/SkMatrixProvider.h" |
16 | #include "src/core/SkRasterPipeline.h" |
17 | #include "src/core/SkReadBuffer.h" |
18 | #include "src/core/SkTLazy.h" |
19 | #include "src/core/SkVM.h" |
20 | #include "src/core/SkWriteBuffer.h" |
21 | #include "src/shaders/SkBitmapProcShader.h" |
22 | #include "src/shaders/SkColorShader.h" |
23 | #include "src/shaders/SkEmptyShader.h" |
24 | #include "src/shaders/SkPictureShader.h" |
25 | #include "src/shaders/SkShaderBase.h" |
26 | |
27 | #if SK_SUPPORT_GPU |
28 | #include "src/gpu/GrFragmentProcessor.h" |
29 | #endif |
30 | |
31 | SkShaderBase::SkShaderBase(const SkMatrix* localMatrix) |
32 | : fLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I()) { |
33 | // Pre-cache so future calls to fLocalMatrix.getType() are threadsafe. |
34 | (void)fLocalMatrix.getType(); |
35 | } |
36 | |
37 | SkShaderBase::~SkShaderBase() {} |
38 | |
39 | void SkShaderBase::flatten(SkWriteBuffer& buffer) const { |
40 | this->INHERITED::flatten(buffer); |
41 | bool hasLocalM = !fLocalMatrix.isIdentity(); |
42 | buffer.writeBool(hasLocalM); |
43 | if (hasLocalM) { |
44 | buffer.writeMatrix(fLocalMatrix); |
45 | } |
46 | } |
47 | |
48 | SkTCopyOnFirstWrite<SkMatrix> |
49 | SkShaderBase::totalLocalMatrix(const SkMatrix* preLocalMatrix) const { |
50 | SkTCopyOnFirstWrite<SkMatrix> m(fLocalMatrix); |
51 | |
52 | if (preLocalMatrix) { |
53 | m.writable()->preConcat(*preLocalMatrix); |
54 | } |
55 | |
56 | return m; |
57 | } |
58 | |
59 | bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm, |
60 | const SkMatrix* outerLocalMatrix, |
61 | SkMatrix* totalInverse) const { |
62 | return SkMatrix::Concat(ctm, *this->totalLocalMatrix(outerLocalMatrix)).invert(totalInverse); |
63 | } |
64 | |
65 | bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const { |
66 | SkColor storage; |
67 | if (nullptr == colorPtr) { |
68 | colorPtr = &storage; |
69 | } |
70 | if (this->onAsLuminanceColor(colorPtr)) { |
71 | *colorPtr = SkColorSetA(*colorPtr, 0xFF); // we only return opaque |
72 | return true; |
73 | } |
74 | return false; |
75 | } |
76 | |
77 | SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const { |
78 | #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT |
79 | // We always fall back to raster pipeline when perspective is present. |
80 | if (rec.fMatrix->hasPerspective() || |
81 | fLocalMatrix.hasPerspective() || |
82 | (rec.fLocalMatrix && rec.fLocalMatrix->hasPerspective()) || |
83 | !this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr)) { |
84 | return nullptr; |
85 | } |
86 | |
87 | return this->onMakeContext(rec, alloc); |
88 | #else |
89 | return nullptr; |
90 | #endif |
91 | } |
92 | |
93 | SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec) |
94 | : fShader(shader), fCTM(*rec.fMatrix) |
95 | { |
96 | // We should never use a context with perspective. |
97 | SkASSERT(!rec.fMatrix->hasPerspective()); |
98 | SkASSERT(!rec.fLocalMatrix || !rec.fLocalMatrix->hasPerspective()); |
99 | SkASSERT(!shader.getLocalMatrix().hasPerspective()); |
100 | |
101 | // Because the context parameters must be valid at this point, we know that the matrix is |
102 | // invertible. |
103 | SkAssertResult(fShader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &fTotalInverse)); |
104 | |
105 | fPaintAlpha = rec.fPaint->getAlpha(); |
106 | } |
107 | |
108 | SkShaderBase::Context::~Context() {} |
109 | |
110 | bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace) const { |
111 | // In legacy pipelines, shaders always produce premul (or opaque) and the destination is also |
112 | // always premul (or opaque). (And those "or opaque" caveats won't make any difference here.) |
113 | SkAlphaType shaderAT = kPremul_SkAlphaType, |
114 | dstAT = kPremul_SkAlphaType; |
115 | return 0 == SkColorSpaceXformSteps{shaderColorSpace, shaderAT, |
116 | fDstColorSpace, dstAT}.flags.mask(); |
117 | } |
118 | |
119 | SkImage* SkShader::isAImage(SkMatrix* localMatrix, SkTileMode xy[2]) const { |
120 | return as_SB(this)->onIsAImage(localMatrix, xy); |
121 | } |
122 | |
123 | SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const { |
124 | return kNone_GradientType; |
125 | } |
126 | |
127 | #if SK_SUPPORT_GPU |
128 | std::unique_ptr<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const GrFPArgs&) const { |
129 | return nullptr; |
130 | } |
131 | #endif |
132 | |
133 | sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const { |
134 | return nullptr; |
135 | } |
136 | |
137 | sk_sp<SkShader> SkShaders::Empty() { return sk_make_sp<SkEmptyShader>(); } |
138 | sk_sp<SkShader> SkShaders::Color(SkColor color) { return sk_make_sp<SkColorShader>(color); } |
139 | |
140 | sk_sp<SkShader> SkBitmap::makeShader(SkTileMode tmx, SkTileMode tmy, const SkMatrix* lm) const { |
141 | if (lm && !lm->invert(nullptr)) { |
142 | return nullptr; |
143 | } |
144 | return SkMakeBitmapShader(*this, tmx, tmy, lm, kIfMutable_SkCopyPixelsMode); |
145 | } |
146 | |
147 | sk_sp<SkShader> SkBitmap::makeShader(const SkMatrix* lm) const { |
148 | return this->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, lm); |
149 | } |
150 | |
151 | bool SkShaderBase::appendStages(const SkStageRec& rec) const { |
152 | return this->onAppendStages(rec); |
153 | } |
154 | |
155 | bool SkShaderBase::onAppendStages(const SkStageRec& rec) const { |
156 | // SkShader::Context::shadeSpan() handles the paint opacity internally, |
157 | // but SkRasterPipelineBlitter applies it as a separate stage. |
158 | // We skip the internal shadeSpan() step by forcing the paint opaque. |
159 | SkTCopyOnFirstWrite<SkPaint> opaquePaint(rec.fPaint); |
160 | if (rec.fPaint.getAlpha() != SK_AlphaOPAQUE) { |
161 | opaquePaint.writable()->setAlpha(SK_AlphaOPAQUE); |
162 | } |
163 | |
164 | ContextRec cr(*opaquePaint, rec.fMatrixProvider.localToDevice(), rec.fLocalM, rec.fDstColorType, |
165 | sk_srgb_singleton()); |
166 | |
167 | struct CallbackCtx : SkRasterPipeline_CallbackCtx { |
168 | sk_sp<const SkShader> shader; |
169 | Context* ctx; |
170 | }; |
171 | auto cb = rec.fAlloc->make<CallbackCtx>(); |
172 | cb->shader = sk_ref_sp(this); |
173 | cb->ctx = as_SB(this)->makeContext(cr, rec.fAlloc); |
174 | cb->fn = [](SkRasterPipeline_CallbackCtx* self, int active_pixels) { |
175 | auto c = (CallbackCtx*)self; |
176 | int x = (int)c->rgba[0], |
177 | y = (int)c->rgba[1]; |
178 | SkPMColor tmp[SkRasterPipeline_kMaxStride]; |
179 | c->ctx->shadeSpan(x,y, tmp, active_pixels); |
180 | |
181 | for (int i = 0; i < active_pixels; i++) { |
182 | auto rgba_4f = SkPMColor4f::FromPMColor(tmp[i]); |
183 | memcpy(c->rgba + 4*i, rgba_4f.vec(), 4*sizeof(float)); |
184 | } |
185 | }; |
186 | |
187 | if (cb->ctx) { |
188 | rec.fPipeline->append(SkRasterPipeline::seed_shader); |
189 | rec.fPipeline->append(SkRasterPipeline::callback, cb); |
190 | rec.fAlloc->make<SkColorSpaceXformSteps>(sk_srgb_singleton(), kPremul_SkAlphaType, |
191 | rec.fDstCS, kPremul_SkAlphaType) |
192 | ->apply(rec.fPipeline); |
193 | return true; |
194 | } |
195 | return false; |
196 | } |
197 | |
198 | skvm::Color SkShaderBase::program(skvm::Builder* p, |
199 | skvm::Coord device, skvm::Coord local, skvm::Color paint, |
200 | const SkMatrixProvider& matrices, const SkMatrix* localM, |
201 | SkFilterQuality quality, const SkColorInfo& dst, |
202 | skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const { |
203 | // Shader subclasses should always act as if the destination were premul or opaque. |
204 | // SkVMBlitter handles all the coordination of unpremul itself, via premul. |
205 | SkColorInfo tweaked = dst.alphaType() == kUnpremul_SkAlphaType |
206 | ? dst.makeAlphaType(kPremul_SkAlphaType) |
207 | : dst; |
208 | |
209 | // Force opaque alpha for all opaque shaders. |
210 | // |
211 | // This is primarily nice in that we usually have a 1.0f constant splat |
212 | // somewhere in the program anyway, and this will let us drop the work the |
213 | // shader notionally does to produce alpha, p->extract(...), etc. in favor |
214 | // of that simple hoistable splat. |
215 | // |
216 | // More subtly, it makes isOpaque() a parameter to all shader program |
217 | // generation, guaranteeing that is-opaque bit is mixed into the overall |
218 | // shader program hash and blitter Key. This makes it safe for us to use |
219 | // that bit to make decisions when constructing an SkVMBlitter, like doing |
220 | // SrcOver -> Src strength reduction. |
221 | if (auto color = this->onProgram(p, device,local, paint, matrices,localM, quality,tweaked, |
222 | uniforms,alloc)) { |
223 | if (this->isOpaque()) { |
224 | color.a = p->splat(1.0f); |
225 | } |
226 | return color; |
227 | } |
228 | return {}; |
229 | } |
230 | |
231 | skvm::Color SkShaderBase::onProgram(skvm::Builder*, |
232 | skvm::Coord device, skvm::Coord local, skvm::Color paint, |
233 | const SkMatrixProvider&, const SkMatrix* localM, |
234 | SkFilterQuality quality, const SkColorInfo& dst, |
235 | skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const { |
236 | // SkDebugf("cannot onProgram %s\n", this->getTypeName()); |
237 | return {}; |
238 | } |
239 | |
240 | // need a cheap way to invert the alpha channel of a shader (i.e. 1 - a) |
241 | sk_sp<SkShader> SkShaderBase::makeInvertAlpha() const { |
242 | return this->makeWithColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut)); |
243 | } |
244 | |
245 | |
246 | skvm::Coord SkShaderBase::ApplyMatrix(skvm::Builder* p, const SkMatrix& m, |
247 | skvm::Coord coord, skvm::Uniforms* uniforms) { |
248 | skvm::F32 x = coord.x, |
249 | y = coord.y; |
250 | if (m.isIdentity()) { |
251 | // That was easy. |
252 | } else if (m.isTranslate()) { |
253 | x = p->add(x, p->uniformF(uniforms->pushF(m[2]))); |
254 | y = p->add(y, p->uniformF(uniforms->pushF(m[5]))); |
255 | } else if (m.isScaleTranslate()) { |
256 | x = p->mad(x, p->uniformF(uniforms->pushF(m[0])), p->uniformF(uniforms->pushF(m[2]))); |
257 | y = p->mad(y, p->uniformF(uniforms->pushF(m[4])), p->uniformF(uniforms->pushF(m[5]))); |
258 | } else { // Affine or perspective. |
259 | auto dot = [&,x,y](int row) { |
260 | return p->mad(x, p->uniformF(uniforms->pushF(m[3*row+0])), |
261 | p->mad(y, p->uniformF(uniforms->pushF(m[3*row+1])), |
262 | p->uniformF(uniforms->pushF(m[3*row+2])))); |
263 | }; |
264 | x = dot(0); |
265 | y = dot(1); |
266 | if (m.hasPerspective()) { |
267 | x = x * (1.0f / dot(2)); |
268 | y = y * (1.0f / dot(2)); |
269 | } |
270 | } |
271 | return {x,y}; |
272 | } |
273 | |
274 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
275 | |
276 | skvm::Color SkEmptyShader::onProgram(skvm::Builder*, skvm::Coord, skvm::Coord, skvm::Color, |
277 | const SkMatrixProvider&, const SkMatrix*, |
278 | SkFilterQuality, const SkColorInfo&, |
279 | skvm::Uniforms*, SkArenaAlloc*) const { |
280 | return {}; // signal failure |
281 | } |
282 | |
283 | sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) { |
284 | return SkShaders::Empty(); |
285 | } |
286 | |