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