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