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
31SkShaderBase::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
37SkShaderBase::~SkShaderBase() {}
38
39void 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
48SkTCopyOnFirstWrite<SkMatrix>
49SkShaderBase::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
59bool 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
65bool 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
77SkShaderBase::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
93SkShaderBase::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
108SkShaderBase::Context::~Context() {}
109
110bool 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
119SkImage* SkShader::isAImage(SkMatrix* localMatrix, SkTileMode xy[2]) const {
120 return as_SB(this)->onIsAImage(localMatrix, xy);
121}
122
123SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const {
124 return kNone_GradientType;
125}
126
127#if SK_SUPPORT_GPU
128std::unique_ptr<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const GrFPArgs&) const {
129 return nullptr;
130}
131#endif
132
133sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const {
134 return nullptr;
135}
136
137sk_sp<SkShader> SkShaders::Empty() { return sk_make_sp<SkEmptyShader>(); }
138sk_sp<SkShader> SkShaders::Color(SkColor color) { return sk_make_sp<SkColorShader>(color); }
139
140sk_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
147sk_sp<SkShader> SkBitmap::makeShader(const SkMatrix* lm) const {
148 return this->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, lm);
149}
150
151bool SkShaderBase::appendStages(const SkStageRec& rec) const {
152 return this->onAppendStages(rec);
153}
154
155bool 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
198skvm::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
231skvm::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)
241sk_sp<SkShader> SkShaderBase::makeInvertAlpha() const {
242 return this->makeWithColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut));
243}
244
245
246skvm::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
276skvm::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
283sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) {
284 return SkShaders::Empty();
285}
286