xref: /aosp_15_r20/external/skia/src/shaders/SkBlendShader.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2006 The Android Open Source Project
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/SkBlendShader.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlendMode.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlender.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFlattenable.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkRuntimeEffect.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkArenaAlloc.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBlendModePriv.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBlenderBase.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkEffectPriv.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkKnownRuntimeEffects.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipeline.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineOpContexts.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineOpList.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkReadBuffer.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkWriteBuffer.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/shaders/SkShaderBase.h"
26*c8dee2aaSAndroid Build Coastguard Worker 
27*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
28*c8dee2aaSAndroid Build Coastguard Worker 
CreateProc(SkReadBuffer & buffer)29*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkFlattenable> SkBlendShader::CreateProc(SkReadBuffer& buffer) {
30*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkShader> dst(buffer.readShader());
31*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkShader> src(buffer.readShader());
32*c8dee2aaSAndroid Build Coastguard Worker     if (!buffer.validate(dst && src)) {
33*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
34*c8dee2aaSAndroid Build Coastguard Worker     }
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker     unsigned mode = buffer.read32();
37*c8dee2aaSAndroid Build Coastguard Worker 
38*c8dee2aaSAndroid Build Coastguard Worker     if (mode == kCustom_SkBlendMode) {
39*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkBlender> blender = buffer.readBlender();
40*c8dee2aaSAndroid Build Coastguard Worker         if (buffer.validate(blender != nullptr)) {
41*c8dee2aaSAndroid Build Coastguard Worker             return SkShaders::Blend(std::move(blender), std::move(dst), std::move(src));
42*c8dee2aaSAndroid Build Coastguard Worker         }
43*c8dee2aaSAndroid Build Coastguard Worker     } else {
44*c8dee2aaSAndroid Build Coastguard Worker         if (buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
45*c8dee2aaSAndroid Build Coastguard Worker             return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
46*c8dee2aaSAndroid Build Coastguard Worker         }
47*c8dee2aaSAndroid Build Coastguard Worker     }
48*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
49*c8dee2aaSAndroid Build Coastguard Worker }
50*c8dee2aaSAndroid Build Coastguard Worker 
flatten(SkWriteBuffer & buffer) const51*c8dee2aaSAndroid Build Coastguard Worker void SkBlendShader::flatten(SkWriteBuffer& buffer) const {
52*c8dee2aaSAndroid Build Coastguard Worker     buffer.writeFlattenable(fDst.get());
53*c8dee2aaSAndroid Build Coastguard Worker     buffer.writeFlattenable(fSrc.get());
54*c8dee2aaSAndroid Build Coastguard Worker     buffer.write32((int)fMode);
55*c8dee2aaSAndroid Build Coastguard Worker }
56*c8dee2aaSAndroid Build Coastguard Worker 
57*c8dee2aaSAndroid Build Coastguard Worker // Returns the output of e0, and leaves the output of e1 in r,g,b,a
append_two_shaders(const SkStageRec & rec,const SkShaders::MatrixRec & mRec,SkShader * s0,SkShader * s1)58*c8dee2aaSAndroid Build Coastguard Worker static float* append_two_shaders(const SkStageRec& rec,
59*c8dee2aaSAndroid Build Coastguard Worker                                  const SkShaders::MatrixRec& mRec,
60*c8dee2aaSAndroid Build Coastguard Worker                                  SkShader* s0,
61*c8dee2aaSAndroid Build Coastguard Worker                                  SkShader* s1) {
62*c8dee2aaSAndroid Build Coastguard Worker     struct Storage {
63*c8dee2aaSAndroid Build Coastguard Worker         float fCoords[2 * SkRasterPipeline_kMaxStride];
64*c8dee2aaSAndroid Build Coastguard Worker         float fRes0[4 * SkRasterPipeline_kMaxStride];
65*c8dee2aaSAndroid Build Coastguard Worker     };
66*c8dee2aaSAndroid Build Coastguard Worker     auto storage = rec.fAlloc->make<Storage>();
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker     // Note we cannot simply apply mRec here and then unconditionally store the coordinates. When
69*c8dee2aaSAndroid Build Coastguard Worker     // building for Android Framework it would interrupt the backwards local matrix concatenation if
70*c8dee2aaSAndroid Build Coastguard Worker     // mRec had a pending local matrix and either of the children also had a local matrix.
71*c8dee2aaSAndroid Build Coastguard Worker     // b/256873449
72*c8dee2aaSAndroid Build Coastguard Worker     if (mRec.rasterPipelineCoordsAreSeeded()) {
73*c8dee2aaSAndroid Build Coastguard Worker         rec.fPipeline->append(SkRasterPipelineOp::store_src_rg, storage->fCoords);
74*c8dee2aaSAndroid Build Coastguard Worker     }
75*c8dee2aaSAndroid Build Coastguard Worker     if (!as_SB(s0)->appendStages(rec, mRec)) {
76*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
77*c8dee2aaSAndroid Build Coastguard Worker     }
78*c8dee2aaSAndroid Build Coastguard Worker     rec.fPipeline->append(SkRasterPipelineOp::store_src, storage->fRes0);
79*c8dee2aaSAndroid Build Coastguard Worker 
80*c8dee2aaSAndroid Build Coastguard Worker     if (mRec.rasterPipelineCoordsAreSeeded()) {
81*c8dee2aaSAndroid Build Coastguard Worker         rec.fPipeline->append(SkRasterPipelineOp::load_src_rg, storage->fCoords);
82*c8dee2aaSAndroid Build Coastguard Worker     }
83*c8dee2aaSAndroid Build Coastguard Worker     if (!as_SB(s1)->appendStages(rec, mRec)) {
84*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
85*c8dee2aaSAndroid Build Coastguard Worker     }
86*c8dee2aaSAndroid Build Coastguard Worker     return storage->fRes0;
87*c8dee2aaSAndroid Build Coastguard Worker }
88*c8dee2aaSAndroid Build Coastguard Worker 
appendStages(const SkStageRec & rec,const SkShaders::MatrixRec & mRec) const89*c8dee2aaSAndroid Build Coastguard Worker bool SkBlendShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
90*c8dee2aaSAndroid Build Coastguard Worker     float* res0 = append_two_shaders(rec, mRec, fDst.get(), fSrc.get());
91*c8dee2aaSAndroid Build Coastguard Worker     if (!res0) {
92*c8dee2aaSAndroid Build Coastguard Worker         return false;
93*c8dee2aaSAndroid Build Coastguard Worker     }
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker     rec.fPipeline->append(SkRasterPipelineOp::load_dst, res0);
96*c8dee2aaSAndroid Build Coastguard Worker     SkBlendMode_AppendStages(fMode, rec.fPipeline);
97*c8dee2aaSAndroid Build Coastguard Worker     return true;
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker 
Blend(SkBlendMode mode,sk_sp<SkShader> dst,sk_sp<SkShader> src)100*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> SkShaders::Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
101*c8dee2aaSAndroid Build Coastguard Worker     if (!src || !dst) {
102*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
103*c8dee2aaSAndroid Build Coastguard Worker     }
104*c8dee2aaSAndroid Build Coastguard Worker     switch (mode) {
105*c8dee2aaSAndroid Build Coastguard Worker         case SkBlendMode::kClear:
106*c8dee2aaSAndroid Build Coastguard Worker             return Color(0);
107*c8dee2aaSAndroid Build Coastguard Worker         case SkBlendMode::kDst:
108*c8dee2aaSAndroid Build Coastguard Worker             return dst;
109*c8dee2aaSAndroid Build Coastguard Worker         case SkBlendMode::kSrc:
110*c8dee2aaSAndroid Build Coastguard Worker             return src;
111*c8dee2aaSAndroid Build Coastguard Worker         default:
112*c8dee2aaSAndroid Build Coastguard Worker             break;
113*c8dee2aaSAndroid Build Coastguard Worker     }
114*c8dee2aaSAndroid Build Coastguard Worker     return sk_sp<SkShader>(new SkBlendShader(mode, std::move(dst), std::move(src)));
115*c8dee2aaSAndroid Build Coastguard Worker }
116*c8dee2aaSAndroid Build Coastguard Worker 
Blend(sk_sp<SkBlender> blender,sk_sp<SkShader> dst,sk_sp<SkShader> src)117*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> SkShaders::Blend(sk_sp<SkBlender> blender,
118*c8dee2aaSAndroid Build Coastguard Worker                                  sk_sp<SkShader> dst,
119*c8dee2aaSAndroid Build Coastguard Worker                                  sk_sp<SkShader> src) {
120*c8dee2aaSAndroid Build Coastguard Worker     using namespace SkKnownRuntimeEffects;
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker     if (!src || !dst) {
123*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
124*c8dee2aaSAndroid Build Coastguard Worker     }
125*c8dee2aaSAndroid Build Coastguard Worker     if (!blender) {
126*c8dee2aaSAndroid Build Coastguard Worker         return SkShaders::Blend(SkBlendMode::kSrcOver, std::move(dst), std::move(src));
127*c8dee2aaSAndroid Build Coastguard Worker     }
128*c8dee2aaSAndroid Build Coastguard Worker     if (std::optional<SkBlendMode> mode = as_BB(blender)->asBlendMode()) {
129*c8dee2aaSAndroid Build Coastguard Worker         return sk_make_sp<SkBlendShader>(mode.value(), std::move(dst), std::move(src));
130*c8dee2aaSAndroid Build Coastguard Worker     }
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker     // This isn't a built-in blend mode; we might as well use a runtime effect to evaluate it.
133*c8dee2aaSAndroid Build Coastguard Worker     const SkRuntimeEffect* blendEffect = GetKnownRuntimeEffect(StableKey::kBlend);
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker     SkRuntimeEffect::ChildPtr children[] = {std::move(src), std::move(dst), std::move(blender)};
136*c8dee2aaSAndroid Build Coastguard Worker     return blendEffect->makeShader(/*uniforms=*/{}, children);
137*c8dee2aaSAndroid Build Coastguard Worker }
138*c8dee2aaSAndroid Build Coastguard Worker 
SkRegisterBlendShaderFlattenable()139*c8dee2aaSAndroid Build Coastguard Worker void SkRegisterBlendShaderFlattenable() {
140*c8dee2aaSAndroid Build Coastguard Worker     SK_REGISTER_FLATTENABLE(SkBlendShader);
141*c8dee2aaSAndroid Build Coastguard Worker     // Previous name
142*c8dee2aaSAndroid Build Coastguard Worker     SkFlattenable::Register("SkShader_Blend", SkBlendShader::CreateProc);
143*c8dee2aaSAndroid Build Coastguard Worker }
144