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