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