• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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/effects/SkRuntimeEffect.h"
11 #include "include/private/SkTPin.h"
12 #include "modules/skottie/src/Adapter.h"
13 #include "modules/skottie/src/SkottieJson.h"
14 #include "modules/skottie/src/SkottieValue.h"
15 #include "modules/sksg/include/SkSGColorFilter.h"
16 
17 namespace skottie::internal {
18 
19 namespace  {
20 
21 // The B&W effect allows controlling individual luminance contribution of
22 // primary and secondary colors.
23 //
24 // The implementation relies on computing primary/secondary relative weights
25 // for the input color on the hue hexagon, and modulating based on weight
26 // coefficients.
27 //
28 // Note:
29 //   - at least one of (dr,dg,db) is 0
30 //   - at least two of (wr,wg,wb) and two of (wy,wc,wm) are 0
31 //  => we are effectively selecting the color hue sextant without explicit branching
32 //
33 // (inspired by https://github.com/RoyiAvital/StackExchangeCodes/blob/master/SignalProcessing/Q688/ApplyBlackWhiteFilter.m)
34 
make_effect()35 static sk_sp<SkRuntimeEffect> make_effect() {
36     static constexpr char BLACK_AND_WHITE_EFFECT[] = R"(
37         uniform half kR, kY, kG, kC, kB, kM;
38 
39         half4 main(half4 c) {
40             half m = min(min(c.r, c.g), c.b),
41 
42                 dr = c.r - m,
43                 dg = c.g - m,
44                 db = c.b - m,
45 
46                 // secondaries weights
47                 wy = min(dr,dg),
48                 wc = min(dg,db),
49                 wm = min(db,dr),
50 
51                 // primaries weights
52                 wr = dr - wy - wm,
53                 wg = dg - wy - wc,
54                 wb = db - wc - wm,
55 
56                 // final luminance
57                 l = m + kR*wr + kY*wy + kG*wg + kC*wc + kB*wb + kM*wm;
58 
59             return half4(l, l, l, c.a);
60         }
61     )";
62 
63     static const SkRuntimeEffect* effect =
64             SkRuntimeEffect::MakeForColorFilter(SkString(BLACK_AND_WHITE_EFFECT)).effect.release();
65     SkASSERT(effect);
66 
67     return sk_ref_sp(effect);
68 }
69 
70 class BlackAndWhiteAdapter final : public DiscardableAdapterBase<BlackAndWhiteAdapter,
71                                                                  sksg::ExternalColorFilter> {
72 public:
BlackAndWhiteAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,sk_sp<sksg::RenderNode> layer)73     BlackAndWhiteAdapter(const skjson::ArrayValue& jprops,
74                          const AnimationBuilder& abuilder,
75                          sk_sp<sksg::RenderNode> layer)
76         : INHERITED(sksg::ExternalColorFilter::Make(std::move(layer)))
77         , fEffect(make_effect())
78     {
79         SkASSERT(fEffect);
80 
81         enum : size_t {
82                 kReds_Index = 0,
83              kYellows_Index = 1,
84               kGreens_Index = 2,
85                kCyans_Index = 3,
86                kBlues_Index = 4,
87             kMagentas_Index = 5,
88             // TODO
89             //    kTint_Index = 6,
90             // kTintColorIndex = 7,
91         };
92 
93         EffectBinder(jprops, abuilder, this)
94             .bind(    kReds_Index, fCoeffs[0])
95             .bind( kYellows_Index, fCoeffs[1])
96             .bind(  kGreens_Index, fCoeffs[2])
97             .bind(   kCyans_Index, fCoeffs[3])
98             .bind(   kBlues_Index, fCoeffs[4])
99             .bind(kMagentas_Index, fCoeffs[5]);
100     }
101 
102 private:
onSync()103     void onSync() override {
104         struct {
105             float normalized_coeffs[6];
106         } coeffs = {
107             (fCoeffs[0] ) / 100,
108             (fCoeffs[1] ) / 100,
109             (fCoeffs[2] ) / 100,
110             (fCoeffs[3] ) / 100,
111             (fCoeffs[4] ) / 100,
112             (fCoeffs[5] ) / 100,
113         };
114 
115         this->node()->setColorFilter(
116                 fEffect->makeColorFilter(SkData::MakeWithCopy(&coeffs, sizeof(coeffs))));
117     }
118 
119     const sk_sp<SkRuntimeEffect> fEffect;
120 
121     ScalarValue                  fCoeffs[6];
122 
123     using INHERITED = DiscardableAdapterBase<BlackAndWhiteAdapter, sksg::ExternalColorFilter>;
124 };
125 
126 } // namespace
127 
128 
attachBlackAndWhiteEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const129 sk_sp<sksg::RenderNode> EffectBuilder::attachBlackAndWhiteEffect(
130         const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const {
131     return fBuilder->attachDiscardableAdapter<BlackAndWhiteAdapter>(jprops,
132                                                                     *fBuilder,
133                                                                     std::move(layer));
134 }
135 
136 } // namespace skottie::internal
137