1 | /* |
2 | * Copyright 2019 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 | |
8 | #include "modules/skottie/src/effects/Effects.h" |
9 | |
10 | #include "modules/skottie/src/SkottieValue.h" |
11 | #include "modules/sksg/include/SkSGColorFilter.h" |
12 | #include "src/utils/SkJSON.h" |
13 | |
14 | namespace skottie { |
15 | namespace internal { |
16 | |
17 | namespace { |
18 | |
19 | class InvertEffectAdapter final : public AnimatablePropertyContainer { |
20 | public: |
21 | static sk_sp<InvertEffectAdapter> Make(const skjson::ArrayValue& jprops, |
22 | sk_sp<sksg::RenderNode> layer, |
23 | const AnimationBuilder* abuilder) { |
24 | return sk_sp<InvertEffectAdapter>( |
25 | new InvertEffectAdapter(jprops, std::move(layer), abuilder)); |
26 | } |
27 | |
28 | const sk_sp<sksg::ExternalColorFilter>& node() const { return fColorFilter; } |
29 | |
30 | private: |
31 | InvertEffectAdapter(const skjson::ArrayValue& jprops, |
32 | sk_sp<sksg::RenderNode> layer, |
33 | const AnimationBuilder* abuilder) |
34 | : fColorFilter(sksg::ExternalColorFilter::Make(std::move(layer))) { |
35 | enum : size_t { |
36 | kChannel_Index = 0, |
37 | }; |
38 | |
39 | EffectBinder(jprops, *abuilder, this).bind(kChannel_Index, fChannel); |
40 | } |
41 | |
42 | void onSync() override { |
43 | struct STColorMatrix { |
44 | std::array<float,4> scale, |
45 | trans; |
46 | bool hsla; |
47 | }; |
48 | |
49 | const auto stcm = [this]() -> STColorMatrix { |
50 | // https://helpx.adobe.com/after-effects/using/channel-effects.html#invert_effect |
51 | enum : uint8_t { |
52 | kRGB_Channel = 1, |
53 | kR_Channel = 2, |
54 | kG_Channel = 3, |
55 | kB_Channel = 4, |
56 | |
57 | // NB: HLS vs. HSL |
58 | kHLS_Channel = 6, |
59 | kH_Channel = 7, |
60 | kL_Channel = 8, |
61 | kS_Channel = 9, |
62 | |
63 | // kYIQ_Channel = ?, |
64 | // kLum_Channel = ?, |
65 | // kIPC_Channel = ?, |
66 | // kQAC_Channel = ?, |
67 | |
68 | kA_Channel = 16, |
69 | }; |
70 | |
71 | switch (static_cast<uint8_t>(fChannel)) { |
72 | case kR_Channel: return { {-1, 1, 1, 1}, { 1,0,0,0}, false}; // r' = 1 - r |
73 | case kG_Channel: return { { 1,-1, 1, 1}, { 0,1,0,0}, false}; // g' = 1 - g |
74 | case kB_Channel: return { { 1, 1,-1, 1}, { 0,0,1,0}, false}; // b' = 1 - b |
75 | case kA_Channel: return { { 1, 1, 1,-1}, { 0,0,0,1}, false}; // a' = 1 - a |
76 | case kRGB_Channel: return { {-1,-1,-1, 1}, { 1,1,1,0}, false}; |
77 | |
78 | case kH_Channel: return { {-1, 1, 1, 1}, {.5f,0,0,0}, true}; // h' = .5 - h |
79 | case kS_Channel: return { { 1,-1, 1, 1}, { 0,1,0,0}, true}; // s' = 1 - s |
80 | case kL_Channel: return { { 1, 1,-1, 1}, { 0,0,1,0}, true}; // l' = 1 - l |
81 | case kHLS_Channel: return { {-1,-1,-1, 1}, {.5f,1,1,0}, true}; |
82 | |
83 | default: return { { 1, 1, 1, 1}, { 0,0,0,0}, false}; |
84 | } |
85 | |
86 | SkUNREACHABLE; |
87 | }(); |
88 | |
89 | const float m[] = { |
90 | stcm.scale[0], 0, 0, 0, stcm.trans[0], |
91 | 0, stcm.scale[1], 0, 0, stcm.trans[1], |
92 | 0, 0, stcm.scale[2], 0, stcm.trans[2], |
93 | 0, 0, 0, stcm.scale[3], stcm.trans[3], |
94 | |
95 | }; |
96 | |
97 | fColorFilter->setColorFilter(stcm.hsla ? SkColorFilters::HSLAMatrix(m) |
98 | : SkColorFilters::Matrix(m)); |
99 | } |
100 | |
101 | const sk_sp<sksg::ExternalColorFilter> fColorFilter; |
102 | |
103 | float fChannel = 0; |
104 | }; |
105 | |
106 | } // namespace |
107 | |
108 | sk_sp<sksg::RenderNode> EffectBuilder::attachInvertEffect(const skjson::ArrayValue& jprops, |
109 | sk_sp<sksg::RenderNode> layer) const { |
110 | return fBuilder->attachDiscardableAdapter<InvertEffectAdapter>(jprops, |
111 | std::move(layer), |
112 | fBuilder); |
113 | } |
114 | |
115 | } // namespace internal |
116 | } // namespace skottie |
117 | |