1 /*
2 * Copyright 2022 Google Inc.
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 #include "include/core/SkBlendMode.h"
8 #include "include/core/SkBlender.h"
9 #include "include/core/SkData.h"
10 #include "include/core/SkRefCnt.h"
11 #include "include/core/SkString.h"
12 #include "include/effects/SkRuntimeEffect.h"
13 #include "modules/skottie/include/Skottie.h"
14 #include "modules/skottie/src/SkottieJson.h"
15 #include "modules/skottie/src/SkottiePriv.h"
16 #include "modules/sksg/include/SkSGRenderEffect.h"
17 #include "modules/sksg/include/SkSGRenderNode.h"
18 #include "src/utils/SkJSON.h"
19
20 #include <array>
21 #include <cstddef>
22 #include <utility>
23
24 namespace skottie::internal {
25
26 namespace {
27
28 enum CustomBlenders {
29 HARDMIX = 17,
30 };
31
hardMix()32 static sk_sp<SkBlender> hardMix() {
33 static SkRuntimeEffect* hardMixEffect = []{
34 const char hardMix[] =
35 "half4 main(half4 src, half4 dst) {"
36 "src.rgb = unpremul(src).rgb + unpremul(dst).rgb;"
37 "src.rgb = min(floor(src.rgb), 1) * src.a;"
38
39 "return src + (1 - src.a)*dst;"
40 "}"
41 ;
42 auto result = SkRuntimeEffect::MakeForBlender(SkString(hardMix));
43 return result.effect.release();
44 }();
45 return hardMixEffect->makeBlender(nullptr);
46 }
47
get_blender(const skjson::ObjectValue & jobject,const AnimationBuilder * abuilder)48 static sk_sp<SkBlender> get_blender(const skjson::ObjectValue& jobject,
49 const AnimationBuilder* abuilder) {
50 static constexpr SkBlendMode kBlendModeMap[] = {
51 SkBlendMode::kSrcOver, // 0:'normal'
52 SkBlendMode::kMultiply, // 1:'multiply'
53 SkBlendMode::kScreen, // 2:'screen'
54 SkBlendMode::kOverlay, // 3:'overlay
55 SkBlendMode::kDarken, // 4:'darken'
56 SkBlendMode::kLighten, // 5:'lighten'
57 SkBlendMode::kColorDodge, // 6:'color-dodge'
58 SkBlendMode::kColorBurn, // 7:'color-burn'
59 SkBlendMode::kHardLight, // 8:'hard-light'
60 SkBlendMode::kSoftLight, // 9:'soft-light'
61 SkBlendMode::kDifference, // 10:'difference'
62 SkBlendMode::kExclusion, // 11:'exclusion'
63 SkBlendMode::kHue, // 12:'hue'
64 SkBlendMode::kSaturation, // 13:'saturation'
65 SkBlendMode::kColor, // 14:'color'
66 SkBlendMode::kLuminosity, // 15:'luminosity'
67 SkBlendMode::kPlus, // 16:'add'
68 };
69
70 const size_t mode = ParseDefault<size_t>(jobject["bm"], 0);
71
72 // Special handling of src-over, so we can detect the trivial/no-fancy-blending case
73 // (a null blender is equivalent to src-over).
74 if (!mode) {
75 return nullptr;
76 }
77
78 // Modes that are expressible as SkBlendMode.
79 if (mode < std::size(kBlendModeMap)) {
80 return SkBlender::Mode(kBlendModeMap[mode]);
81 }
82
83 // Modes that require custom blenders.
84 switch (mode)
85 {
86 case HARDMIX:
87 return hardMix();
88 default:
89 break;
90 }
91
92 abuilder->log(Logger::Level::kWarning, &jobject, "Unsupported blend mode %zu\n", mode);
93 return nullptr;
94 }
95
96 } // namespace
97
attachBlendMode(const skjson::ObjectValue & jobject,sk_sp<sksg::RenderNode> child) const98 sk_sp<sksg::RenderNode> AnimationBuilder::attachBlendMode(const skjson::ObjectValue& jobject,
99 sk_sp<sksg::RenderNode> child) const {
100 if (auto blender = get_blender(jobject, this)) {
101 fHasNontrivialBlending = true;
102 child = sksg::BlenderEffect::Make(std::move(child), std::move(blender));
103 }
104
105 return child;
106 }
107
108 } // namespace skottie::internal
109