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