xref: /aosp_15_r20/external/skia/src/shaders/SkShaderBase.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2023 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/shaders/SkShaderBase.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlendMode.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorFilter.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkColorSpaceXformSteps.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkEffectPriv.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipeline.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineOpList.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/shaders/SkLocalMatrixShader.h"
18*c8dee2aaSAndroid Build Coastguard Worker 
19*c8dee2aaSAndroid Build Coastguard Worker class SkWriteBuffer;
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker namespace SkShaders {
MatrixRec(const SkMatrix & ctm)22*c8dee2aaSAndroid Build Coastguard Worker MatrixRec::MatrixRec(const SkMatrix& ctm) : fCTM(ctm) {}
23*c8dee2aaSAndroid Build Coastguard Worker 
apply(const SkStageRec & rec,const SkMatrix & postInv) const24*c8dee2aaSAndroid Build Coastguard Worker std::optional<MatrixRec> MatrixRec::apply(const SkStageRec& rec, const SkMatrix& postInv) const {
25*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix total = fPendingLocalMatrix;
26*c8dee2aaSAndroid Build Coastguard Worker     if (!fCTMApplied) {
27*c8dee2aaSAndroid Build Coastguard Worker         total = SkMatrix::Concat(fCTM, total);
28*c8dee2aaSAndroid Build Coastguard Worker     }
29*c8dee2aaSAndroid Build Coastguard Worker     if (!total.invert(&total)) {
30*c8dee2aaSAndroid Build Coastguard Worker         return {};
31*c8dee2aaSAndroid Build Coastguard Worker     }
32*c8dee2aaSAndroid Build Coastguard Worker     total = SkMatrix::Concat(postInv, total);
33*c8dee2aaSAndroid Build Coastguard Worker     if (!fCTMApplied) {
34*c8dee2aaSAndroid Build Coastguard Worker         rec.fPipeline->append(SkRasterPipelineOp::seed_shader);
35*c8dee2aaSAndroid Build Coastguard Worker     }
36*c8dee2aaSAndroid Build Coastguard Worker     // appendMatrix is a no-op if total worked out to identity.
37*c8dee2aaSAndroid Build Coastguard Worker     rec.fPipeline->appendMatrix(rec.fAlloc, total);
38*c8dee2aaSAndroid Build Coastguard Worker     return MatrixRec{fCTM,
39*c8dee2aaSAndroid Build Coastguard Worker                      fTotalLocalMatrix,
40*c8dee2aaSAndroid Build Coastguard Worker                      /*pendingLocalMatrix=*/SkMatrix::I(),
41*c8dee2aaSAndroid Build Coastguard Worker                      fTotalMatrixIsValid,
42*c8dee2aaSAndroid Build Coastguard Worker                      /*ctmApplied=*/true};
43*c8dee2aaSAndroid Build Coastguard Worker }
44*c8dee2aaSAndroid Build Coastguard Worker 
applyForFragmentProcessor(const SkMatrix & postInv) const45*c8dee2aaSAndroid Build Coastguard Worker std::tuple<SkMatrix, bool> MatrixRec::applyForFragmentProcessor(const SkMatrix& postInv) const {
46*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fCTMApplied);
47*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix total;
48*c8dee2aaSAndroid Build Coastguard Worker     if (!fPendingLocalMatrix.invert(&total)) {
49*c8dee2aaSAndroid Build Coastguard Worker         return {SkMatrix::I(), false};
50*c8dee2aaSAndroid Build Coastguard Worker     }
51*c8dee2aaSAndroid Build Coastguard Worker     return {SkMatrix::Concat(postInv, total), true};
52*c8dee2aaSAndroid Build Coastguard Worker }
53*c8dee2aaSAndroid Build Coastguard Worker 
applied() const54*c8dee2aaSAndroid Build Coastguard Worker MatrixRec MatrixRec::applied() const {
55*c8dee2aaSAndroid Build Coastguard Worker     // We mark the CTM as "not applied" because we *never* apply the CTM for FPs. Their starting
56*c8dee2aaSAndroid Build Coastguard Worker     // coords are local, not device, coords.
57*c8dee2aaSAndroid Build Coastguard Worker     return MatrixRec{fCTM,
58*c8dee2aaSAndroid Build Coastguard Worker                      fTotalLocalMatrix,
59*c8dee2aaSAndroid Build Coastguard Worker                      /*pendingLocalMatrix=*/SkMatrix::I(),
60*c8dee2aaSAndroid Build Coastguard Worker                      fTotalMatrixIsValid,
61*c8dee2aaSAndroid Build Coastguard Worker                      /*ctmApplied=*/false};
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker 
concat(const SkMatrix & m) const64*c8dee2aaSAndroid Build Coastguard Worker MatrixRec MatrixRec::concat(const SkMatrix& m) const {
65*c8dee2aaSAndroid Build Coastguard Worker     return {fCTM,
66*c8dee2aaSAndroid Build Coastguard Worker             SkShaderBase::ConcatLocalMatrices(fTotalLocalMatrix, m),
67*c8dee2aaSAndroid Build Coastguard Worker             SkShaderBase::ConcatLocalMatrices(fPendingLocalMatrix, m),
68*c8dee2aaSAndroid Build Coastguard Worker             fTotalMatrixIsValid,
69*c8dee2aaSAndroid Build Coastguard Worker             fCTMApplied};
70*c8dee2aaSAndroid Build Coastguard Worker }
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkShaders
73*c8dee2aaSAndroid Build Coastguard Worker 
74*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////////////
75*c8dee2aaSAndroid Build Coastguard Worker 
76*c8dee2aaSAndroid Build Coastguard Worker SkShaderBase::SkShaderBase() = default;
77*c8dee2aaSAndroid Build Coastguard Worker 
78*c8dee2aaSAndroid Build Coastguard Worker SkShaderBase::~SkShaderBase() = default;
79*c8dee2aaSAndroid Build Coastguard Worker 
flatten(SkWriteBuffer & buffer) const80*c8dee2aaSAndroid Build Coastguard Worker void SkShaderBase::flatten(SkWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); }
81*c8dee2aaSAndroid Build Coastguard Worker 
asLuminanceColor(SkColor4f * colorPtr) const82*c8dee2aaSAndroid Build Coastguard Worker bool SkShaderBase::asLuminanceColor(SkColor4f* colorPtr) const {
83*c8dee2aaSAndroid Build Coastguard Worker     SkColor4f storage;
84*c8dee2aaSAndroid Build Coastguard Worker     if (nullptr == colorPtr) {
85*c8dee2aaSAndroid Build Coastguard Worker         colorPtr = &storage;
86*c8dee2aaSAndroid Build Coastguard Worker     }
87*c8dee2aaSAndroid Build Coastguard Worker     if (this->onAsLuminanceColor(colorPtr)) {
88*c8dee2aaSAndroid Build Coastguard Worker         colorPtr->fA = 1.0f;  // we only return opaque
89*c8dee2aaSAndroid Build Coastguard Worker         return true;
90*c8dee2aaSAndroid Build Coastguard Worker     }
91*c8dee2aaSAndroid Build Coastguard Worker     return false;
92*c8dee2aaSAndroid Build Coastguard Worker }
93*c8dee2aaSAndroid Build Coastguard Worker 
makeContext(const ContextRec & rec,SkArenaAlloc * alloc) const94*c8dee2aaSAndroid Build Coastguard Worker SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
95*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
96*c8dee2aaSAndroid Build Coastguard Worker     // We always fall back to raster pipeline when perspective is present.
97*c8dee2aaSAndroid Build Coastguard Worker     auto totalMatrix = rec.fMatrixRec.totalMatrix();
98*c8dee2aaSAndroid Build Coastguard Worker     if (totalMatrix.hasPerspective() || !totalMatrix.invert(nullptr)) {
99*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
100*c8dee2aaSAndroid Build Coastguard Worker     }
101*c8dee2aaSAndroid Build Coastguard Worker 
102*c8dee2aaSAndroid Build Coastguard Worker     return this->onMakeContext(rec, alloc);
103*c8dee2aaSAndroid Build Coastguard Worker #else
104*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
105*c8dee2aaSAndroid Build Coastguard Worker #endif
106*c8dee2aaSAndroid Build Coastguard Worker }
107*c8dee2aaSAndroid Build Coastguard Worker 
Context(const SkShaderBase & shader,const ContextRec & rec)108*c8dee2aaSAndroid Build Coastguard Worker SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec)
109*c8dee2aaSAndroid Build Coastguard Worker         : fShader(shader) {
110*c8dee2aaSAndroid Build Coastguard Worker     // We should never use a context with perspective.
111*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!rec.fMatrixRec.totalMatrix().hasPerspective());
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker     // Because the context parameters must be valid at this point, we know that the matrix is
114*c8dee2aaSAndroid Build Coastguard Worker     // invertible.
115*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(rec.fMatrixRec.totalInverse(&fTotalInverse));
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     fPaintAlpha = rec.fPaintAlpha;
118*c8dee2aaSAndroid Build Coastguard Worker }
119*c8dee2aaSAndroid Build Coastguard Worker 
~Context()120*c8dee2aaSAndroid Build Coastguard Worker SkShaderBase::Context::~Context() {}
121*c8dee2aaSAndroid Build Coastguard Worker 
isLegacyCompatible(SkColorSpace * shaderColorSpace) const122*c8dee2aaSAndroid Build Coastguard Worker bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace) const {
123*c8dee2aaSAndroid Build Coastguard Worker     // In legacy pipelines, shaders always produce premul (or opaque) and the destination is also
124*c8dee2aaSAndroid Build Coastguard Worker     // always premul (or opaque).  (And those "or opaque" caveats won't make any difference here.)
125*c8dee2aaSAndroid Build Coastguard Worker     SkAlphaType shaderAT = kPremul_SkAlphaType, dstAT = kPremul_SkAlphaType;
126*c8dee2aaSAndroid Build Coastguard Worker     return 0 ==
127*c8dee2aaSAndroid Build Coastguard Worker            SkColorSpaceXformSteps{shaderColorSpace, shaderAT, fDstColorSpace, dstAT}.flags.mask();
128*c8dee2aaSAndroid Build Coastguard Worker }
129*c8dee2aaSAndroid Build Coastguard Worker 
makeAsALocalMatrixShader(SkMatrix *) const130*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const { return nullptr; }
131*c8dee2aaSAndroid Build Coastguard Worker 
appendRootStages(const SkStageRec & rec,const SkMatrix & ctm) const132*c8dee2aaSAndroid Build Coastguard Worker bool SkShaderBase::appendRootStages(const SkStageRec& rec, const SkMatrix& ctm) const {
133*c8dee2aaSAndroid Build Coastguard Worker     return this->appendStages(rec, SkShaders::MatrixRec(ctm));
134*c8dee2aaSAndroid Build Coastguard Worker }
135*c8dee2aaSAndroid Build Coastguard Worker 
makeWithCTM(const SkMatrix & postM) const136*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> SkShaderBase::makeWithCTM(const SkMatrix& postM) const {
137*c8dee2aaSAndroid Build Coastguard Worker     return sk_sp<SkShader>(new SkCTMShader(sk_ref_sp(this), postM));
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker // need a cheap way to invert the alpha channel of a shader (i.e. 1 - a)
makeInvertAlpha() const141*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> SkShaderBase::makeInvertAlpha() const {
142*c8dee2aaSAndroid Build Coastguard Worker     return this->makeWithColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut));
143*c8dee2aaSAndroid Build Coastguard Worker }
144