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 "include/private/SkTPin.h"
11 #include "modules/skottie/src/SkottieJson.h"
12 #include "modules/skottie/src/SkottieValue.h"
13 #include "modules/sksg/include/SkSGColorFilter.h"
14 #include "src/utils/SkJSON.h"
15 
16 namespace skottie {
17 namespace internal {
18 
19 namespace  {
20 
21 class HueSaturationEffectAdapter final : public AnimatablePropertyContainer {
22 public:
Make(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const AnimationBuilder * abuilder)23     static sk_sp<HueSaturationEffectAdapter> Make(const skjson::ArrayValue& jprops,
24                                                   sk_sp<sksg::RenderNode> layer,
25                                                   const AnimationBuilder* abuilder) {
26 
27         return sk_sp<HueSaturationEffectAdapter>(
28                     new HueSaturationEffectAdapter(jprops, std::move(layer), abuilder));
29     }
30 
node() const31     const sk_sp<sksg::ExternalColorFilter>& node() const { return fColorFilter; }
32 
33 private:
HueSaturationEffectAdapter(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const AnimationBuilder * abuilder)34     HueSaturationEffectAdapter(const skjson::ArrayValue& jprops,
35                                sk_sp<sksg::RenderNode> layer,
36                                const AnimationBuilder* abuilder)
37         : fColorFilter(sksg::ExternalColorFilter::Make(std::move(layer))) {
38         enum : size_t {
39                kChannelControl_Index = 0,
40                  kChannelRange_Index = 1,
41                     kMasterHue_Index = 2,
42                     kMasterSat_Index = 3,
43               kMasterLightness_Index = 4,
44                      kColorize_Index = 5,
45                   kColorizeHue_Index = 6,
46                   kColorizeSat_Index = 7,
47             kColorizeLightness_Index = 8,
48         };
49 
50         EffectBinder(jprops, *abuilder, this)
51                 .bind( kChannelControl_Index, fChanCtrl   )
52                 .bind(      kMasterHue_Index, fMasterHue  )
53                 .bind(      kMasterSat_Index, fMasterSat  )
54                 .bind(kMasterLightness_Index, fMasterLight);
55 
56         // TODO: colorize support?
57     }
58 
onSync()59     void onSync() override {
60         fColorFilter->setColorFilter(this->makeColorFilter());
61     }
62 
makeColorFilter() const63     sk_sp<SkColorFilter> makeColorFilter() const {
64         enum : uint8_t {
65             kMaster_Chan   = 0x01,
66             kReds_Chan     = 0x02,
67             kYellows_Chan  = 0x03,
68             kGreens_Chan   = 0x04,
69             kCyans_Chan    = 0x05,
70             kBlues_Chan    = 0x06,
71             kMagentas_Chan = 0x07,
72         };
73 
74         // We only support master channel controls at this point.
75         if (static_cast<int>(fChanCtrl) != kMaster_Chan) {
76             return nullptr;
77         }
78 
79         // AE semantics:
80         //
81         // master       hue [degrees]   => color.H offset
82         // master       sat [-100..100] => [-100..0) -> [0 .. color.S)
83         //                                 ( 0..100] -> (color.S .. 1]
84         // master lightness [-100..100] => [-100..0) -> [0 .. color.L]
85         //                                 ( 0..100] -> (color.L .. 1]
86         const auto h = fMasterHue / 360,
87                    s = SkTPin(fMasterSat   / 100, -1.0f, 1.0f),
88                    l = SkTPin(fMasterLight / 100, -1.0f, 1.0f),
89                    h_bias  = h,
90                    s_bias  = std::max(s, 0.0f),
91                    s_scale = 1 - std::abs(s),
92                    l_bias  = std::max(l, 0.0f),
93                    l_scale = 1 - std::abs(l);
94 
95         const float hsl_cm[20] = {
96             1,       0,       0, 0, h_bias,
97             0, s_scale,       0, 0, s_bias,
98             0,       0, l_scale, 0, l_bias,
99             0,       0,       0, 1,      0,
100         };
101 
102         return SkColorFilters::HSLAMatrix(hsl_cm);
103     }
104 
105     const sk_sp<sksg::ExternalColorFilter> fColorFilter;
106 
107     float fChanCtrl    = 0.0f,
108           fMasterHue   = 0.0f,
109           fMasterSat   = 0.0f,
110           fMasterLight = 0.0f;
111 };
112 
113 } // namespace
114 
attachHueSaturationEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const115 sk_sp<sksg::RenderNode> EffectBuilder::attachHueSaturationEffect(
116         const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const {
117     return fBuilder->attachDiscardableAdapter<HueSaturationEffectAdapter>(jprops,
118                                                                           std::move(layer),
119                                                                           fBuilder);
120 }
121 
122 } // namespace internal
123 } // namespace skottie
124