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/SkColorData.h" |
11 | #include "modules/skottie/src/SkottieJson.h" |
12 | #include "modules/skottie/src/SkottieValue.h" |
13 | #include "modules/sksg/include/SkSGColorFilter.h" |
14 | |
15 | namespace skottie { |
16 | namespace internal { |
17 | |
18 | namespace { |
19 | |
20 | /* |
21 | * AE's Shift Channels effect overrides individual channels based on a selectable function: |
22 | * |
23 | * C' = {fR(C), fG(C), fB(C), fA(C)} |
24 | * |
25 | * where fR, fG, fB, fA can be one of |
26 | * |
27 | * C.r, C.g, C.b, C.a, Luminance(C), Hue(C), Saturation(C), Lightness(C), 1 or 0. |
28 | */ |
29 | class ShiftChannelsEffectAdapter final : public AnimatablePropertyContainer { |
30 | public: |
31 | static sk_sp<ShiftChannelsEffectAdapter> Make(const skjson::ArrayValue& jprops, |
32 | sk_sp<sksg::RenderNode> layer, |
33 | const AnimationBuilder* abuilder) { |
34 | return sk_sp<ShiftChannelsEffectAdapter>( |
35 | new ShiftChannelsEffectAdapter(jprops, std::move(layer), abuilder)); |
36 | } |
37 | |
38 | const sk_sp<sksg::ExternalColorFilter>& node() const { return fColorFilter; } |
39 | |
40 | private: |
41 | ShiftChannelsEffectAdapter(const skjson::ArrayValue& jprops, |
42 | sk_sp<sksg::RenderNode> layer, |
43 | const AnimationBuilder* abuilder) |
44 | : fColorFilter(sksg::ExternalColorFilter::Make(std::move(layer))) { |
45 | enum : size_t { |
46 | kTakeAlphaFrom_Index = 0, |
47 | kTakeRedFrom_Index = 1, |
48 | kTakeGreenFrom_Index = 2, |
49 | kTakeBlueFrom_Index = 3, |
50 | }; |
51 | |
52 | EffectBinder(jprops, *abuilder, this) |
53 | .bind( kTakeRedFrom_Index, fR) |
54 | .bind(kTakeGreenFrom_Index, fG) |
55 | .bind( kTakeBlueFrom_Index, fB) |
56 | .bind(kTakeAlphaFrom_Index, fA); |
57 | } |
58 | |
59 | enum class Source : uint8_t { |
60 | kAlpha = 1, |
61 | kRed = 2, |
62 | kGreen = 3, |
63 | kBlue = 4, |
64 | kLuminance = 5, |
65 | kHue = 6, |
66 | kLightness = 7, |
67 | kSaturation = 8, |
68 | kFullOn = 9, |
69 | kFullOff = 10, |
70 | |
71 | kMax = kFullOff |
72 | }; |
73 | |
74 | void onSync() override { |
75 | // TODO: support for HSL sources will require a custom color filter. |
76 | |
77 | static constexpr float gSourceCoeffs[][5] = { |
78 | { 0, 0, 0, 1, 0}, // kAlpha |
79 | { 1, 0, 0, 0, 0}, // kRed |
80 | { 0, 1, 0, 0, 0}, // kGreen |
81 | { 0, 0, 1, 0, 0}, // kBlue |
82 | {SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0}, // kLuminance |
83 | { 0, 0, 0, 0, 0}, // TODO: kHue |
84 | { 0, 0, 0, 0, 0}, // TODO: kLightness |
85 | { 0, 0, 0, 0, 0}, // TODO: kSaturation |
86 | { 0, 0, 0, 0, 1}, // kFullOn |
87 | { 0, 0, 0, 0, 0}, // kFullOff |
88 | }; |
89 | static_assert(SK_ARRAY_COUNT(gSourceCoeffs) == static_cast<size_t>(Source::kMax), "" ); |
90 | |
91 | auto coeffs = [](float src) { |
92 | // Channel sources are encoded as Source enum values. |
93 | // We map these onto our coeffs table. |
94 | src = SkTPin(src, 1.0f, static_cast<float>(Source::kMax)); |
95 | return gSourceCoeffs[static_cast<size_t>(src) - 1]; |
96 | }; |
97 | |
98 | const float* rc = coeffs(fR); |
99 | const float* gc = coeffs(fG); |
100 | const float* bc = coeffs(fB); |
101 | const float* ac = coeffs(fA); |
102 | |
103 | const float cm[] = { |
104 | rc[0], rc[1], rc[2], rc[3], rc[4], |
105 | gc[0], gc[1], gc[2], gc[3], gc[4], |
106 | bc[0], bc[1], bc[2], bc[3], bc[4], |
107 | ac[0], ac[1], ac[2], ac[3], ac[4], |
108 | }; |
109 | |
110 | fColorFilter->setColorFilter(SkColorFilters::Matrix(cm)); |
111 | } |
112 | |
113 | const sk_sp<sksg::ExternalColorFilter> fColorFilter; |
114 | |
115 | ScalarValue fR = static_cast<float>(Source::kRed), |
116 | fG = static_cast<float>(Source::kGreen), |
117 | fB = static_cast<float>(Source::kBlue), |
118 | fA = static_cast<float>(Source::kAlpha); |
119 | }; |
120 | |
121 | } // namespace |
122 | |
123 | |
124 | sk_sp<sksg::RenderNode> EffectBuilder::attachShiftChannelsEffect( |
125 | const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const { |
126 | return fBuilder->attachDiscardableAdapter<ShiftChannelsEffectAdapter>(jprops, |
127 | std::move(layer), |
128 | fBuilder); |
129 | } |
130 | |
131 | } // namespace internal |
132 | } // namespace skottie |
133 | |