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
30SkShaderBase::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
36SkShaderBase::~SkShaderBase() {}
37
38void 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
47SkTCopyOnFirstWrite<SkMatrix>
48SkShaderBase::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
58bool 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
64bool 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
76SkShaderBase::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
92SkShaderBase::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
107SkShaderBase::Context::~Context() {}
108
109bool 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
118SkImage* SkShader::isAImage(SkMatrix* localMatrix, SkTileMode xy[2]) const {
119 return as_SB(this)->onIsAImage(localMatrix, xy);
120}
121
122SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const {
123 return kNone_GradientType;
124}
125
126#if SK_SUPPORT_GPU
127std::unique_ptr<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const GrFPArgs&) const {
128 return nullptr;
129}
130#endif
131
132sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const {
133 return nullptr;
134}
135
136sk_sp<SkShader> SkShaders::Empty() { return sk_make_sp<SkEmptyShader>(); }
137sk_sp<SkShader> SkShaders::Color(SkColor color) { return sk_make_sp<SkColorShader>(color); }
138
139sk_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
146sk_sp<SkShader> SkBitmap::makeShader(const SkMatrix* lm) const {
147 return this->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, lm);
148}
149
150bool SkShaderBase::appendStages(const SkStageRec& rec) const {
151 return this->onAppendStages(rec);
152}
153
154bool 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
196skvm::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
221skvm::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)
230sk_sp<SkShader> SkShaderBase::makeInvertAlpha() const {
231 return this->makeWithColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut));
232}
233
234
235void 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
262skvm::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
268sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) {
269 return SkShaders::Empty();
270}
271