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