xref: /aosp_15_r20/external/skia/modules/skottie/src/effects/ShiftChannelsEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2019 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorFilter.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkColorData.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTPin.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/src/SkottiePriv.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/src/SkottieValue.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/src/animator/Animator.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/src/effects/Effects.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "modules/sksg/include/SkSGColorFilter.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "modules/sksg/include/SkSGRenderNode.h"
18*c8dee2aaSAndroid Build Coastguard Worker 
19*c8dee2aaSAndroid Build Coastguard Worker #include <array>
20*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
21*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
22*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
23*c8dee2aaSAndroid Build Coastguard Worker 
24*c8dee2aaSAndroid Build Coastguard Worker namespace skjson {
25*c8dee2aaSAndroid Build Coastguard Worker class ArrayValue;
26*c8dee2aaSAndroid Build Coastguard Worker }
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker namespace skottie {
29*c8dee2aaSAndroid Build Coastguard Worker namespace internal {
30*c8dee2aaSAndroid Build Coastguard Worker 
31*c8dee2aaSAndroid Build Coastguard Worker namespace {
32*c8dee2aaSAndroid Build Coastguard Worker 
33*c8dee2aaSAndroid Build Coastguard Worker /*
34*c8dee2aaSAndroid Build Coastguard Worker  * AE's Shift Channels effect overrides individual channels based on a selectable function:
35*c8dee2aaSAndroid Build Coastguard Worker  *
36*c8dee2aaSAndroid Build Coastguard Worker  *     C' = {fR(C), fG(C), fB(C), fA(C)}
37*c8dee2aaSAndroid Build Coastguard Worker  *
38*c8dee2aaSAndroid Build Coastguard Worker  * where fR, fG, fB, fA can be one of
39*c8dee2aaSAndroid Build Coastguard Worker  *
40*c8dee2aaSAndroid Build Coastguard Worker  *     C.r, C.g, C.b, C.a, Luminance(C), Hue(C), Saturation(C), Lightness(C), 1 or 0.
41*c8dee2aaSAndroid Build Coastguard Worker  */
42*c8dee2aaSAndroid Build Coastguard Worker class ShiftChannelsEffectAdapter final : public AnimatablePropertyContainer {
43*c8dee2aaSAndroid Build Coastguard Worker public:
Make(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const AnimationBuilder * abuilder)44*c8dee2aaSAndroid Build Coastguard Worker     static sk_sp<ShiftChannelsEffectAdapter> Make(const skjson::ArrayValue& jprops,
45*c8dee2aaSAndroid Build Coastguard Worker                                                   sk_sp<sksg::RenderNode> layer,
46*c8dee2aaSAndroid Build Coastguard Worker                                                   const AnimationBuilder* abuilder) {
47*c8dee2aaSAndroid Build Coastguard Worker         return sk_sp<ShiftChannelsEffectAdapter>(
48*c8dee2aaSAndroid Build Coastguard Worker                     new ShiftChannelsEffectAdapter(jprops, std::move(layer), abuilder));
49*c8dee2aaSAndroid Build Coastguard Worker     }
50*c8dee2aaSAndroid Build Coastguard Worker 
node() const51*c8dee2aaSAndroid Build Coastguard Worker     const sk_sp<sksg::ExternalColorFilter>& node() const { return fColorFilter; }
52*c8dee2aaSAndroid Build Coastguard Worker 
53*c8dee2aaSAndroid Build Coastguard Worker private:
ShiftChannelsEffectAdapter(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const AnimationBuilder * abuilder)54*c8dee2aaSAndroid Build Coastguard Worker     ShiftChannelsEffectAdapter(const skjson::ArrayValue& jprops,
55*c8dee2aaSAndroid Build Coastguard Worker                                sk_sp<sksg::RenderNode> layer,
56*c8dee2aaSAndroid Build Coastguard Worker                                const AnimationBuilder* abuilder)
57*c8dee2aaSAndroid Build Coastguard Worker         : fColorFilter(sksg::ExternalColorFilter::Make(std::move(layer))) {
58*c8dee2aaSAndroid Build Coastguard Worker         enum : size_t {
59*c8dee2aaSAndroid Build Coastguard Worker             kTakeAlphaFrom_Index = 0,
60*c8dee2aaSAndroid Build Coastguard Worker               kTakeRedFrom_Index = 1,
61*c8dee2aaSAndroid Build Coastguard Worker             kTakeGreenFrom_Index = 2,
62*c8dee2aaSAndroid Build Coastguard Worker              kTakeBlueFrom_Index = 3,
63*c8dee2aaSAndroid Build Coastguard Worker         };
64*c8dee2aaSAndroid Build Coastguard Worker 
65*c8dee2aaSAndroid Build Coastguard Worker         EffectBinder(jprops, *abuilder, this)
66*c8dee2aaSAndroid Build Coastguard Worker                 .bind(  kTakeRedFrom_Index, fR)
67*c8dee2aaSAndroid Build Coastguard Worker                 .bind(kTakeGreenFrom_Index, fG)
68*c8dee2aaSAndroid Build Coastguard Worker                 .bind( kTakeBlueFrom_Index, fB)
69*c8dee2aaSAndroid Build Coastguard Worker                 .bind(kTakeAlphaFrom_Index, fA);
70*c8dee2aaSAndroid Build Coastguard Worker     }
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker     enum class Source : uint8_t {
73*c8dee2aaSAndroid Build Coastguard Worker         kAlpha      = 1,
74*c8dee2aaSAndroid Build Coastguard Worker         kRed        = 2,
75*c8dee2aaSAndroid Build Coastguard Worker         kGreen      = 3,
76*c8dee2aaSAndroid Build Coastguard Worker         kBlue       = 4,
77*c8dee2aaSAndroid Build Coastguard Worker         kLuminance  = 5,
78*c8dee2aaSAndroid Build Coastguard Worker         kHue        = 6,
79*c8dee2aaSAndroid Build Coastguard Worker         kLightness  = 7,
80*c8dee2aaSAndroid Build Coastguard Worker         kSaturation = 8,
81*c8dee2aaSAndroid Build Coastguard Worker         kFullOn     = 9,
82*c8dee2aaSAndroid Build Coastguard Worker         kFullOff    = 10,
83*c8dee2aaSAndroid Build Coastguard Worker 
84*c8dee2aaSAndroid Build Coastguard Worker         kMax        = kFullOff
85*c8dee2aaSAndroid Build Coastguard Worker     };
86*c8dee2aaSAndroid Build Coastguard Worker 
onSync()87*c8dee2aaSAndroid Build Coastguard Worker     void onSync() override {
88*c8dee2aaSAndroid Build Coastguard Worker         // TODO: support for HSL sources will require a custom color filter.
89*c8dee2aaSAndroid Build Coastguard Worker 
90*c8dee2aaSAndroid Build Coastguard Worker         static constexpr float gSourceCoeffs[][5] = {
91*c8dee2aaSAndroid Build Coastguard Worker             {             0,              0,              0, 1, 0}, // kAlpha
92*c8dee2aaSAndroid Build Coastguard Worker             {             1,              0,              0, 0, 0}, // kRed
93*c8dee2aaSAndroid Build Coastguard Worker             {             0,              1,              0, 0, 0}, // kGreen
94*c8dee2aaSAndroid Build Coastguard Worker             {             0,              0,              1, 0, 0}, // kBlue
95*c8dee2aaSAndroid Build Coastguard Worker             {SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0}, // kLuminance
96*c8dee2aaSAndroid Build Coastguard Worker             {             0,              0,              0, 0, 0}, // TODO: kHue
97*c8dee2aaSAndroid Build Coastguard Worker             {             0,              0,              0, 0, 0}, // TODO: kLightness
98*c8dee2aaSAndroid Build Coastguard Worker             {             0,              0,              0, 0, 0}, // TODO: kSaturation
99*c8dee2aaSAndroid Build Coastguard Worker             {             0,              0,              0, 0, 1}, // kFullOn
100*c8dee2aaSAndroid Build Coastguard Worker             {             0,              0,              0, 0, 0}, // kFullOff
101*c8dee2aaSAndroid Build Coastguard Worker         };
102*c8dee2aaSAndroid Build Coastguard Worker         static_assert(std::size(gSourceCoeffs) == static_cast<size_t>(Source::kMax), "");
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker         auto coeffs = [](float src) {
105*c8dee2aaSAndroid Build Coastguard Worker             // Channel sources are encoded as Source enum values.
106*c8dee2aaSAndroid Build Coastguard Worker             // We map these onto our coeffs table.
107*c8dee2aaSAndroid Build Coastguard Worker             src = SkTPin(src, 1.0f, static_cast<float>(Source::kMax));
108*c8dee2aaSAndroid Build Coastguard Worker             return gSourceCoeffs[static_cast<size_t>(src) - 1];
109*c8dee2aaSAndroid Build Coastguard Worker         };
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker         const float* rc = coeffs(fR);
112*c8dee2aaSAndroid Build Coastguard Worker         const float* gc = coeffs(fG);
113*c8dee2aaSAndroid Build Coastguard Worker         const float* bc = coeffs(fB);
114*c8dee2aaSAndroid Build Coastguard Worker         const float* ac = coeffs(fA);
115*c8dee2aaSAndroid Build Coastguard Worker 
116*c8dee2aaSAndroid Build Coastguard Worker         const float cm[] = {
117*c8dee2aaSAndroid Build Coastguard Worker             rc[0], rc[1], rc[2], rc[3], rc[4],
118*c8dee2aaSAndroid Build Coastguard Worker             gc[0], gc[1], gc[2], gc[3], gc[4],
119*c8dee2aaSAndroid Build Coastguard Worker             bc[0], bc[1], bc[2], bc[3], bc[4],
120*c8dee2aaSAndroid Build Coastguard Worker             ac[0], ac[1], ac[2], ac[3], ac[4],
121*c8dee2aaSAndroid Build Coastguard Worker         };
122*c8dee2aaSAndroid Build Coastguard Worker 
123*c8dee2aaSAndroid Build Coastguard Worker         fColorFilter->setColorFilter(SkColorFilters::Matrix(cm));
124*c8dee2aaSAndroid Build Coastguard Worker 
125*c8dee2aaSAndroid Build Coastguard Worker         // When applied to the alpha channel, kFullOn expands the effect coverage
126*c8dee2aaSAndroid Build Coastguard Worker         // to the content bounding box.
127*c8dee2aaSAndroid Build Coastguard Worker         fColorFilter->setCoverage(fA == static_cast<float>(Source::kFullOn)
128*c8dee2aaSAndroid Build Coastguard Worker                                     ? sksg::ExternalColorFilter::Coverage::kBoundingBox
129*c8dee2aaSAndroid Build Coastguard Worker                                     : sksg::ExternalColorFilter::Coverage::kNormal);
130*c8dee2aaSAndroid Build Coastguard Worker     }
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker     const sk_sp<sksg::ExternalColorFilter> fColorFilter;
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker     ScalarValue fR = static_cast<float>(Source::kRed),
135*c8dee2aaSAndroid Build Coastguard Worker                 fG = static_cast<float>(Source::kGreen),
136*c8dee2aaSAndroid Build Coastguard Worker                 fB = static_cast<float>(Source::kBlue),
137*c8dee2aaSAndroid Build Coastguard Worker                 fA = static_cast<float>(Source::kAlpha);
138*c8dee2aaSAndroid Build Coastguard Worker };
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker } // namespace
141*c8dee2aaSAndroid Build Coastguard Worker 
142*c8dee2aaSAndroid Build Coastguard Worker 
attachShiftChannelsEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const143*c8dee2aaSAndroid Build Coastguard Worker sk_sp<sksg::RenderNode> EffectBuilder::attachShiftChannelsEffect(
144*c8dee2aaSAndroid Build Coastguard Worker         const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const {
145*c8dee2aaSAndroid Build Coastguard Worker     return fBuilder->attachDiscardableAdapter<ShiftChannelsEffectAdapter>(jprops,
146*c8dee2aaSAndroid Build Coastguard Worker                                                                           std::move(layer),
147*c8dee2aaSAndroid Build Coastguard Worker                                                                           fBuilder);
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker 
150*c8dee2aaSAndroid Build Coastguard Worker } // namespace internal
151*c8dee2aaSAndroid Build Coastguard Worker } // namespace skottie
152